A custom attribute records something the flow already knows onto the conversation record: whether a payment link was sent, whether the call was handed to a live agent, which plan the customer chose. It never reads the conversation, so where the branch carrying the node never ran, the value is empty.
Two pages sit beside this one. Conversation Attributes covers what both kinds have in common: the edition, the switch that turns them on, and where recorded values surface. AI Insights covers the other tab, for values a model infers from the transcript after the conversation ends. This article is about the Custom Attributes tab and the Set Attribute node that writes the values.
Defining an attribute
Open the project settings gear, then Conversation Attributes, then the Custom Attributes tab.

1. Custom Attributes tab: The second tab of Conversation Attributes. AI Insights sits on the first one; the two lists are separate, including their limits.
2. Key: The attribute's identifier, and the name the column, the filter and the export use. It must start with a letter or an underscore and can then contain letters, digits and underscores only. Two attributes cannot share a key, and the check ignores case. The placeholder suggests the shape: e.g. selected_plan.
3. Output Type: What the attribute holds. See the table below. The type decides which value editor the Set Attribute node offers and how the value is displayed.
4. Possible Values: For a Category or Categories type, the closed list of values the attribute can hold, entered one at a time as chips.
Each row also carries its own switch, so an attribute can be disabled instead of deleted.
Output types
| Output Type | Holds | What you configure |
|---|---|---|
| Category | One value from your fixed list | Possible Values |
| Categories | Several values from your fixed list | Possible Values |
| Free text | One text value | Nothing further |
| Free text list | Several text values | Nothing further |
| Score (whole number) | One whole number inside a range | A minimum and a maximum |
| Score list (whole number) | Several whole numbers inside a range | A minimum and a maximum |
| Score (decimals) | One decimal number inside a range | A minimum and a maximum |
| Score list (decimals) | Several decimal numbers inside a range | A minimum and a maximum |
A score's minimum must be lower than its maximum, and decimal scores are held to two decimal places. If a supervisor will ever filter or count on the attribute, make it a Category: category values are the only ones the Conversations filter offers as clickable chips.
Setting the value in a flow
The value is written by a Set Attribute node, described in the palette as "Set custom attribute values in the flow to persist them after the conversation."
The node is available in every flow type: the Main flow, Survey, subflows, the fallback flow, an intent's Actions flow, the Post Session Flow, and an AI Agent tool's own workflow.
The node's configuration

1. Key: A pick list of the project's enabled custom attributes. Keys are not typed here, so a node can only ever write to an attribute that exists. A node can carry more than one row, one per key, so several attributes can be set at once.
2. Value: What to write. In Static mode the editor follows the attribute's output type: a dropdown limited to the possible values for a Category type, range-checked and precision-checked entries for a score type, and free entries for a text type. In Variable mode it becomes a rich text field where you insert context variables with % mentions.
3. Static / Variable toggle: Which of the two modes the row is in. The label shows the mode the row is currently in, not the mode clicking it will switch to, so "Static" beside the switch means this row is Static.
A row can hold up to 5 values, and more than one value is only accepted when the output type is a list type.
Static and Variable
In Static mode you choose the value yourself, from the attribute's own definition: a Category attribute offers its possible values, and a score attribute accepts a number inside its range. In Variable mode the value comes from the flow, resolved when the node runs, so it can carry something the flow worked out earlier such as an API result or a customer's answer.
Use Static wherever the answer is fixed at design time, which is most of the time. TransferredToHuman on the branch that performs a transfer is always true, so there is nothing to compute. Keep Variable for values that genuinely differ per conversation.
⚠️ Warning: A Variable row is never checked against the attribute's definition, at design time or at runtime. A Category attribute can end up holding a value that is not in its list, and a score can land outside its range, with nothing to indicate it. Make sure the flow can only produce values the attribute is defined to hold.
When the value is recorded
⚠️ Warning: Values are written to the conversation record at the end of the conversation, not at the moment the node runs. A conversation that never reaches its end records nothing at all, even though the node ran and the value was carried by the flow the whole time.
Until then, the values set so far are readable inside the flow through the CustomAttributes context variable, which holds them as JSON. A list output type renders as a JSON array, and every other type as a single value.
When the node is marked invalid
While Conversation Attributes is switched off for the project, a Set Attribute node cannot be used: flow validation marks it invalid and publishing is refused. The node is also invalid when a row points at a key that does not exist or has been disabled, when a row has no value, when a row has more than 5 values, when a non-list type is given more than one value, when a Variable row's value is empty, or when a Static value is not allowed by the attribute's own definition.
Exporting a flow strips the Set Attribute nodes' configured rows. The nodes arrive in the target project empty, so their keys and values are chosen again there, against that project's own attribute definitions.
Changing an attribute after it exists
The key and the output type cannot be changed once an attribute has been saved; both fields are locked on an existing row. What stays editable is the possible values, the score range, the order and the enable switch. If a key turns out to be wrong, the way out is a new attribute beside it.
An attribute that a flow uses is also protected. While a Set Attribute node in the same project version still references it, you cannot delete or disable the attribute, remove a possible value the node writes, or narrow a score range past a value the node writes. Remove it from the flows first. The check looks at Static values configured in the nodes, so a Variable row, which writes whatever it resolves to at runtime, cannot be protected this way.
Where the values appear
Recorded values show on the conversation record in a Custom Attributes block, as optional columns on the Conversations page, as filters there and on the Datasets page of AI Testing, and in both exports. Conversation Attributes covers all of that for both kinds of attribute.
Limits and naming rules
| Item | Value |
|---|---|
| Custom attributes per project version | 20, counted separately from the 20 AI Insights |
| Key | Up to 100 characters; must start with a letter or underscore, then letters, digits and underscores; unique among the project's attributes, ignoring case |
| Possible values on a Category type | Up to 20 values, each up to 50 characters |
| Decimal places on a decimal score | 2 |
| Values per key in one Set Attribute row | 5, and more than one only for a list output type |
| Length of a recorded value | 2,000 characters; longer values are trimmed to it |

