The and(), or(), and not() functions in Attio: turning your team's definitions into one filterable field
Ask five people on a revenue team what makes a deal "qualified" and you get five saved filters, each subtly different, each drifting on its own schedule. That isn't a discipline problem — it's a data-model problem. The definition lives in views instead of on records. and(), or(), and not() are how you move it: combine the conditions once, force the output to a checkbox, and the answer becomes a field everyone shares.
This is part of our function-by-function series on Attio formula attributes — previously: if(), timeSpentIn(), dateDiff(), the ?? operator, count(), hasBeenIn(), valueSetAt(), contains(), sum(), previousValue(), dateAdd(), formatDate(), min()/max(), valueAt(), replace()/replaceAll(), avg()/median(), and round()/ceil()/floor(). This one takes the three logic functions together, because you almost never use one alone.
Table of contents
- What and(), or(), and not() do
- The saved-filter problem
- and(): both things must be true
- or(): one bucket from many stages
- not(): the exclusion field
- Chaining three or more conditions
- Composing with the rest of the library
- CRM use cases that earn their keep
- Copy-paste formulas
- Gotchas
- Final thoughts
What and(), or(), and not() do
Three functions, each with a shorthand form:
and(left, right) left and right
or(left, right) left or right
not(value) !valueand() is true only when both arguments are true. or() is true when at least one is. not() returns the opposite boolean. The keyword and symbol forms are equivalent to the function forms, so you can write whichever reads better in the editor — {Deal value} > 10000 and {Deal stage} == "Negotiation" says the same thing as and({Deal value} > 10000, {Deal stage} == "Negotiation").
The important step comes after the formula: force the output type to Checkbox rather than leaving it on Auto. A boolean rendered as a checkbox is a clean tick in a list view, a filter condition, a report dimension, and an automation trigger. Left on Auto it's just a word.
The saved-filter problem
A saved filter is a view. A boolean formula is data. That difference is the whole argument for this article.
When "sales qualified" is a three-condition filter, it exists once per person who built it, it can't be reported on, no automation can fire off it, and nobody can tell you last quarter's version of the definition. When it's a formula attribute, there is exactly one definition, it sits on the record where anyone can see why a deal qualifies, and changing the rule changes every record at once — no re-saving ten views.
The tell that you need one: two people disagreeing about which deals belong in a number, and both being right about their own filter.
and(): both things must be true
and() is the qualification function. Qualification is almost always a conjunction — size *and* timing, budget *and* authority, fit *and* intent:
and({Employee range} >= 200, {Purchase timeline} == "This quarter")One "Sales qualified" tick instead of a multi-condition filter every rep rebuilds slightly differently. The same shape covers deal-stage gates:
and({Deal value} > 10000, {Deal stage} == "Negotiation")That's your "big deal in the room" flag — the list a sales leader actually wants pinned, and the trigger for whatever your team does about it.
or(): one bucket from many stages
or() is the bucketing function. It exists because pipelines have several stages that mean the same thing for reporting:
or({Deal stage} == "Closed won", {Deal stage} == "Closed lost")One "Closed" field, regardless of outcome — so win/loss reporting, cycle-time math, and "still open" filters all read from one place instead of enumerating stages every time. Add a stage to your pipeline and you update one formula, not every report built on a stage list.
or() is also how you catch a condition that arrives through more than one route: an intent signal that might land in either of two fields, or a disqualifier that can come from enrichment or from a rep.
not(): the exclusion field
not() is the one that saves you from an outbound incident:
not(hasBeenIn({Deal stage}, "Closed lost"))Previously-lost accounts stay out of re-engagement sequences, where a cheerful cold email to someone who already told you no does real damage. The shorthand is just as good for simple flags:
!{Is customer}Prospects only, customers excluded. Exclusion logic is where formula attributes pay for themselves fastest, because the cost of a wrong record isn't a bad number in a report — it's a message that shouldn't have been sent.
Chaining three or more conditions
The documented signature takes two arguments, so three conditions means either the keyword form or a nest:
and({Employee range} >= 200, and({Purchase timeline} == "This quarter", !{Is customer}))The keyword syntax reads far better for anything with more than two parts, and mixing and with or is where parentheses stop being optional — a and b or c and a and (b or c) are different rules, and only one of them is yours. Parenthesize every mixed expression, even where you think the precedence is on your side.
Two structures worth knowing when the logic gets tangled: an "all of these must be false" rule is not(or(a, b)), and "not all of these are true" is not(and(a, b)). Writing exclusion as a negated or() usually reads better than a pile of != comparisons.
Composing with the rest of the library
Logic functions are glue — they're most useful wrapping the rest of the library. Combine a condition with if() to get a label instead of a tick:
if(and({Employee range} >= 200, {Purchase timeline} == "This quarter"), "SQL", "MQL")Use 13px] bg-[color:var(--color-bg-muted)] border border-[color:var(--color-border)] px-1.5 py-0.5 rounded">and() on top of the history functions to catch stalled deals that are also worth chasing, pairing it with [timeSpentIn():
and(timeSpentIn({Deal stage}, {Deal stage}, "days") > 30, {Deal value} > 25000)And guard the inputs with ?? before they reach the comparison — a blank number field compared against a threshold is the single most common reason a qualification flag quietly stops ticking:
and(({Employee count} ?? 0) >= 200, {Purchase timeline} == "This quarter")CRM use cases that earn their keep
- Sales-qualified flag —
and({Employee range} >= 200, {Purchase timeline} == "This quarter")— important because it replaces five reps' five definitions with one field the whole team filters and reports on. - Big deal in the room —
and({Deal value} > 10000, {Deal stage} == "Negotiation")— important because the deals that deserve leadership attention should surface themselves, not depend on someone remembering to look. - Closed bucket —
or({Stage} == "Closed won", {Stage} == "Closed lost")— important because win/loss reporting shouldn't re-enumerate your stage list every time the pipeline changes. - Safe-to-email flag —
not(hasBeenIn({Deal stage}, "Closed lost"))— important because a tone-deaf sequence to a lost account costs more than any campaign gains. - Prospects only —
!{Is customer}— important because customers in a cold outbound list is the mistake people notice, and remember. - Stalled and valuable —
and(timeSpentIn({Deal stage}, {Deal stage}, "days") > 30, {Deal value} > 25000)— important because "stuck" only matters when the deal is big enough to be worth unsticking.
Copy-paste formulas
Swap in your attribute names and these work as-is:
Sales-qualified flag (Checkbox output):
and({Employee range} >= 200, {Purchase timeline} == "This quarter")Blank-safe version of the same rule (Checkbox output):
and(({Employee count} ?? 0) >= 200, {Purchase timeline} == "This quarter")Closed bucket for win/loss reporting (Checkbox output):
or({Deal stage} == "Closed won", {Deal stage} == "Closed lost")Safe to re-engage (Checkbox output):
not(hasBeenIn({Deal stage}, "Closed lost"))Tiered label built on a combined condition (Text output):
if(and({Employee range} >= 200, {Purchase timeline} == "This quarter"), "SQL", "MQL")Stalled high-value deal (Checkbox output):
and(timeSpentIn({Deal stage}, {Deal stage}, "days") > 30, {Deal value} > 25000)Gotchas
- Force the output to Checkbox. On Auto you get a value; on Checkbox you get a tick you can filter, group, and trigger automations from. This is the step people skip.
- Parenthesize mixed and/or.
a and b or cis nota and (b or c). Write the parentheses even when you're sure — the next person to read it isn't. - Two arguments per call. For three-plus conditions, use the keyword form or nest the calls; don't assume a third argument is accepted, test it in the editor.
- color:var(--color-text-heading)]">Blanks break comparisons. An empty number compared to a threshold won't behave like zero. Guard with [?? inside the condition.
- History functions are select-only.
hasBeenIn()and friends work on select and status attributes. Attio's documentedcurrentkeyword doesn't work in-product — pass the attribute itself again, as intimeSpentIn({Stage}, {Stage}, "days"). - Nesting limit. Three formula attributes deep. Build the full condition in one formula rather than stacking a boolean attribute on top of two other formula attributes.
- A flag isn't a policy. A qualification checkbox that nobody has agreed on is just a faster way to disagree. Write the rule down with the team first, then encode it.
Final thoughts
and(), or(), and not() aren't interesting on their own — they're the functions that turn everything else in the library into a decision. The value isn't the boolean; it's that the definition of "qualified", "closed", or "safe to contact" stops living in ten people's saved views and starts living on the record, where it can be argued about once and then trusted.
For the rest of the library — every history, logic, math, date, and text function with CRM use cases — see the complete guide to Attio formula attributes.
And if you'd rather have your qualification rules, exclusion lists, and reporting buckets designed and shipped for you, that's literally what we do. Get a free workspace audit or see the AI-native Attio sprint.
Official sources
Attio documentation used to verify this guide:
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 callReady when you are.
Two ways in. Pick the friction that fits.