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, placed on the branch of the flow that already knows the answer.
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. Overwrite / Append switch: Appears at the right of the Value heading, only when the key has a list output type. Overwrite (the default) replaces whatever the conversation already holds for that key with this row's values; Append adds this row's values to them. It works in Static and Variable mode alike. Rows created before the switch existed open on Overwrite, so existing flows behave as they did.
4. Static / Variable toggle: Which of the two modes the row is in.
Both switches label the row's current state, not the state clicking will switch to: "Static" beside the toggle means this row is Static, and "Overwrite" means this row overwrites.
A row can hold up to 10 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.
For a list output type in Variable mode, a variable that holds a list is recorded as separate values, while a variable combined with other text in the same field is recorded as one value.
⚠️ Warning: A Variable row is not validated against the attribute's definition, as the info icon beside Value on the row points out. A value outside the Category list or the score range is still recorded on the conversation, but it cannot be used in filters. Make sure the flow can only produce values the attribute is defined to hold.
Collecting values from several points in a conversation
With Overwrite, each node that writes a key replaces what earlier nodes wrote, so only the last one reaches the record. To collect values from several points in one conversation, set each of those rows to Append.
For example, a billing assistant records every reason a customer raised. Define an attribute with Key contact_reasons, Output Type Categories, and the possible values billing_dispute, payment_link, plan_change and refund_request. Then:
- On the branch that handles a disputed charge, add a Set Attribute node with Key
contact_reasons, Static valuebilling_dispute, and the switch set to Append. - On the branch that sends a payment link, add a second Set Attribute node with Key
contact_reasons, Static valuepayment_link, and the switch set to Append.
A conversation that runs through both branches records both values, billing_dispute and payment_link. Left on Overwrite, the same conversation would record only payment_link.
Appended values are not de-duplicated: a value appended twice is recorded twice. An attribute with a single-value output type always keeps only the last value set.
When the value is recorded
Values are written to the conversation record when the conversation ends, however it ends, not at the moment the node runs.
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 of every value collected so far, and every other type as a single value.
When the node is marked invalid
While Conversation Attributes is switched off for the project, the Set Attribute node does not appear in the palette, and a Set Attribute node already in a flow is marked invalid, so 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 10 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.
A key with more than three values shows the first three and a +N control. In a Conversations column, hovering it lists the rest; on the conversation record, clicking it expands them in place. Both exports carry every value, separated by commas.
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 | 10, and more than one only for a list output type |
| Values per attribute in one conversation | 10 (list types); further values are dropped |
| Length of a recorded value | 2,000 characters; longer values are trimmed to it |

