New to Attio?Get 10% off when you sign up through Craftt.Try Attio free →
All articles

The if() function in Attio formulas: conditional logic explained

·9 min read

If you learn a single function from Attio's formula library, make it if(). Every genuinely useful formula attribute we build for clients — deal tiers, lead routing bands, renewal risk flags, stale-deal alerts — has an if() at its core, because if() is what turns raw numbers into words your team can filter, sort, and act on.

This is the first deep dive in our function-by-function series on Attio formula attributes. One article, one function, everything you need: syntax, nesting, multi-condition logic, empty-value traps, and formulas you can paste straight into your workspace.

Table of contents

What if() does

if() evaluates a condition and returns one of two values:

if(condition, trueValue, falseValue)

When the condition is true, the formula returns the second argument; otherwise the third. The classic one-liner:

if({Deal value} > 10000, "Enterprise", "Standard")

Every deal now carries an "Enterprise" or "Standard" label — computed live, recalculated within seconds whenever the deal value changes, and usable everywhere an attribute is usable: view filters, sorts, reports, workflow triggers, and sequence personalization.

That last part is the entire point. A number like 84300 requires interpretation. A label like "Enterprise" is a decision already made — encoded once by whoever owns the playbook, applied consistently to every record forever.

The syntax, piece by piece

Three arguments, in order:

  1. Condition — any expression that resolves to true or false. Usually a comparison built from operators: ==, !=, >, >=, <, <=. You can also pass a checkbox attribute directly, or negate one with !.
  2. trueValue — what the formula returns when the condition holds. Text, a number, a date, another attribute reference, or even another expression.
  3. falseValue — what it returns otherwise. Same rules.

Attribute references go in curly braces — type { in the formula editor and every attribute on the object appears, including relationship attributes from connected objects. A few valid shapes:

if({Is customer}, "Customer", "Prospect")
if({Employee range} >= 200, {Enterprise price}, {Standard price})
if(dateDiff({Last interaction}, today(), "days") > 30, "Going cold", "Active")

Note the third example: the condition doesn't have to compare two attributes — it can wrap any other function. That composability is what makes if() the connective tissue of the whole library.

And if you'd rather not write any of this by hand, describe it in the AI prompt box below the editor — *"label deals over 10k as Enterprise, otherwise Standard"* — and Attio writes the formula for you, without consuming workspace AI credits. Read what it produces; it's the fastest syntax tutorial you'll get.

Nested if(): building tiers

Two buckets are rarely enough. The pattern for three or more is to put another if() in the falseValue slot:

if({Lead score} > 80, "Hot", if({Lead score} > 50, "Warm", "Cold"))

Read it left to right: over 80 → "Hot"; otherwise, over 50 → "Warm"; otherwise "Cold". Conditions are checked in order, so put the highest threshold first and each subsequent branch only sees records that fell through.

The same shape builds deal-size bands:

if({Deal value} >= 100000, "Tier 1", if({Deal value} >= 25000, "Tier 2", "Tier 3"))

One clarification that trips people up: nesting if() inside a single formula is unrestricted. The three-deep limit you may have read about applies to something different — formula *attributes* referencing other formula attributes. A ten-branch nested if() in one formula is fine (if hard to read); a formula built on a formula built on a formula built on a formula is not.

Readability tip from building these in client workspaces: past three or four branches, stop nesting and reconsider the model. A select attribute with explicit options, or a dedicated scoring formula feeding a simple banding formula, beats a seven-level nest every time someone other than you has to maintain it.

Multiple conditions with and(), or(), not()

Real qualification logic rarely hangs on one condition. Combine them:

if(and({Estimated ARR} > 50000, {Employee range} >= 200), "ICP", "Not ICP")

Both and() and or() also support keyword syntax, which often reads better inline:

if({Stage} == "Closed won" or {Stage} == "Closed lost", "Closed", "Open")

And not() — or its shorthand ! — flips a boolean, which is how you express exclusions:

if(not(hasBeenIn({Deal stage}, "Closed lost")), "Safe to sequence", "Previously lost")

That last formula pairs if() with an attribute-history function to keep previously lost deals out of automated re-engagement — the kind of guardrail that saves a rep from a tone-deaf "excited to connect!" email. (Note hasBeenIn() only works on select and status attributes.)

Choosing the right output type

By default the attribute's output type is Auto and Attio infers it from what your formula returns. For if() formulas it usually pays to force it:

  • Returning labels like "Hot"/"Warm"/"Cold"? Force Text.
  • Returning true/false? Force Checkbox — you get a clean, filterable tick in every view.
  • Returning numbers per branch, like a score? Force Number, or Rating if it's a 1–5 scale — ratings read instantly in a list view.
  • Returning money, like a discounted price? Force Currency for proper formatting in views and reports.

The one thing to avoid is branches returning *different* types — if({X} > 5, "High", 0) mixes text and number, which forces Auto into its vaguest behavior and breaks filtering. Keep both branches the same type.

The empty-value trap

The most common way an if() formula silently fails: an empty input. If {Lead score} is blank, {Lead score} > 50 isn't false — it's nothing, and your formula can return nothing at all. On a fresh workspace with patchy data, that can mean half your records show an empty tier.

The fix is the ?? (null coalescing) operator, which substitutes a fallback when the left side is empty:

if(({Lead score} ?? 0) > 50, "Qualified", "Nurture")

Now a blank score behaves as 0 and lands deliberately in "Nurture" instead of producing an empty cell. Rule of thumb: any attribute that can plausibly be blank — enriched fields especially — gets a ?? before it enters a condition.

CRM use cases that earn their keep

Where if() actually pays for itself in day-to-day pipeline operations:

  • Deal tieringif({Deal value} > 50000, "High touch", "Standard") — routes every deal to the right motion without a rep judging case by case. Important because playbook selection stops depending on who happens to look at the record.
  • Stale-deal flagif(timeSpentIn({Stage}, {Stage}, "days") > 14, "Stale", "Active") — surfaces stuck deals in every pipeline view. Important because slipped deals are found by the system, not by the Friday pipeline review.
  • Renewal risk windowif(dateDiff(today(), {Renewal date}, "days") < 60, "Renewal risk", "Healthy") — feeds the CS queue automatically. Important because renewal saves are won or lost on lead time.
  • Speed-to-lead SLAif(dateDiff({Created at}, now(), "hours") > 4 and !{Contacted}, "SLA breach", "OK") — makes response-time failures visible on the record. Important because speed-to-lead is one of the strongest conversion levers and the first one to silently decay.
  • ICP flagif(and({Estimated ARR} > 50000, {Employee range} >= 200), "ICP", "Not ICP") — encodes the ideal customer profile once, centrally. Important because otherwise every rep rebuilds the definition slightly differently in saved views.
  • Single-threaded warningif(count({Team}) < 2, "Single-threaded", "Multi-threaded") — uses relationship data to flag deals with only one contact. Important because single-threading is the most common silent deal killer.

Copy-paste formulas

Each of these works as-is once you swap in your attribute names:

Deal size tiers (Text output):

if({Deal value} >= 100000, "Tier 1", if({Deal value} >= 25000, "Tier 2", "Tier 3"))

Lead temperature with blank-safe scoring (Text output):

if(({Lead score} ?? 0) > 80, "Hot", if(({Lead score} ?? 0) > 50, "Warm", "Cold"))

Stale-deal flag (Text output — needs a select or status Stage attribute):

if(timeSpentIn({Stage}, {Stage}, "days") > 14, "Stale", "Active")

Days-of-silence alert (Checkbox output):

if((dateDiff({Last interaction}, today(), "days") ?? 999) > 30, true, false)

Weighted pipeline value per deal (Currency output):

if({Stage} == "Negotiation", {Deal value} * 0.6, if({Stage} == "Proposal", {Deal value} * 0.3, {Deal value} * 0.1))

if() formulas vs. workflow conditions

Attio's workflows also have condition blocks, so when does logic belong in a formula instead? Our rule:

Use an if() formula when...Use a workflow condition when...
The result is a *state* you want visible on the record — a tier, flag, or labelThe result is an *action* — send a Slack message, create a task, enroll in a sequence
It should recalculate continuously as data changesIt should evaluate once, at a specific trigger moment
You want to filter, sort, and report on the outcomeThe outcome is routing, not data

The two compose beautifully: compute the state with a formula, then trigger the workflow *from* the formula attribute. "When Deal health changes to Stale, notify the owner" is a two-line workflow because the formula already did the thinking.

Final thoughts

if() is where formula attributes stop being a calculator and start being an operating system for your pipeline: thresholds, tiers, and guardrails defined once by the person who owns the playbook, enforced everywhere, always current. Master the three-argument shape, guard your inputs with ??, keep both branches the same type, and reach for a nested if() before you reach for another spreadsheet export.

For the rest of the library — every operator, math, date, text, and history function with CRM use cases — see the complete guide to Attio formula attributes.

And if you'd rather have your scoring, tiering, and pipeline-hygiene formulas designed and shipped for you, that's literally what we do. Get a free workspace audit or see the AI-native Attio sprint.

Need help with your Attio setup?

We migrate teams, build data models, wire automations, and train Claude agents inside your workspace. Discovery call is free.

Book a free discovery call