Actions
Actions automate your workspace. When something happens - a form submission, a task completion, a message - actions can automatically respond with notifications, task switches, webhooks, and more.
The Building Blocks
Every action has three parts:
- Trigger - What event fires this action?
- Conditions - Should it run this time? (optional CEL expressions)
- Action - What should happen?
Creating an Action
- Go to Actions
- Click New Action
- Configure your trigger, conditions, and action
Triggers
Pick what event fires your action:
| Category | Events |
|---|---|
| Task | Entered, Exited |
| Data Record | Created, Updated |
| Member | Joined, Left, Updated |
| Chat | Message sent (by user or assistant) |
Action Types
| Action | What It Does |
|---|---|
| Assistant Message | AI sends a message. Voice note: Continue immediately is supported for text chats and voice agents that use local/composable TTS. For native realtime model audio, Gravity Rail treats the message as Wait for reply until native audio playback can be safely ordered with the follow-up response. |
| Member Notify | Send notification to team |
| Switch Task | Move to a different workflow step |
| Member Label | Add or remove labels |
| Send Webhook | POST to an external URL |
| Form Field Update | Modify form data. String values support Jinja template syntax (e.g. {{ current_date }}, {{ member.name }}). Supports targeting specific collection records via updateType. |
| Send Email | Send a templated email to the member |
| Send SMS | Send a templated SMS message, optionally starting a conversation |
| Send WhatsApp | Send a templated WhatsApp message to a member (workspace must have WhatsApp enabled) |
| Phone Call | Initiate an AI-powered phone call using a workflow |
| Play Audio | Play a pre-recorded audio file on a phone call (disclosures, voicemail drops) |
| Upsert Member | Create or update a member from incoming webhook data. Configure behavior (upsert, create_only, update_only) and skip flags (skipIfExists, skipIfNotFound) |
| Validate Message | Check an AI-generated message against a CEL condition before it is sent. Set condition (CEL expression that flags a policy violation) and reason (message shown when the condition is met). Fires in response_validation mode — the message is blocked if the condition evaluates to true. |
| Conclude Assignment | End the current assignment with a specified outcome |
| Enroll in Journey | Enroll the member in a journey program |
| Create Chat | Open a new chat for a member on a specified channel and workflow. Deprecated — only configurable via API or app-connections integrations, not the rule builder UI. Prefer Send SMS, Phone Call, or Send Email for new rules. |
| Continue Chat | Legacy action — no longer available for new rules. Handled automatically by the Scheduled Callbacks feature. |
| Pause Chat | Pause an active chat so a human agent can respond |
| Summarize Chat | Generate an AI summary of a chat conversation |
Conditions with CEL
Use CEL (Common Expression Language) to control when actions fire.
Quick Reference
# Check labels
"vip" in member.labels
# Check form data (single-record forms)
member.data.profile.age >= 18
# Check collection data
member.collections.patient_visits.count > 0
member.collections.patient_visits.latest.data.diagnosis == "Allergies"
# Search recent collection records
member.collections.lab_results.recent.exists(r, r.data.status == "critical")
# Check time
datetime.hour >= 9 && datetime.hour < 17
# Check channel
chat.channel == "phone-sms"
# Combine conditions
"premium" in member.labels && is_business_hours
Available Variables
| Variable | What It Contains |
|---|---|
member | Name, email, phone, date of birth, age, labels, form data |
member.collections | Collection form data: count, latest record, recent records |
task | Current task info |
chat | Channel, paused state, test flag |
phone_number | Workspace phone number a voice/SMS chat arrived on (number, name, brand_name) |
message | The message content (for message events) |
datetime | Current time in workspace timezone |
is_business_hours | Whether it's within business hours |
Common Patterns
VIP customers only:
"vip" in member.labels
During business hours:
is_business_hours
Specific channel:
chat.channel == "phone-voice"
Form field check:
member.data.contact.preferred_language == "spanish"
Delayed Actions
Schedule actions for later:
- Enable the delay option
- Set hours, minutes, or seconds
- Action runs after the delay
Great for:
- Follow-up messages after 24 hours
- Escalation if no response in 2 hours
- Scheduled reminders
Webhooks
Send data to external systems when events happen.
Setup
- Create an action with Send Webhook type
- Enter your HTTPS endpoint URL
- Choose which events trigger it
Payload Format
Every payload includes event, eventId, eventRule, and workspace at the top level, plus a slim member block when a member is in context:
{
"event": "call:completed",
"eventId": "9d3f1c88-6b0a-45e2-9f41-c7a2b5d80e63",
"eventRule": {
"id": 88,
"uuid": "a1b2…",
"name": "Export call to data warehouse"
},
"workspace": {
"uuid": "9e8d…",
"name": "Acme Health"
},
"member": {
"id": 123,
"accountUuid": "c4d5…",
"email": "jane@example.com"
}
}
eventId is a unique UUID generated when the event fires. Retries of the same delivery carry the same eventId — use it as your idempotency/dedup key.
The exact fields vary by event family (data record events carry a full member block and a dataRecord block; member events carry the full member profile). See Webhooks for all three payload shapes.
Payloads are signed with HMAC-SHA256 via the X-Webhook-Signature header when a signing secret is configured on the rule. See Webhooks — Security for verification examples.
Common Automations
Route VIPs to priority queue:
- Trigger: Task Entered
- Condition:
"vip" in member.labels - Action: Switch Task to "Priority Support"
Notify team on escalation:
- Trigger: Task Entered (Escalation task)
- Action: Member Notify to support team
Update CRM on signup:
- Trigger: Member Joined
- Action: Send Webhook to CRM API
Follow up after call:
- Trigger: Task Exited (Call task)
- Action: Switch Task to "Follow Up" (24h delay)
Tips
- Start simple - One trigger, one action. Add complexity later.
- Test with conditions - Use
chat.is_testto run test conversations. - Order matters - Drag actions to control execution order.
- Check logs - Failed actions show error details.
Chat Actions
Create Chat
Deprecated. This action is not available in the rule builder UI and can only be configured via the API or app-connections integrations. For new rules, use Send SMS, Phone Call, or Send Email instead — they cover the most common outbound-initiation use cases and are fully supported in the UI.
Opens a new chat for a member on a specified channel and workflow. Use this to proactively start a conversation — for example, sending an outbound SMS after a form submission or initiating a voice callback.
Configuration options:
- Channel —
sms,email,phone-voice,web, etc. - Workflow — The workflow the new chat will follow
- Initial message — Optional opening message sent when the chat is created
Pause Chat
Pauses an active chat so a human agent can take over. The chat is marked as paused in the inbox and will not receive automated replies until a human responds or the pause is cleared.
Configuration options:
- Message — Optional message sent to the member when the chat is paused (e.g., "A team member will be with you shortly")
Summarize Chat
Generates an AI summary of the chat conversation and stores it on the chat record. Useful for hand-off notes, post-call documentation, or building context before a follow-up workflow step.
Configuration options:
- Summarization prompt — Custom instructions for what to focus on in the summary
- Chat model — Override the default AI model used for summarization
Form Field Update
Updates a field on a member's form record. You can set a static value or use Jinja template syntax to write a dynamic value — for example, the current date or a piece of the member's profile.
Template variables
String values are rendered through the same template engine used by the Send Email and Send SMS actions. Common variables include:
| Variable | Example output |
|---|---|
{{ current_date }} | 2026-06-13 |
{{ member.name }} | Jane Smith |
{{ member.email }} | jane@example.com |
{{ member.phone }} | +15551234567 |
Non-string values (numbers, booleans) and plain strings without {{ or {% are written verbatim and unchanged.
Example: stamp a date field on status change
A common use-case is recording when a status field last changed by writing the current date to a companion status_changed_at date field.
Setup:
| Setting | Value |
|---|---|
| Trigger | Data Record Updated |
| Condition | has(changes.status) |
| Action | Form Field Update |
| Field | status_changed_at |
| Value | {{ current_date }} |
Every time the status field changes, the action stamps status_changed_at with today's date.
Collection record selection
When the target field belongs to a collection data type (a form that can have multiple records per member), you must specify which record to update using the updateType parameter.
updateType | Behaviour |
|---|---|
newest | Update the most recently created record. Creates a new record if none exist. |
oldest | Update the least recently created record. Creates a new record if none exist. |
create | Always insert a new record regardless of existing records. |
trigger | Update the exact record that fired the rule. Only valid on Data Record Created and Data Record Updated triggers. |
matchField | Update the record whose matchField field equals the template-rendered matchValue. See details below. |
selector | Evaluate a CEL expression (the candidate record is bound as record) and update the first match. See details below. |
matchField mode
Use this mode to look up a record by a specific field value — for example, updating a lab result record that has a matching accession number.
| Option | Description |
|---|---|
matchField | The field name on the collection record to match against |
matchValue | The value to match (supports Jinja template syntax) |
tiebreaker | newest (default) or oldest — which record wins when multiple records match |
createIfMissing | true to insert a new record when no match is found; false (default) to fail with an audit event instead |
A matchValue that renders to an empty string fails with a empty_match_value audit rather than matching records with an unset field.
selector mode
Use this mode for complex match logic. The action evaluates your CEL expression against each of the member's records and updates the first one that matches.
| Option | Description |
|---|---|
selector | CEL expression evaluated per record; the candidate record is bound as record |
tiebreaker | newest (default) or oldest — scan order and which record wins on first match |
createIfMissing | Same semantics as matchField mode |
Example: update the most recent open appointment record:
record.data.status == "open"
Breaking change note
Before this template support was added (prior to 2026-06-12), a field value containing {{ or {% was written to the form verbatim. Those values are now rendered as templates. If an existing rule stored a literal string that happened to contain double braces, that string will now be interpreted as a template expression. Review any Form Field Update rules that use such values and update them if needed.
Related
- CEL Expressions — Write conditions to control when actions fire
- Webhooks — Send encrypted data to external systems
- Workflows — Build the conversation flows that actions automate