> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inflection.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Tokens

> Personalize journeys and emails with tokens that reference and calculate based on attributes and activities.

In Inflection, tokens are elements that reference an attribute or calculation related to a contact, user, org, or account. They are used to personalize journeys and emails.

## Token categories & examples

We think of tokens in 3 categories:

<AccordionGroup>
  <Accordion title="(1) Fields in Inflection">
    * On the contact: `first name`, `company name`, `lead score`, `email address`, `job title`, `phone number`, etc.
    * On the user: `role`, `last login date`, `user ID`, `permissions level`, etc.
    * On the org (or workspace): `plan`, `workspace created date`, `feature enabled`, `organization ID`, `subscription tier`, `billing status`, etc.
    * On the account: `AE`, `CSM`, `renewal date`, `contract value`, `health score`, `onboarding status`, etc.
  </Accordion>

  <Accordion title="(2) Simple calculations">
    * **Sums**: like summed intent and fit scores (`+`)
    * **Concatenations**: like full name concatenation; first name & last name (`concat()`)
    * **If statements**: if user role = "Admin", show admin-specific content; if login count > 10, return "Active User"; etc. (`if()`)
    * For a full list, visit our tokens glossary
  </Accordion>

  <Accordion title="(3) Advanced functions">
    * **User-level aggregations**:
      * Number of times a user has completed a specific event (`countIf`)
      * User's most recent product events (`getMostRecentProductEventUser`)
      * Top event properties for a specific user (`topEventPropertyValuesUser`)
      * For a full list, visit our tokens glossary
    * **Workspace-level aggregations**:
      * Number of times a specific event has occurred in a workspace (`countIfPersonsGroup`)
      * Top users by events in a workspace (`topEventPropertyValuesOrg`)
      * Whether any user in the workspace has performed specific actions (`countIfPersonsGroupPersonField`)
      * For a full list, visit our tokens glossary
  </Accordion>
</AccordionGroup>

Similar to Excel formulas, calculations and advanced functions can be nested. More on syntax and use of tokens, below.

## Where to use tokens

Tokens are used to personalize journeys and emails.

<Tip>
  Tip: To use token values as audience criteria, leverage a journey that sorts contacts into static lists, based on the token value. (1) Build a journey that branches users or contacts by token (note: this will count as an MMC journey); (2) Add users or contacts to static lists based on their token values; (3) Reference the static lists in the dynamic list.
</Tip>

### Tokens in emails

Once you've created your token, follow the following methods to use the token in an email.

<AccordionGroup>
  <Accordion title="As email copy">
    To include a token in email copy:

    1. In a text content block, select **Tokens** in the menu.

           <img src="https://mintcdn.com/inflection-4b2c0de4/sEk80mgTrRhGEHCY/images/journeys/tokens/01.png?fit=max&auto=format&n=sEk80mgTrRhGEHCY&q=85&s=d8911fe3140d3386dac650ecd7ed4435" alt="Tokens option in the text content block menu" width="522" height="115" data-path="images/journeys/tokens/01.png" />

    2. Search for and select the token to include.

           <img src="https://mintcdn.com/inflection-4b2c0de4/sEk80mgTrRhGEHCY/images/journeys/tokens/02.png?fit=max&auto=format&n=sEk80mgTrRhGEHCY&q=85&s=29f2f48969eba0ba651b64219363467b" alt="Token search dropdown for selecting a token" width="521" height="206" data-path="images/journeys/tokens/02.png" />

    3. Once selected, the token will appear in the email as below. Note that the token will take on the formatting of the text block its in.

           <img src="https://mintcdn.com/inflection-4b2c0de4/sEk80mgTrRhGEHCY/images/journeys/tokens/03.png?fit=max&auto=format&n=sEk80mgTrRhGEHCY&q=85&s=1040fb8ca58b5a18eb530094b10cf07a" alt="Token displayed inline in the email copy" width="498" height="61" data-path="images/journeys/tokens/03.png" />
  </Accordion>

  <Accordion title="In the subject line">
    To include a token in the subject line of the email, begin typing the field or token you wish to include.

    <img src="https://mintcdn.com/inflection-4b2c0de4/sEk80mgTrRhGEHCY/images/journeys/tokens/04.png?fit=max&auto=format&n=sEk80mgTrRhGEHCY&q=85&s=025e3dc2e63d2130cbfb11f573d36ed8" alt="Token suggestions appearing while typing in the subject line" width="483" height="239" data-path="images/journeys/tokens/04.png" />

    Once selected, the token will appear in the subject line as below.

    <img src="https://mintcdn.com/inflection-4b2c0de4/sEk80mgTrRhGEHCY/images/journeys/tokens/05.png?fit=max&auto=format&n=sEk80mgTrRhGEHCY&q=85&s=28baf2ded9596f25bf96a9bbec12778c" alt="Token displayed in the email subject line" width="460" height="130" data-path="images/journeys/tokens/05.png" />
  </Accordion>

  <Accordion title="As an image">
    To use a token to display a dynamic image in an email, drag an image block onto the email and enable Dynamic image. Then copy/paste the token into the **Dynamic Url** editor.

    <img src="https://mintcdn.com/inflection-4b2c0de4/sEk80mgTrRhGEHCY/images/journeys/tokens/06.png?fit=max&auto=format&n=sEk80mgTrRhGEHCY&q=85&s=bed0f47a9435dd5f45cc4efd674a3bd6" alt="Dynamic Url editor with a token pasted in" width="397" height="143" data-path="images/journeys/tokens/06.png" />

    <Tip>
      Tip: To ensure the correct syntax, follow the steps to insert the token in the subject line. Copy the token and paste it into the **Dynamic Url** editor.
    </Tip>
  </Accordion>

  <Accordion title="As a link">
    To use a token to create a personalized link, copy/paste the token in the **Url** editor. Note that the syntax is slightly different (single quotes rather than double).

    <img src="https://mintcdn.com/inflection-4b2c0de4/sEk80mgTrRhGEHCY/images/journeys/tokens/07.png?fit=max&auto=format&n=sEk80mgTrRhGEHCY&q=85&s=c64dbb0b9386b9528b63d1f07f49c616" alt="Url editor with a token using single-quote syntax" width="406" height="139" data-path="images/journeys/tokens/07.png" />

    <Tip>
      Tip: To ensure the correct syntax, follow the steps to insert the token in the subject line. Copy the token and paste it into the **Url** editor. Replace the double quotes with single quotes.
    </Tip>
  </Accordion>
</AccordionGroup>

### Tokens in journeys

To use a token to determine the path of a journey, use the **Branch by token** flow step.

## Testing

<AccordionGroup>
  <Accordion title="In the token editor">
    Test token values directly in the editor. Add a contact to the preview table and click **Add to preview**.

    <img src="https://mintcdn.com/inflection-4b2c0de4/sEk80mgTrRhGEHCY/images/journeys/tokens/08.png?fit=max&auto=format&n=sEk80mgTrRhGEHCY&q=85&s=efb781d8f623eb201f881dd5367b03f0" alt="Token editor preview table with the Add to preview button" width="1324" height="604" data-path="images/journeys/tokens/08.png" />

    Once added, view the token output value and variables used to calculate it. The variables visible in the preview editor will change depending on the variables used in the token calculation.

    <img src="https://mintcdn.com/inflection-4b2c0de4/sEk80mgTrRhGEHCY/images/journeys/tokens/09.png?fit=max&auto=format&n=sEk80mgTrRhGEHCY&q=85&s=6ff597ba5173990ba35aa89cdb2dd6bd" alt="Token preview showing the output value and calculation variables" width="1323" height="152" data-path="images/journeys/tokens/09.png" />

    <Tip>
      Note that the token must be saved to test it.
    </Tip>
  </Accordion>

  <Accordion title="In the email editor">
    To test a token in an email, follow the testing steps detailed in the Visual Email Editor doc. Note that when sending an email test where the email contains a token, a contact with a token value is required to send the test email.
  </Accordion>

  <Accordion title="In a journey canvas">
    To test a token in a journey, clone the journey and replace all actions in journey paths with an **add to static list** flow step. This will provide a preview for how pathing will behave in the actual journey.

    <img src="https://mintcdn.com/inflection-4b2c0de4/sEk80mgTrRhGEHCY/images/journeys/tokens/10.png?fit=max&auto=format&n=sEk80mgTrRhGEHCY&q=85&s=9bc037c68646c853887b2cd0e372ca02" alt="Cloned journey using add to static list flow steps to preview token pathing" width="3024" height="1728" data-path="images/journeys/tokens/10.png" />
  </Accordion>
</AccordionGroup>

## Tokens glossary

<AccordionGroup>
  <Accordion title="Simple calculations">
    #### abSplit(n)

    **Formula:** `abSplit(2)`

    **Components:** `n` = number of splits for A/B testing

    **Use Case:** Randomly assign users to different test groups for A/B testing campaigns

    **Example:** `abSplit(3)` assigns users to groups 1, 2, or 3 for testing three email variants

    #### add (+)

    **Formula:** `value1 + value2`

    **Components:** `value1` and `value2` = numbers to add or strings to concatenate

    **Use Case:** Calculate totals or combine text fields

    **Example:** `column("deal_value") + column("upsell_value")` to get total opportunity value

    #### and

    **Formula:** `condition1 and condition2`

    **Components:** `condition1` and `condition2` = Boolean expressions

    **Use Case:** Combine multiple conditions for complex logic

    **Example:** `column("status") == "Active" and column("last_login") >= now()-30` for active recent users

    #### capitalize()

    **Formula:** `capitalize(string)`

    **Components:** `string` = text to capitalize

    **Use Case:** Format names with proper capitalization

    **Example:** `capitalize(column("first_name"))` converts "john" to "John"

    #### column()

    **Formula:** `column("field_name")`

    **Components:** `field_name` = name of the data field to reference

    **Use Case:** Reference contact, user, org, or account fields in calculations

    **Example:** `column("company_name")` retrieves the company name field

    #### concat()

    **Formula:** `concat(string1, string2, ...string_n)`

    **Components:** `string1, string2, etc.` = text values to combine

    **Use Case:** Combine multiple fields into a single text value

    **Example:** `concat(column("first_name"), " ", column("last_name"))` creates full name

    #### concatWithSpace()

    **Formula:** `concatWithSpace(string1, string2, ...string_n)`

    **Components:** `string1, string2, etc.` = text values to combine with spaces

    **Use Case:** Combine text fields with automatic spacing

    **Example:** `concatWithSpace(column("first_name"), column("last_name"))` creates "John Smith"

    #### contains()

    **Formula:** `contains(text, search_value)`

    **Components:** `text` = text to search in, `search_value` = text to find

    **Use Case:** Check if a field contains specific text

    **Example:** `contains(column("job_title"), "Manager")` returns true if title contains "Manager"

    #### dateTimeFormat()

    **Formula:** `dateTimeFormat(date_time, format_string)`

    **Components:** `date_time` = date field, `format_string` = desired format pattern

    **Use Case:** Format dates for display in emails

    **Example:** `dateTimeFormat(column("renewal_date"), "%m/%d/%y")` formats as "12/15/24"

    #### divide (/)

    **Formula:** `value1 / value2`

    **Components:** `value1` = dividend, `value2` = divisor

    **Use Case:** Calculate percentages or ratios

    **Example:** `column("closed_deals") / column("total_leads")` for conversion rate

    #### endsWith()

    **Formula:** `endsWith(text, suffix)`

    **Components:** `text` = text to check, `suffix` = ending text to match

    **Use Case:** Filter records based on text endings

    **Example:** `endsWith(column("email"), ".edu")` identifies educational email addresses

    #### equal (==)

    **Formula:** `value1 == value2`

    **Components:** `value1` and `value2` = values to compare

    **Use Case:** Compare field values for conditional logic

    **Example:** `column("plan_type") == "Enterprise"` checks for enterprise customers

    #### greater\_than (>)

    **Formula:** `value1 > value2`

    **Components:** `value1` and `value2` = values to compare

    **Use Case:** Compare numeric values or dates

    **Example:** `column("deal_size") > 10000` identifies large deals

    #### greater\_than\_or\_equal (>=)

    **Formula:** `value1 >= value2`

    **Components:** `value1` and `value2` = values to compare

    **Use Case:** Set minimum thresholds

    **Example:** `column("lead_score") >= 80` for high-quality leads

    #### hash()

    **Formula:** `hash(string, seed)`

    **Components:** `string` = text to hash, `seed` = number for randomization

    **Use Case:** Create consistent random assignments based on user data

    **Example:** `hash(column("email"), 0)` creates consistent hash for email personalization

    #### if()

    **Formula:** `if(condition, value_if_true, value_if_false)`

    **Components:** `condition` = boolean test, `value_if_true` = result when true, `value_if_false` = result when false

    **Use Case:** Conditional content or calculations

    **Example:** `if(column("plan") == "Pro", "Premium Features", "Standard Features")` for plan-specific messaging

    #### isEmpty()

    **Formula:** `isEmpty(value)`

    **Components:** `value` = field or expression to test

    **Use Case:** Check for empty or null fields

    **Example:** `isEmpty(column("phone_number"))` returns true if phone is blank

    #### isNotEmpty()

    **Formula:** `isNotEmpty(value)`

    **Components:** `value` = field or expression to test

    **Use Case:** Verify required fields have values

    **Example:** `isNotEmpty(column("company_name"))` confirms company name exists

    #### join()

    **Formula:** `join(separator, string1, ...string_n)`

    **Components:** `separator` = text between values, `string1, etc.` = values to join

    **Use Case:** Combine multiple values with custom separators

    **Example:** `join(", ", column("city"), column("state"))` creates "Austin, TX"

    #### length()

    **Formula:** `length(value)`

    **Components:** `value` = text or array to measure

    **Use Case:** Count characters in text or items in lists

    **Example:** `length(column("description"))` returns character count

    #### lesser\_than (\<)

    **Formula:** `value1 < value2`

    **Components:** `value1` and `value2` = values to compare

    **Use Case:** Find values below thresholds

    **Example:** `column("days_since_login") < 7` for recently active users

    #### lesser\_than\_or\_equal (\<=)

    **Formula:** `value1 <= value2`

    **Components:** `value1` and `value2` = values to compare

    **Use Case:** Set maximum limits

    **Example:** `column("team_size") <= 50` for small to medium teams

    #### mod (%)

    **Formula:** `value1 % value2`

    **Components:** `value1` = dividend, `value2` = divisor

    **Use Case:** Create patterns or cycles

    **Example:** `hash(column("email"), 0) % 2` for even/odd splitting

    #### multiply (\*)

    **Formula:** `value1 * value2`

    **Components:** `value1` and `value2` = numbers to multiply

    **Use Case:** Calculate totals or scale values

    **Example:** `column("seat_count") * column("price_per_seat")` for total cost

    #### not()

    **Formula:** `not(boolean_value)`

    **Components:** `boolean_value` = true/false expression to reverse

    **Use Case:** Invert boolean conditions

    **Example:** `not(column("unsubscribed"))` to find subscribed users

    #### not\_equal (!=)

    **Formula:** `value1 != value2`

    **Components:** `value1` and `value2` = values to compare

    **Use Case:** Exclude specific values

    **Example:** `column("status") != "Canceled"` for active accounts

    #### now()

    **Formula:** `now()`

    **Components:** None

    **Use Case:** Get current date/time for calculations

    **Example:** `now() - column("created_date")` for account age

    #### or

    **Formula:** `condition1 or condition2`

    **Components:** `condition1` and `condition2` = boolean expressions

    **Use Case:** Create inclusive conditions

    **Example:** `column("source") == "Website" or column("source") == "Referral"` for organic leads

    #### randint()

    **Formula:** `randint(start, end)`

    **Components:** `start` = minimum value, `end` = maximum value

    **Use Case:** Generate random numbers for testing or sampling

    **Example:** `randint(1, 100)` creates random number between 1 and 100

    #### replace()

    **Formula:** `replace(text, old_value, new_value)`

    **Components:** `text` = original text, `old_value` = text to replace, `new_value` = replacement text

    **Use Case:** Clean or standardize text data

    **Example:** `replace(column("phone"), "-", "")` removes dashes from phone numbers

    #### round()

    **Formula:** `round(number, digits)`

    **Components:** `number` = value to round, `digits` = decimal places

    **Use Case:** Format numeric values for display

    **Example:** `round(column("conversion_rate"), 2)` shows percentage with 2 decimal places

    #### startsWith()

    **Formula:** `startsWith(text, prefix)`

    **Components:** `text` = text to check, `prefix` = beginning text to match

    **Use Case:** Filter records based on text beginnings

    **Example:** `startsWith(column("phone"), "+1")` identifies US phone numbers

    #### stringToDateTime()

    **Formula:** `stringToDateTime(time_string)`

    **Components:** `time_string` = date in text format

    **Use Case:** Convert text dates to datetime for calculations

    **Example:** `stringToDateTime("2024-12-31")` converts text to date object

    #### subString()

    **Formula:** `subString(text, separator, position)`

    **Components:** `text` = original text, `separator` = split character, `position` = which part to return

    **Use Case:** Extract parts of text fields

    **Example:** `subString(column("full_name"), " ", 1)` extracts first name

    #### subtract (-)

    **Formula:** `value1 - value2`

    **Components:** `value1` = starting value, `value2` = value to subtract

    **Use Case:** Calculate differences or time spans

    **Example:** `column("renewal_date") - now()` for days until renewal

    #### title()

    **Formula:** `title(string)`

    **Components:** `string` = text to format

    **Use Case:** Format text with proper title case

    **Example:** `title(column("job_title"))` converts "product manager" to "Product Manager"

    #### toLower()

    **Formula:** `toLower(string)`

    **Components:** `string` = text to convert

    **Use Case:** Standardize text for comparisons

    **Example:** `toLower(column("industry"))` converts all industry names to lowercase

    #### toUpper()

    **Formula:** `toUpper(string)`

    **Components:** `string` = text to convert

    **Use Case:** Format text for emphasis or standardization

    **Example:** `toUpper(column("state"))` converts state abbreviations to uppercase
  </Accordion>

  <Accordion title="Advanced functions">
    <Accordion title="Available operators">
      #### String Operators

      * `=` or `is_one_of` - **equal to** (accepts multiple values)
      * `!=` or `is_not_one_of` - **not equal to** (accepts multiple values)
      * `contains` - **contains** (accepts multiple values)
      * `not_contains` - **does not contain** (accepts multiple values)
      * `begins_with` - **begins with**
      * `not_begin_with` - **does not begin with**
      * `ends_with` - **ends with**
      * `not_ends_with` - **does not end with**
      * `is_blank` - **blank** (no arguments)
      * `is_not_blank` - **not blank** (no arguments)

      #### Integer/Number Operators

      * `=` or `is_one_of` - **equal to** (accepts multiple values)
      * `!=` or `is_not_one_of` - **not equal to** (accepts multiple values)
      * `>` or `greater_than` - **greater than**
      * `>=` or `greater_or_equal` - **greater than or equal to**
      * `<` or `less_than` - **less than**
      * `<=` or `lesser_or_equal` - **less than or equal to**
      * `between` - **between (inclusive)** (requires 2 values)
      * `number_not_between_includes_null` - **not between** (requires 2 values)
      * `number_is_null` - **blank** (no arguments)
      * `number_is_not_null` - **not blank** (no arguments)
      * `is_zero_or_blank` - **zero or blank** (no arguments)

      #### Boolean Operators

      * `is_true` - **true** (no arguments)
      * `is_false` - **false** (no arguments)

      #### Date Operators

      * `is_on` - **on** (specific date)
      * `not_on` - **not on** (specific date)
      * `before` - **before**
      * `after` - **after**
      * `on_or_before` - **on or before**
      * `on_or_after` - **on or after**
      * `date_between` - **between (inclusive)** (requires 2 dates)
      * `date_not_between` - **not between** (requires 2 dates)
      * `relative_in_past` - **in past** (e.g., "in past 30 days")
      * `relative_timeframe` - **in relative timeframe between (inclusive)**
      * `date_is_null` - **blank** (no arguments)
      * `is_not_blank` - **not blank** (no arguments)
    </Accordion>

    <Info>
      Note: When referencing a product event in a token formula, write the event as it is recorded in Inflection, including spaces and capital letters as relevant.
    </Info>

    #### allOrgs()

    **Formula:** `allOrgs()`

    **Components:** None

    **Use Case:** Get list of all organizations in the tenant for cross-org analysis

    **Example:** `allOrgs()` returns array of all organization data

    #### countIf()

    **Formula:** `countIf("product_activity", column, operator, value, [connector, column2, operator2, value2])`

    **Components:** `"product_activity"` = table referenced, `column` = field to filter, `operator` = comparison type, `value` = filter value

    **Use Case:** Count user-specific product events

    **Example:** `countIf("product_activity", "event_name", =, "campaign_published")` counts campaigns published by user

    **Example filtered by date:** `countIf("product_activity", "event_name", =, "campaign_published", and, "event_date", >=, "2025-01-01")` counts campaigns published by user after January 1, 2025

    #### countIfPersonsGroup()

    **Formula:** `countIfPersonsGroup("product_activity", org_field, column, operator, value, [conditions])`

    **Components:** `"product_activity"` = table referenced, `org_field` = organization identifier, `column` = field to filter, `operator` = comparison, `value` = filter value

    **Use Case:** Count events across all users in an organization

    **Example:** `countIfPersonsGroup("product_activity", "User.org_id", "event_name", =, "user_login")` counts all logins in workspace

    #### countIfPersonsGroupPersonField()

    **Formula:** `countIfPersonsGroupPersonField("product_activity", column, operator, value, [conditions])`

    **Components:** `"product_activity"` = table referenced, `column` = field to filter, `operator` = comparison, `value` = filter value

    **Use Case:** Check if anyone in organization has performed an action

    **Example:** `countIfPersonsGroupPersonField("product_activity", "event_name", =, "integration_connected")` checks if org has any integrations

    #### getMostRecentProductEventAllUsers()

    **Formula:** `getMostRecentProductEventAllUsers(event_name, [properties], [filters], time_frame)`

    **Components:** `event_name` = product event type, `properties` = fields to return, `filters` = conditions, `time_frame` = days to look back

    **Use Case:** Find the latest occurrence of an event across all users

    **Example:** `getMostRecentProductEventAllUsers("campaign_published", ["campaign_name"], [], 30)` gets most recent campaign published anywhere

    #### getMostRecentProductEventOrg()

    **Formula:** `getMostRecentProductEventOrg(event_name, [properties], [filters], time_frame)`

    **Components:** `event_name` = product event type, `properties` = fields to return, `filters` = conditions, `time_frame` = days to look back

    **Use Case:** Find the latest event within user's organization

    **Example:** `getMostRecentProductEventOrg("template_created", ["template_name"], [], 7)` gets org's most recent template

    #### getMostRecentProductEventUser()

    **Formula:** `getMostRecentProductEventUser(event_name, [properties], [filters], time_frame)`

    **Components:** `event_name` = product event type, `properties` = fields to return, `filters` = conditions, `time_frame` = days to look back

    **Use Case:** Find user's most recent activity of a specific type

    **Example:** `getMostRecentProductEventUser("login", ["login_time"], [], 10)` gets user's last login details

    #### getProductEventByAllUsers()

    **Formula:** `getProductEventByAllUsers(event_name, [properties], [filters], sort_field, sort_order, time_frame, limit)`

    **Components:** `event_name` = event type, `properties` = fields to return, `filters` = conditions, `sort_field` = field to sort by, `sort_order` = asc/desc, `time_frame` = days, `limit` = max results

    **Use Case:** Retrieve events across all users with specific criteria

    **Example:** `getProductEventByAllUsers("campaign_created", ["campaign_name"], [], "event_date", desc, 30, 10)` gets recent campaigns from all users

    #### getProductEventByOrg()

    **Formula:** `getProductEventByOrg(event_name, [properties], [filters], sort_field, sort_order, time_frame, limit)`

    **Components:** `event_name` = event type, `properties` = fields to return, `filters` = conditions, `sort_field` = sort field, `sort_order` = asc/desc, `time_frame` = days, `limit` = max results

    **Use Case:** Get organization-specific events with sorting and filtering

    **Example:** `getProductEventByOrg("user_invited", ["invitee_email"], [], "event_date", desc, 7, 5)` gets recent invitations in org

    #### getProductEventByUser()

    **Formula:** `getProductEventByUser(event_name, [properties], [filters], sort_field, sort_order, time_frame, limit)`

    **Components:** `event_name` = event type, `properties` = fields to return, `filters` = conditions, `sort_field` = sort field, `sort_order` = asc/desc, `time_frame` = days, `limit` = max results

    **Use Case:** Retrieve user's events with specific parameters

    **Example:** `getProductEventByUser("template_used", ["template_name"], [], "event_date", desc, 30, 5)` gets user's recent template usage

    #### orgCount()

    **Formula:** `orgCount()`

    **Components:** None

    **Use Case:** Count how many organizations a user belongs to

    **Example:** `orgCount()` returns number of orgs for multi-org users

    #### topEventPropertyValuesAllUsers()

    **Formula:** `topEventPropertyValuesAllUsers(N, event_name, [properties], [filters], group_field, sort_order, time_frame)`

    **Components:** `N` = number of results, `event_name` = event type, `properties` = fields to return, `filters` = conditions, `group_field` = grouping field, `sort_order` = asc/desc, `time_frame` = days

    **Use Case:** Find top values across all users for analytics

    **Example:** `topEventPropertyValuesAllUsers(5, "page_viewed", ["page_name"], [], "page_name", desc, 30)` gets most viewed pages

    #### topEventPropertyValuesOrg()

    **Formula:** `topEventPropertyValuesOrg(N, event_name, [properties], [filters], group_field, sort_order, time_frame)`

    **Components:** `N` = number of results, `event_name` = event type, `properties` = fields to return, `filters` = conditions, `group_field` = grouping field, `sort_order` = asc/desc, `time_frame` = days

    **Use Case:** Identify top activity within an organization

    **Example:** `topEventPropertyValuesOrg(10, "feature_used", ["feature_name"], [], "feature_name", desc, 7)` gets org's most used features

    #### topEventPropertyValuesUser()

    **Formula:** `topEventPropertyValuesUser(N, event_name, [properties], [filters], group_field, sort_order, time_frame)`

    **Components:** `N` = number of results, `event_name` = event type, `properties` = fields to return, `filters` = conditions, `group_field` = grouping field, `sort_order` = asc/desc, `time_frame` = days

    **Use Case:** Analyze user's top activities or preferences

    **Example:** `topEventPropertyValuesUser(3, "email_clicked", ["link_url"], [], "link_url", desc, 14)` gets user's most clicked links

    #### countPageVisits()

    **Formula:** `countPageVisits([condition], window)`

    **Components:** `condition` (optional) = a web condition helper, or `allOf()`/`anyOf()`, to filter which page visits count; `window` = a number of rolling days, or a `between()` date range, to look back

    **Use Case:** Personalize based on how many times a contact visited a specific page or set of pages

    **Example:** `countPageVisits(urlContains("/pricing"), 30)` → number of pricing-page visits in the last 30 days; **Fallback:** `0`

    #### lastPageVisitAt()

    **Formula:** `lastPageVisitAt([condition])`

    **Components:** `condition` (optional) = a web condition helper to filter which page visit to match (no window - always returns the most recent match)

    **Use Case:** Trigger content based on when a contact last visited a page

    **Example:** `lastPageVisitAt(urlContains("/pricing"))` → date/time of the contact's most recent pricing-page visit; **Fallback:** `2000-01-01`

    #### lastPageVisit()

    **Formula:** `lastPageVisit(property, [condition])`

    **Components:** `property` = one of `url`, `page_title`, `referrer`, `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`; `condition` (optional) = a web condition helper to filter which page visit to match

    **Use Case:** Reference the specific page a contact last viewed in email copy

    **Example:** `lastPageVisit("url", urlContains("/pricing"))` → URL of the contact's most recent pricing-page visit; **Fallback:** `there`

    #### countEmailEvents()

    **Formula:** `countEmailEvents(action, window)`

    **Components:** `action` = one of `delivered`, `blocked`, `open`, `click`, `bounced`, `spam_reported`, `unsubscribed`; `window` = a number of rolling days, or a `between()` date range, to look back (no condition helper for email events)

    **Use Case:** Personalize based on how engaged a contact has been with past emails

    **Example:** `countEmailEvents("click", 30)` → number of email clicks in the last 30 days; **Fallback:** `0`

    #### lastEmailEventAt()

    **Formula:** `lastEmailEventAt(action)`

    **Components:** `action` = one of `delivered`, `blocked`, `open`, `click`, `bounced`, `spam_reported`, `unsubscribed` (no window - always returns the most recent match)

    **Use Case:** Time a follow-up based on a contact's most recent email activity

    **Example:** `lastEmailEventAt("open")` → date/time of the contact's most recent email open; **Fallback:** `2000-01-01`

    #### lastClickedUrl()

    **Formula:** `lastClickedUrl()`

    **Components:** None

    **Use Case:** Reference the last link a contact clicked to follow up on their interest

    **Example:** `lastClickedUrl()` → the URL the contact most recently clicked in an email; **Fallback:** `there`

    #### countFormSubmissions()

    **Formula:** `countFormSubmissions([condition], window)`

    **Components:** `condition` (optional) = `formName` or `formNameContains`; `window` = a number of rolling days, or a `between()` date range, to look back

    **Use Case:** Personalize based on how many times a contact submitted a given form

    **Example:** `countFormSubmissions(formName("Demo Request"), 30)` → number of "Demo Request" form submissions in the last 30 days; **Fallback:** `0`

    #### lastFormSubmissionAt()

    **Formula:** `lastFormSubmissionAt([condition])`

    **Components:** `condition` (optional) = `formName` or `formNameContains` (no window - always returns the most recent match)

    **Use Case:** Time a follow-up based on when a contact last submitted a form

    **Example:** `lastFormSubmissionAt(formName("Demo Request"))` → date/time of the contact's most recent "Demo Request" form submission; **Fallback:** `2000-01-01`

    #### countWebhookSubmissions()

    **Formula:** `countWebhookSubmissions([condition], window)`

    **Components:** `condition` (optional) = `webhookName` or `webhookNameContains`; `window` = a number of rolling days, or a `between()` date range, to look back

    **Use Case:** Personalize based on how many times a contact triggered a given webhook

    **Example:** `countWebhookSubmissions(webhookName("Intent Feed"), 30)` → number of "Intent Feed" webhook submissions in the last 30 days; **Fallback:** `0`

    #### lastWebhookSubmissionAt()

    **Formula:** `lastWebhookSubmissionAt([condition])`

    **Components:** `condition` (optional) = `webhookName` or `webhookNameContains` (no window - always returns the most recent match)

    **Use Case:** Time a follow-up based on when a contact last triggered a webhook

    **Example:** `lastWebhookSubmissionAt(webhookName("Intent Feed"))` → date/time of the contact's most recent "Intent Feed" webhook submission; **Fallback:** `2000-01-01`

    #### countCampaignMemberships()

    **Formula:** `countCampaignMemberships([condition], window)`

    **Components:** `condition` (optional) = `campaignType`, `campaignNameContains`, `memberStatus`, or `hasResponded`; `window` = a number of rolling days, or a `between()` date range, to look back, measured against the member-added date

    **Use Case:** Personalize based on a contact's Salesforce campaign membership history

    **Example:** `countCampaignMemberships(campaignType("tradeshow"), 90)` → number of tradeshow campaign memberships added in the last 90 days; **Fallback:** `0`

    #### lastCampaignMembershipAt()

    **Formula:** `lastCampaignMembershipAt([condition])`

    **Components:** `condition` (optional) = `campaignType`, `campaignNameContains`, `memberStatus`, or `hasResponded` (no window - always returns the most recent match)

    **Use Case:** Time a follow-up based on when a contact was last added to a Salesforce campaign

    **Example:** `lastCampaignMembershipAt(campaignType("tradeshow"))` → date/time the contact was most recently added to a tradeshow campaign; **Fallback:** `2000-01-01`

    #### Condition helpers

    **Formula:** `helper(value)` - used inside the count or accessor function for its source

    **Components:** `value` = a single string, or a list of strings (a list matches if **any** value matches, e.g. `urlContains(["/pricing", "/plans"])`); helpers ending in `Contains` match on a partial/substring basis, all others require an exact, whole-value match

    **Use Case:** Narrow down which page visit, form, webhook, or campaign activity a function counts or matches

    **Example:**

    | Helper | Source | Match type | Example |
    | - | - | - | - |
    | `urlContains` | Page visits | Substring | `urlContains("/pricing")` |
    | `pageTitleContains` | Page visits | Substring | `pageTitleContains("Pricing")` |
    | `referrerContains` | Page visits | Substring | `referrerContains("google.com")` |
    | `utmSource` | Page visits | Exact | `utmSource("newsletter")` |
    | `utmMedium` | Page visits | Exact | `utmMedium("email")` |
    | `utmCampaign` | Page visits | Exact | `utmCampaign("q3_launch")` |
    | `utmTerm` | Page visits | Exact | `utmTerm("crm")` |
    | `utmContent` | Page visits | Exact | `utmContent("hero_cta")` |
    | `formName` | Form submissions | Exact | `formName("Demo Request")` |
    | `formNameContains` | Form submissions | Substring | `formNameContains("Demo")` |
    | `webhookName` | Webhook submissions | Exact | `webhookName("Intent Feed")` |
    | `webhookNameContains` | Webhook submissions | Substring | `webhookNameContains("Intent")` |
    | `campaignType` | Salesforce campaign memberships | Exact | `campaignType("tradeshow")` |
    | `campaignNameContains` | Salesforce campaign memberships | Substring | `campaignNameContains("Summit")` |
    | `memberStatus` | Salesforce campaign memberships | Exact | `memberStatus("Responded")` |
    | `hasResponded` | Salesforce campaign memberships | Exact (`true`/`false`) | `hasResponded(true)` |

    #### allOf()

    **Formula:** `allOf(conditionA, conditionB, ...)`

    **Components:** `conditionA, conditionB, etc.` = condition helpers to combine, all from the same source; nesting is capped at 3 levels

    **Use Case:** Require multiple criteria to match at once, like a page visit that also came from a specific UTM source

    **Example:** `countPageVisits(allOf(urlContains("/pricing"), utmSource("google")), 30)` → counts pricing-page visits that came from Google in the last 30 days

    #### anyOf()

    **Formula:** `anyOf(conditionA, conditionB, ...)`

    **Components:** `conditionA, conditionB, etc.` = condition helpers to combine; matches if **any** one of them matches

    **Use Case:** Match activity across a set of related pages, forms, webhooks, or campaigns

    **Example:** `countPageVisits(anyOf(urlContains("/pricing"), urlContains("/plans")), 30)` → counts visits to either the pricing or plans page in the last 30 days

    #### between()

    **Formula:** `between("YYYY-MM-DD", "YYYY-MM-DD")`

    **Components:** two dates = the start and end of the range; both days are fully included

    **Use Case:** Look back over a fixed calendar range instead of a rolling number of days

    **Example:** `countEmailEvents("click", between("2026-01-01", "2026-01-30"))` → counts email clicks between January 1 and January 30, 2026, inclusive
  </Accordion>
</AccordionGroup>

## Marketing Activity Tokens

This family of functions pulls a contact's marketing activity - page visits, email events, form submissions, webhook submissions, and Salesforce campaign memberships - into a token. Use them to personalize copy based on what a contact has actually done, like the pages they've viewed or the emails they've engaged with.

Like any other token, a marketing activity token is a separate asset: build it first in the token builder using these functions, then reference the saved token in your email copy or subject line (see [Tokens in emails](#tokens-in-emails)).

For example, create a token named `last_pricing_page` with the formula `lastPageVisit("page_title", urlContains("/pricing"))`, then reference it in your email copy to greet a contact by the topic they've shown the most interest in:

> Hi `{{first_name}}`, since you've been checking out `{{last_pricing_page}}`, here's a closer look at our plans.

Or create a token named `email_clicks_30d` with the formula `countEmailEvents("click", 30)` and reference recent engagement to decide what to send next:

> It looks like you clicked through our last email - `{{email_clicks_30d}}` times this month. Here's what else you might like.

<Info>
  Misspell a form, webhook, or campaign name in a condition helper, and it simply matches nothing - the token returns `0` (or the relevant fallback) with no error. If a token unexpectedly shows `0`, double-check the spelling of the name you passed in.
</Info>

<Info>
  `urlContains` matches against the domain and path only - it doesn't match against the query string. To match on UTM values, use the `utm...` helpers instead.
</Info>

<Info>
  The `last...At` accessors (`lastPageVisitAt`, `lastEmailEventAt`, `lastFormSubmissionAt`, `lastWebhookSubmissionAt`, `lastCampaignMembershipAt`) ignore the window entirely - they always return the most recent match, no matter how old it is.
</Info>

See the **Tokens glossary** above for the full list of marketing activity functions, condition helpers, and combinators, including their formulas, arguments, and fallback values.

# Answers to Common Questions

## Referencing fields and saved tokens

To reference a raw contact/product field directly inside a formula:

```text theme={null}
{{column('field_name')}}
```

Typing the field name while creating a token will auto-suggest it — click the suggestion to pull it in.

To reference an existing saved token from within another token or asset:

```text theme={null}
{{token('token_name')}}
```

Note: If you go the saved-token route, consider giving it a generic fallback value (see below) rather than leaving null cases unhandled.

## Building conditional logic

### Combining two conditions with AND

The token language's `if()` doesn't have a native AND operator. To combine two conditions, nest a second `if()` inside the true-branch of the first:

```text theme={null}
if(column("Country")=="United States", if(column("email")=="vip@example.com", "true", "false"), "false")
```

Only when the outer condition is true does the inner condition get evaluated; if the inner condition is also true, the formula returns the real value — otherwise it falls through to `"false"`.

Note: Every branch needs an explicit false/else output. Don't assume the token falls through to a sensible default if you leave a branch open.

### Boolean fields with a null state

If your boolean field actually has three possible states — true, false, and null/not-yet-populated — comparing it directly to a literal (like `1`) in a single check can throw an "incompatible argument type" error. Instead, write two separate checks: first test whether the field is empty, then test whether it's true. Between the two checks, you've covered all three real-world states.

Note: If a boolean or date comparison still errors after applying this two-check pattern, treat it as a possible token-engine issue rather than a formula mistake, and contact Inflection support.

### String functions abort silently to the fallback value on a null column

If a formula uses a string function (`toLower()`, `startsWith()`, `contains()`, `endsWith()`) on a column that can be null, the function doesn't evaluate to false and fall through to your else branch — it **aborts the whole formula** and jumps straight to the token's configured Fallback value, with no visible error.

**Fix:** guard every string-function call with `isEmpty()` on that column first, and order your branches so the "is this actually blank" check happens before any branch that calls a string function.

Note: Test a reordered formula in the token's draft/staging version first, confirm the output, then promote it to the live token — the live token keeps running the old formula until you explicitly promote the change. Also double-check any fallback values on related tokens that are literal `""` strings or the word `"default"` — these can silently pass as if they were real data and mask this same bug.

### Fallback values

A fallback value is a required field when you create a token — set it at token-creation time rather than trying to handle every null case only inside the formula logic.

Note: Watch for fallback values that are themselves silently wrong — a literal `""` or the word `"default"` can look like real data at a glance.

## Counting and rolling up activity

### `countIf()` syntax

`countIf()` takes an object name, then property/operator/value triples chained with `and`:

```text theme={null}
countIf("product_activity", "event_name", =, "Trial Started", and, "event_properties.category", =, "27")
```

Event-level custom properties need the `event_properties.` prefix — a bare property name will error.

Note: `countIf()` tokens (like all tokens) cannot be used directly as dynamic-list/audience criteria — see Known constraints below.

### Tokens can't count distinct users, only activity occurrences

A token can roll up product-activity occurrences, but it cannot produce a distinct-user count directly — for example, "how many active users in this org" isn't something a token formula can compute. Instead, bring that count over as its own field: on the account (via Salesforce) or on the user record (via your warehouse). If you can model it as an org-level attribute, that's generally the better placement than the account — but confirm first whether org attributes are actually flowing in from your connections.

## Date and time tokens

### `now()` always returns UTC

The `now()` function always returns UTC time — it never converts to your account's local timezone. This is standing behavior, not a bug. To make the output explicit or approximate your local time, wrap it with `dateTimeFormat()`:

```text theme={null}
dateTimeFormat(now(), "%b %d, %Y %I:%M %p UTC")
```

To approximate a specific local timezone, subtract a day-fraction offset (this converts the token's output from datetime to string):

```text theme={null}
dateTimeFormat(now() - 0.2917, "%b %d, %Y %I:%M %p")
```

Note: An offset like this is a fixed approximation — it doesn't account for daylight saving shifts, so revisit the offset value around DST changes if you rely on it.

### Tokens can only populate String-typed destination fields

A token can't stamp a native date/time-typed field directly. If you need a date/time value on a String-typed contact/account field, populate that String field with a `dateTimeFormat()`-formatted token instead. This has been confirmed safe for downstream warehouse/dbt use, since raw data typically gets cleaned before it reaches production tables.

## Using a token in a journey flow step

In an **Update Value** flow step, use the **Update by** selector to choose **Token**, then pick which token to use for the update.

### Worked pattern: turning recurring activity into a durable field

If you want to turn recurring product activity (like page views) into a contact/account field that another system can act on — not just display it in an email — this pattern works:

1. Build one token per time window or dimension you need (e.g., activity in the last 24 hours, last 7 days, or a rollup like `column("account_users")` for account-level counts).
2. Build a dynamic list of who should get the field stamped.
3. Create a journey scoped to that audience with an **Update Value by token** step to write each token onto the relevant contact/account field.
4. Because tokens are point-in-time snapshots — not live-updating — schedule the journey to re-run periodically (daily, for a 24-hour-window token) so the field stays current.
5. Spot-check a few contacts in each token's Preview Table before treating the values as reliable.

Note: The point-in-time-snapshot caveat is the load-bearing part of this recipe. Without the scheduled re-run, the stamped field goes stale the moment underlying activity changes.

## Testing and previewing tokens

### Preview mode won't resolve tokens — use a test send

In order for a token to populate, it needs a specific person to personalize for. A test send lets you select that person; plain Preview mode does not, so tokens won't resolve there. Always test with a contact who actually has the relevant field populated — testing against a contact with a null field will make a working token look broken.

### Multi-org/multi-user contacts: preview "scrolling"

If you're previewing an org-scoped token (e.g., `org.id`) for a contact tied to multiple orgs or users, the previewed value may cycle or "scroll" between the different possible values mid-session. This is expected behavior, not a bug. To lock the preview to one specific value, check **Receive as an org-user** and select the specific user-org combination you want to preview.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.