Skip to content

Variables

A variable is a path between double braces that SquadOS replaces with a run’s value. For example, Hi, {{contact.first_name}}! may become Hi, Marina!.

The value is resolved when the step runs, not when you save the flow. Two runs of the same version may therefore produce different text.

The editor offers Insert variable in compatible fields. The picker appears in:

  • the instruction in Analyze with AI;
  • Send message text and WhatsApp template values;
  • the subject and body in Send email;
  • the title in Get the contact’s conversation;
  • the instruction in Hand off to agent;
  • the value in Save to contact;
  • the URL, header values and body in Call webhook;
  • both sides of each comparison in Condition.

In Pick contact by identity, the runtime also interpolates the value, but this field does not show the picker. If needed, type a complete valid path such as {{contact.identity_value}}.

The picker is contextual: it shows the recommended list for that point in the flow. Recognized variables are highlighted; a variable the editor does not recognize turns red. Red is an important warning, but it does not currently guarantee publication will be blocked: an unknown field inside an available namespace may pass and become blank at run time. Do not publish while any variable is red.

They appear when every path reaching the step provides a conversation.

VariableValue
conversation.transcriptup to the 50 most recent messages, in chronological order, one line per message as role: content
conversation.statustechnical state: active, completed or failed
conversation.inbox_idinbox ID
conversation.agent_idassociated AI agent ID
conversation.assigned_toresponsible person’s ID
conversation.channel_sourcetechnical channel source

The full history may contain more than 50 messages. conversation.transcript keeps only the 50 most recent; do not use it as a complete archive or assume the beginning of the conversation is present.

The runtime also recognizes conversation.id and conversation.ai_enabled, although the current picker does not show them. Because this difference may change, prefer paths offered by the interface and test any manual use before activation.

VariableValue
contact.first_namefirst whitespace-separated part of display_name
contact.display_namename as received from the channel or record
contact.identity_valuethe contact’s phone, email, username or other identifier
contact.metadataJSON object containing custom fields

Append a key to read a custom field: {{contact.metadata.city}}. metadata is an open object; the picker does not list each key, so you must type it.

contact.first_name is not stored as a separate field. It is derived from the first part of display_name. If the contact has no name, the result may be blank; if the received name is a phone number or email, that value may appear instead. For a safe greeting, keep fixed text that still works without the variable.

The runtime also recognizes contact.id, contact.identity_type and contact.outbound_opt_out_at, but the picker does not offer them.

The namespace is trigger.payload.

For the Contact added to list trigger, the picker offers:

VariableValue
trigger.payload.list_namelist name
trigger.payload.list_idlist ID
trigger.payload.consent_sourceconsent source received in the event

For the Schedule trigger, the real payload contains trigger.payload.scheduled_for and trigger.payload.fired_at. The picker incorrectly shows the three list fields; type the schedule paths manually if you need them. The other triggers do not promise dedicated payload fields.

Objects inside trigger.payload can also be accessed with dot notation, provided the event actually supplies that structure.

Each field declared in Analyze with AI becomes a variable after the step finishes. Its path combines the node ID and field name, for example {{n5.buying_intent}}.

The output does not exist in the producing step itself or above it. It only appears in downstream steps that the analysis necessarily reaches.

TriggerConversationContactUseful trigger.payload
Conversation eventyesyesnot documented
Idle conversationyesyesnot documented
Contact tagnoyesnot documented
Contact added to listnoyeslist and consent
Schedulenonorun timestamps
Audiencenoyesnot documented

Pick contact starts providing a contact only to the steps below it. Get the contact’s conversation requires a contact and starts providing a conversation only below it.

When paths converge, the intersection applies: the step receives only what every previous path guarantees. If Conversation event and Contact tag reach the same step, both provide a contact, but only one provides a conversation; conversation variables are therefore unavailable after the join. Put Get the contact’s conversation in the path that starts without a conversation, or keep the branches separate.

Publication blocks an absent namespace or an AI output that is not upstream. It validates namespace availability, however, not the catalog of internal fields. {{conversation.status}} is rejected where there is no conversation, but {{conversation.unknown_field}} may be accepted where a conversation exists and resolve to blank.

  • a missing or null value becomes blank text;
  • a number or boolean becomes text;
  • an object or array becomes JSON;
  • whitespace inside the braces is accepted, as in {{ contact.display_name }};
  • paths accept letters, numbers, underscores and dots;
  • an unfinished brace, a hyphen in the path or another unrecognized form may remain literally in the text.

If an entire message or email subject becomes blank after interpolation, that run fails with a configuration alert. Five consecutive failures of the same kind in the same step deactivate the automation; a successful run before then ends the sequence. Correct the value, save and reactivate the automation when needed.

Prefer a textual fallback: Hello! is safe; only {{contact.first_name}} is not. For conditional logic, check the variable with is empty before sending.

  1. Use the picker whenever it offers the path.
  2. Remove every red marker even if the flow still allows saving.
  3. Confirm that every path to the step provides the same context.
  4. Test a contact with no name and a missing metadata field.
  5. For schedules, type scheduled_for or fired_at with the trigger.payload prefix.
  6. Check Activity after the first real runs.

The test uses the steps’ real execution, subject to the limits described for each action. In particular, the Contact added to list test does not currently populate the trigger’s list data. Do not treat a blank field in that simulation as proof that the real event will also be blank.