Actions
Actions change data, send content, or connect the flow to another system. They are available in the palette’s Actions tab.
| Group | Action | What it does |
|---|---|---|
| Artificial intelligence | Analyze with AI | produces a structured result and selects the flow output according to its type |
| Messaging | Send message | sends text through the conversation’s channel |
| Messaging | Send email | sends an email to the flow’s contact |
| Campaigns | Send campaign | sends an email to an entire list |
| Contacts | Apply tag | adds or removes contact tags |
| Contacts | Pick contact | puts a fixed contact or an identity match into the flow context |
| Contacts | Save to contact | writes a value to a custom field |
| Conversations | Tag conversation | adds or removes conversation tags |
| Conversations | Add private note | writes a note on the conversation that only the team sees |
| Conversations | Get the contact’s conversation | finds or creates a conversation in an inbox |
| Support | Assign | sends the conversation to a person or the queue |
| Support | Hand off to agent | changes the conversation’s AI agent |
| Support | Resolve | closes the support conversation and chooses who takes over next |
| External systems | Call webhook | makes an HTTP request to another system |
Understand context before an action
Section titled “Understand context before an action”Each run carries a context. Depending on the trigger and previous steps, it may contain a contact, a conversation, or both.
- Send message, Tag conversation, Add private note, Assign, Hand off to agent, and Resolve require a conversation.
- Send email, Apply tag, Save to contact, and Get the contact’s conversation require a contact.
- Pick contact adds or replaces the contact in context. When the contact changes, it removes the old conversation from context.
- Get the contact’s conversation adds a conversation to context.
Publishing refuses the flow when no path delivers a conversation to the steps that require one, or a contact to Send email and Get the contact’s conversation. In other cases, if the context is missing during the run, it fails at that step. Place Pick contact or Get the contact’s conversation before it when the trigger does not supply the necessary data.
Send message
Section titled “Send message”Sends text through the current conversation’s channel. There is no channel selector. The content supports variables.
Before sending, SquadOS checks:
- the contact’s opt-out status;
- whether the conversation is snoozed;
- whether the customer is waiting for a human reply;
- the 24-hour automated-message limit per contact — three by default;
- the Meta channel’s 24-hour window.
An immediately preceding Wait step may allow the send when the customer spoke last. If a safeguard blocks the message, the step is skipped rather than failed. If the contact record cannot be read during that check, the message does not go out and the step fails: without confirming the opt-out status, SquadOS does not send.
The destination number comes from the conversation; if the conversation has no channel address, SquadOS uses the phone number on the contact record.
Template outside the 24-hour window
Section titled “Template outside the 24-hour window”For WhatsApp Official, select an approved template under Approved template, in the Outside the 24h window band, as the fallback when the window is closed. The field can be searched by name or by the template’s text, and each item shows its language. The first option, Send nothing, is the default. Without a valid fallback, the message is not sent. Instagram Direct does not use this fallback template.
Sending is at-most-once: the action does not automatically retry a request that may already have been delivered.
Send email
Section titled “Send email”Sends an email only to the flow’s contact, at the address on their record. Configure:
- Subject, with or without variables;
- Format: plain text or HTML;
- Body, written in the step or taken from previously prepared content.
The step requires a contact in context and the email module enabled with a default sender. If the contact has no email on record, the step fails. The contact does not need to be subscribed to a list, but opt-out, the per-contact automated-message limit, and email suppressions still apply. The Meta 24-hour window does not apply to email.
Send campaign
Section titled “Send campaign”It sits in the palette’s Campaigns section. It sends to every eligible recipient in the configured list every time the step runs — not only to the current contact in the flow.
Configure:
- List;
- Tags to filter the list, if needed — a contact must have at least one selected tag; leave empty to send to the entire list;
- Subject, with or without variables;
- Format: plain text or HTML;
- Body, written in the step or taken from previously prepared content.
Sending also requires the email module, a default sender, credits, and at least one subscribed recipient with an email identity and no email suppression.
Send campaign only publishes when the only trigger that reaches it is Schedule. Any other trigger — including Audience, which opens one run per contact — runs many times and would make the entire list receive the email on every run; publishing refuses the flow and points to the offending trigger. To write to whoever entered the flow, use Send email.
Automation message opt-out and per-contact limits do not replace the email suppression list. Keep email suppressions accurate before sending.
Apply tags
Section titled “Apply tags”Apply tag changes the contact’s tags. Tag conversation changes only the current support conversation’s tags. In either step, select tags to add and tags to remove.
Adding or removing a contact tag can also fire another automation’s Contact tag trigger. Plan an exit condition so the flows do not form a loop.
Pick contact
Section titled “Pick contact”Use this action when the trigger does not identify a person or when the flow must continue with another contact. You can:
- select a fixed contact;
- search for an exact WhatsApp, Telegram, Instagram, API, web, or email identity.
The action does not create contacts. It fails if no exact match is found. When it selects a different contact, the previous conversation leaves context; add Get the contact’s conversation next if later actions require a conversation.
For Instagram, the technical identity value may be the Meta-provided sender ID rather than the visible username. Confirm the value in the contact record.
Get the contact’s conversation
Section titled “Get the contact’s conversation”Select an inbox. The step finds the contact’s latest conversation in that inbox. If the latest conversation is archived, or none exists, it creates a new one.
When it has to create the conversation and a Send message step comes after it, on any branch, the step first checks whether the message will have somewhere to go. It fails, without creating anything, in two cases:
- the contact has no phone number on record, and that is what the message would be sent to on the channel;
- the inbox belongs to a channel, such as Telegram or Instagram, where the automation only reaches someone who has messaged before — pick a WhatsApp inbox.
Reusing an existing conversation, or creating one to assign, tag, or hand off without sending a message, skips this check.
You may provide a title. Without one, the system uses a default value. A new conversation starts with the inbox’s active agent; if no agent is available, it starts in human support.
After this step, later actions receive the conversation and its variables in context.
Save to contact
Section titled “Save to contact”Writes text to a custom field in the contact’s metadata. For example: field city, value {{analysis.city}}.
It does not change native fields such as name, email, or channel identity. The field name is fixed; the value supports variables. This action commonly follows Analyze with AI.
Add private note
Section titled “Add private note”Writes a private note for the team on the conversation. Fill in Note; the text supports variables from earlier steps and is cut at 4,000 characters.
- The note appears in the conversation timeline with the Only your team can see lock and the name of the automation that wrote it.
- The customer never receives the note, and it is not part of the conversation messages the AI agent reads.
- If the text is empty after variables are filled in, the step fails.
- The step requires a conversation in context; publishing refuses the flow when no path delivers one.
In later steps, {{<step id>.note_id}} and {{<step id>.length}} return the note’s identifier and the length of the saved text.
Assign, Hand off to agent, and Resolve
Section titled “Assign, Hand off to agent, and Resolve”- Assign selects a responsible team member. Selecting Nobody returns the conversation to the queue.
- Hand off to agent activates AI, changes the associated agent, removes the human assignment, and reopens a resolved conversation. The action does not generate an immediate reply. The Instruction for the agent field is not shown to the customer.
- Resolve resolves the conversation. In Return to AI, the system keeps that mode only when an eligible agent exists; otherwise, it preserves human support. See Conversation states.
Call webhook
Section titled “Call webhook”Makes a request to an external system. Configure:
- URL, using
httporhttpsand supporting variables; - Method: GET, POST, PUT, PATCH, or DELETE — POST is the default;
- Headers as key/value pairs;
- Request body, with variable support.
Paste cURL
Section titled “Paste cURL”If the target system documents the call as a curl command, use Paste cURL, above the fields. SquadOS reads the command in your browser, previews what will be filled in, and sets the URL, method, headers, and body. If the step already has values, it asks for confirmation before replacing all of them.
- The method comes from
-X/--request; without it, a command with a body becomes POST and one without a body becomes GET. - Headers come from
-H; the body from-d,--data,--data-raw,--data-binary, or--json;-u user:passwordbecomes anAuthorization: Basicheader. - Options with no equivalent in the step — cookies, proxy, certificates, timeouts,
-F/--form(multipart), and options such as-s,-k, or-L— are ignored and listed in the preview. - Shell variables such as
$TOKENcome in as literal text. Replace them with the value or with a SquadOS variable.
Body as Fields or JSON
Section titled “Body as Fields or JSON”The body has two modes:
- Fields (the default in new steps) — each row is a key/value pair, and SquadOS builds the JSON with correct quoting and escaping. A value that is only a variable, such as
{{analysis.age}}, keeps its type: number, true/false, object, list, or null; a variable with no value becomesnull. Any other value, including a typed number, is sent as text. - JSON — you write the entire body. Variables are inserted as text, so quoting is up to you.
Switching modes converts the content. JSON with nested levels does not convert back to Fields. Headers and body are stored in the step’s configuration as text; use a narrowly scoped credential that can be revoked.
Test call
Section titled “Test call”The Test call band in the step panel makes a real request, from the SquadOS server, for this step only and with the configuration currently in the panel — without saving the automation or running the other steps. Choose the test conversation, contact, or payload according to the trigger; they fill {{contact.*}}, {{conversation.*}}, and {{trigger.payload.*}}, but variables from earlier steps stay empty.
The result shows the HTTP status (or No response), the time, the response formatted when it is JSON, and, under Request sent, the method, URL, headers, and body exactly as they went out. The latest test of each step is kept, with sensitive header values (such as Authorization, API keys, and tokens) masked, and serves as the sample for the variable picker.
Use the response in later steps
Section titled “Use the response in later steps”After the webhook, later steps can use:
{{<step id>.status}}— the HTTP code;{{<step id>.body}}— the response text, up to 4,000 characters;{{<step id>.json.field}}— a field from the JSON response; use a dot to go down levels and for list indexes, such as{{n5.json.items.0.name}}.
json is only filled when the response declares a JSON Content-Type and fits in 16 KB. After a successful Test call, the variable picker lists the fields of the real response with the values received.
Safety and limits
Section titled “Safety and limits”For safety, SquadOS resolves the address before calling it and blocks local, private, reserved, and own-project destinations. Redirects are not followed. The call times out after 15 seconds, treats only 2xx responses as successful, and records at most 4,000 response characters; on an error response, the step fails, but the status and response are recorded. There is no automatic retry. The Content-Type: application/json header is sent by default and can be overridden under Headers; on GET, no body is sent.
Never put secrets directly in documentation text or shared examples. Prefer a narrowly scoped credential that can be revoked.
What testing proves
Section titled “What testing proves”In test mode, most write or delivery actions are simulated so they do not change data or contact customers. The result describes what would happen, but it does not prove external delivery.
Important limits:
- Send message does not send and does not traverse every production safeguard.
- Send email and Send campaign do not send, materialize recipients, or validate body, sender, credits, or suppressions.
- Add private note does not write the note; the result shows the text that would be written.
- Pick contact performs the lookup, but the matched contact does not continue to later test steps.
- Get the contact’s conversation propagates an existing conversation; a conversation that would be created does not continue to the next test step.
- Call webhook is the exception: the request is actually made, both in the automation test and in Test call. Point it at a test destination or make sure the external system tolerates the call.
After validating the logic, monitor a controlled real run in Runs and tests.
End is not an action
Section titled “End is not an action”End is in the Rules tab. It only ends that branch of the flow. See Rules: condition, wait, and end.
Checklist before publishing
Section titled “Checklist before publishing”- Does the trigger provide the contact and conversation required by the actions?
- Does an action replace the contact and invalidate the previous conversation?
- Does Send campaign start only from Schedule, and does email to a single person use Send email?
- Are the email list, sender, and suppressions correct?
- Does the WhatsApp template cover sends outside the 24-hour window?
- Could contact tags start another flow and form a loop?
- Does the webhook target an allowed public destination and handle duplicate calls outside SquadOS?
- Did you validate a controlled real run in addition to the simulation?