Resources

Automations

.md

Create, manage and trigger automation flows via API.

Automations are step-based workflows (send email, wait, branch by condition, tag, change ticket status, webhook, etc.) that run over contacts enrolled when a trigger fires. An automation is composed of an automation record plus N steps (nodes) connected by next_step_id. The engine processes each enrollment step by step.

Plan required
Automations are only available on Pro and Enterprise plans. Creating or activating returns 403 on other plans.

Create flow

To build an automation from scratch: 1) POST /v1/automations creates the automation + trigger node. 2) PUT /v1/automations/{id}/steps defines the flow. 3) POST /v1/automations/{id}/activate validates and activates. 4) POST /v1/automations/{id}/enrollments enrolls contacts by email.

List automationsGET/v1/automations

Permission: read. Returns automations ordered by updated_at (most recent first). Query params: status (draft, active, paused, archived), limit (default 20, max 100) and offset.

response.json
JSON
01{
02 "data": [
03 {
04 "id": "aut_xxxxxxxx",
05 "name": "Onboarding API",
06 "description": null,
07 "status": "active",
08 "trigger_type": "api",
09 "trigger_config": {},
10 "total_enrolled": 120,
11 "total_completed": 98,
12 "total_exited": 4,
13 "created_at": "2026-07-01T10:00:00Z",
14 "updated_at": "2026-07-02T09:00:00Z",
15 "activated_at": "2026-07-02T09:00:00Z"
16 }
17 ],
18 "pagination": { "limit": 20, "offset": 0, "total": 1 }
19}
UTF-8 · LF · JSONtickos.dev

Get automation detailsGET/v1/automations/{id}

Permission: read. Returns { data } with the automation (id, name, description, status, trigger_type, trigger_config, max_entries_per_contact, totals and dates) and all its steps (id, step_type, config, next_step_id, sort_order, created_at, updated_at) ordered by sort_order. Branch targets (true/false/timeout) are stored inside each step config. For editor positions use GET /v1/automations/{id}/steps.

Create an automationPOST/v1/automations

Permission: write. Plan: Pro/Enterprise. Creates the automation and its initial trigger node. Define the flow afterwards with PUT /v1/automations/{id}/steps.

FieldTypeRequiredDescription
namestringNoAutomation name (default "Untitled Automation").
descriptionstringNoFree-text description.
trigger_typestringNoapi (default) or any value from the trigger types table below.
trigger_configobjectNoTrigger filters, e.g. { "group_id": "grp_x" }. Also stored as the config of the initial trigger step.
from_emailstringNoSender for send_email steps (domain must be verified).
from_namestringNoSender display name.
reply_tostringNoReply-to address.
exit_conditionsobjectNo{ on_unsubscribe, on_bounce, on_reply, on_booking }.
max_entries_per_contactnumberNoDefault 1, max 10.
terminal
Bash
01curl -X POST https://api.tickos.dev/v1/automations \
02 -H 'Authorization: Bearer sk_your_api_key' \
03 -H 'Content-Type: application/json' \
04 -d '{ "name": "Onboarding API", "trigger_type": "api" }'
UTF-8 · LF · Bashtickos.dev

Returns 201 with { data: automation }. New automations start in draft status.

Trigger types

trigger_type must be one of the values below. trigger_config fields are filters: when omitted, the trigger matches any value. Send only these values; an unsupported trigger_type is rejected by the database.

trigger_typeFires whentrigger_config
apiPOST /v1/automations/trigger is called for this automation.{}
manualA contact is enrolled manually from the dashboard.{}
contact_added_to_groupA contact is added to a contact group.{ "group_id": "grp_x" } (required to activate)
tag_assignedA tag is assigned to a ticket.{ "tag_id": "tag_x" }
ticket_status_changedA ticket status changes.{ "from_status": "open", "to_status": "resolved" }
ticket_priority_changedA ticket priority changes.{ "from_priority": "low", "to_priority": "high" }
ticket_agent_assignedAn agent is assigned to a ticket.{ "agent_id": "usr-x" } (optional)
email_eventEmail open/click events (handled internally).{}
broadcast_completedA broadcast finishes sending.{ "campaign_id": "cmp_x" } (optional)
booking_scheduledA meeting is booked (Scheduling).{ "calendar_id": "cal_x" } (optional)
booking_cancelledA meeting is cancelled.{ "calendar_id": "cal_x", "cancelled_by": "invitee" | "host" } (optional)
booking_rescheduledThe invitee reschedules a meeting.{ "calendar_id": "cal_x" } (optional)

Update an automationPATCH/v1/automations/{id}

Permission: write. Whitelisted fields: name, description, trigger_type, trigger_config, exit_conditions, max_entries_per_contact, from_email, from_name, reply_to. Other fields are ignored (use activate/pause to change status). Returns { data: automation }; 404 if the automation does not exist.

Delete an automationDELETE/v1/automations/{id}

Permission: write. Deletes the automation and returns { "success": true }. Deleting an active automation returns 400; pause it first. 404 if it does not exist.

List stepsGET/v1/automations/{id}/steps

Permission: read. Returns { data: steps[] } ordered by sort_order. Each step includes id, step_type, config, next_step_id, position_x, position_y, sort_order, created_at and updated_at. Useful to read the current flow before sending a PUT.

Save the flowPUT/v1/automations/{id}/steps

Permission: write. Bulk save of the whole flow. Body: { steps: [...], edges: [...] } (both arrays are required, otherwise 400). Each step has id, step_type, config, position_x, position_y and sort_order. Steps whose id starts with "new_" (or is unknown) are created; existing ids are updated; existing steps not included in the array are deleted. Then all next_step_id values are cleared and rebuilt from edges ({ source, target, label? }), which may reference either "new_" ids or real ids.

Edge labelWritten to the source step
(none)next_step_id
"yes" or "true"config.true_step_id
"no" or "false"config.false_step_id
"timeout"config.timeout_step_id AND config.false_step_id
Timeout edges
A "timeout" edge also sets false_step_id, so it acts as the "no" branch too. Do not combine a "timeout" edge with a separate "no"/"false" edge on the same step: whichever edge comes last in the array wins for false_step_id. Branch targets are kept in config across saves; to remove a branch, send the step config without it.

Response: { data: steps[], id_map }. data is the saved flow (same shape as GET /v1/automations/{id}/steps) and id_map maps each temporary "new_" id to the real step id, e.g. { "new_email": "ast_xxx" }. A step that fails to be created is skipped and is missing from id_map.

step_typeconfig fieldsDescription
send_emailtemplate_id OR (subject + content_html), content_text, preview_text, email_format, variablesSends an email. Requires from_email or a verified domain.
wait_delayduration, unit (minutes|hours|days|weeks)Waits a fixed time.
wait_conditioncondition_type, condition_config, timeout_duration, timeout_unit, timeout_step_idWaits until a condition is met or timeout expires.
conditioncondition_type, condition_config, true_step_id, false_step_idIf/Else branch.
add_tagtag_idAdds the tag to the contact most recent open or pending ticket. Does nothing if there is none.
remove_tagtag_idRemoves the tag from all of the contact tickets, whatever their status.
add_to_group / remove_from_groupgroup_idOn contact groups.
assign_agentagent_idAssigns the agent to all of the contact open and pending tickets.
change_ticket_statusstatusChanges the status of all of the contact open and pending tickets.
change_ticket_prioritypriorityChanges the priority of all of the contact open and pending tickets.
webhookurl, method, headers, custom_payloadCalls an external URL (10s timeout). method: GET, POST (default), PUT or PATCH. See Webhook step payload below.
start_automationautomation_idEnrolls the contact into another automation.
exit{}Ends the enrollment.

condition_type values: email_opened, email_clicked, email_replied, has_tag (needs condition_config.tag_id), in_group (needs condition_config.group_id). A condition step with a false result and no false_step_id ends the flow for that contact. A wait_condition that times out goes to timeout_step_id if set, otherwise to next_step_id.

terminal
Bash
01curl -X PUT https://api.tickos.dev/v1/automations/aut_xxx/steps \
02 -H 'Authorization: Bearer sk_your_api_key' \
03 -H 'Content-Type: application/json' \
04 -d '{
05 "steps": [
06 { "id": "new_trigger", "step_type": "trigger", "config": {}, "position_x": 250, "position_y": 50, "sort_order": 0 },
07 { "id": "new_email", "step_type": "send_email", "config": { "template_id": "tpl_x" }, "position_x": 250, "position_y": 180, "sort_order": 1 },
08 { "id": "new_exit", "step_type": "exit", "config": {}, "position_x": 250, "position_y": 310, "sort_order": 2 }
09 ],
10 "edges": [
11 { "source": "new_trigger", "target": "new_email" },
12 { "source": "new_email", "target": "new_exit" }
13 ]
14 }'
UTF-8 · LF · Bashtickos.dev

Webhook step payload

The webhook step sends the contact plus the full data of the event that triggered the automation. IDs stored when the contact was enrolled (ticket, tag, group, broadcast, agent, booking) are resolved to objects at send time. With POST, PUT or PATCH the payload is sent as a JSON body (Content-Type: application/json, User-Agent: TickOS-Automation/1.0, plus your custom headers). custom_payload keys are merged into the top level of the body and override fields with the same name.

response.json
JSON
01{
02 "event": "automation.step.webhook",
03 "trigger_event": "ticket.status_changed",
04 "timestamp": "2026-10-03T12:00:00Z",
05 "automation": { "id": "aut_x", "name": "Resolved follow-up", "trigger_type": "ticket_status_changed" },
06 "enrollment": { "id": "aen_x", "enrolled_at": "2026-10-03T11:59:58Z" },
07 "customer": { "id": "cst-x", "email": "ana@example.com", "name": "Ana", "phone": null, "metadata": {}, "source": "email", "created_at": "2026-09-01T08:00:00Z" },
08 "trigger": {
09 "type": "ticket_status_changed",
10 "event": "ticket.status_changed",
11 "ticket": {
12 "id": "tck-x", "subject": "Cannot log in", "status": "resolved", "priority": "normal", "channel": "email",
13 "metadata": {}, "created_at": "...", "updated_at": "...", "closed_at": null, "last_message_at": "...",
14 "url": "https://app.tickos.dev/app/tickets/tck-x",
15 "inbox": { "id": "inb_x", "name": "Support", "email_address": "support@acme.com", "channel_type": "email" },
16 "assigned_to": { "id": "usr-x", "full_name": "John Smith", "email": "john@acme.com" },
17 "tags": [{ "id": "tag_x", "name": "billing", "color": "#22c55e" }]
18 },
19 "changes": { "status": { "from": "open", "to": "resolved" } }
20 },
21 "automation_id": "aut_x",
22 "automation_name": "Resolved follow-up",
23 "enrollment_id": "aen_x",
24 "step_id": "ast_x"
25}
UTF-8 · LF · JSONtickos.dev

automation_id, automation_name, enrollment_id and step_id are flat legacy fields kept for compatibility. trigger.type is the trigger that enrolled the contact: contacts enrolled through POST /v1/automations/{id}/enrollments report type api (trigger_event contact.enrolled_via_api) whatever the automation trigger_type. Any extra keys from the enrollment context (for example the context you send when enrolling via API) are returned in trigger.context.

Triggertrigger_eventData in trigger
contact_added_to_groupcontact.added_to_groupgroup (id, name, description, color, total_contacts)
tag_assignedticket.tag_assignedtag (id, name, color) + ticket
ticket_status_changedticket.status_changedticket + changes.status { from, to }
ticket_priority_changedticket.priority_changedticket + changes.priority { from, to }
ticket_agent_assignedticket.agent_assignedticket + agent (id, full_name, email)
email_eventemail.eventData available in the enrollment context.
broadcast_completedbroadcast.completedbroadcast (id, name, subject, status, from_email, from_name, send metrics, sending dates)
booking_scheduledbooking.scheduledbooking + calendar
booking_rescheduledbooking.rescheduledbooking (with previous_start_date, previous_start_time, previous_booking_id) + calendar
booking_cancelledbooking.cancelledbooking (with cancel_reason, cancelled_by) + calendar + cancelled_by
manualcontact.enrolled_manuallyenrolled_by (id, full_name, email) + context
apicontact.enrolled_via_apicontext with the data you sent

booking includes id, status, calendar_id, invitee_name, invitee_email, invitee_phone, invitee_timezone, answers, start_at, end_at, ticket_id, rescheduled_from, cancel_reason, cancelled_by, cancelled_at, the dates already formatted in the invitee timezone (start_date, start_time, end_time, timezone, duration, location), manage_url, add_to_calendar_url and meeting_url. calendar includes id, name, slug, description, duration_minutes, location_type, location_value and timezone.

With method GET there is no body: only the main identifiers are sent as query params (event, trigger_event, trigger_type, automation_id, enrollment_id, customer_id, customer_email, customer_name, ticket_id, tag_id, tag_name, group_id, broadcast_id, booking_id, booking_start_at; empty values are omitted). Use POST to receive the full payload.

Delivery
The webhook is sent once, with a 10 second timeout and no retries. A failed request or a non-2xx response is logged but does not stop the enrollment: the flow continues to the next step.

ActivatePOST/v1/automations/{id}/activate

Permission: write. Plan: Pro/Enterprise. Sets status to active and returns { data: automation }. Contacts whose enrollments were paused by POST /v1/automations/{id}/pause resume.

StatusWhen
400The automation is already active.
400It has no trigger step or no action step.
400trigger_type is contact_added_to_group and trigger_config.group_id is missing.
400The trigger step is not connected to a next step.
400There is a send_email step, no from_email, and no verified sending domain.
403The plan does not include automations.
404Automation not found.

PausePOST/v1/automations/{id}/pause

Permission: write. Pauses the automation and its active enrollments (they resume when the automation is activated again). Returns { data: automation }. 400 if the automation is not active; 404 if it does not exist.

Enroll contacts by emailPOST/v1/automations/{id}/enrollments

Permission: write. Plan: Pro/Enterprise. Enrolls one or more contacts into the automation, whatever its trigger type. The automation must be active. Contacts are looked up by email and created if they do not exist. Each contact starts at the first step right away. The re-enrollment limit (max_entries_per_contact) is respected.

FieldTypeRequiredDescription
emailstringYes*Contact email. *Use either email or contacts.
namestringNoContact name (used when creating the contact).
contextobjectNoExtra data stored on the enrollment (trigger_context).
contactsarrayYes*Bulk mode: up to 100 items, each an email string or { email, name?, context? }. A top-level context applies to items without their own.
terminal
Bash
01curl https://api.tickos.dev/v1/automations/aut_xxxxxxxx/enrollments \
02 -H 'Authorization: Bearer sk_your_api_key' \
03 -H 'Content-Type: application/json' \
04 -d '{ "email": "jane@example.com", "name": "Jane", "context": { "plan": "pro" } }'
UTF-8 · LF · Bashtickos.dev

Single mode returns 201 with { data: { automation_id, email, status: "enrolled", customer_id, enrollment_id, customer_created } }. If the contact already has an active enrollment or reached the re-enrollment limit it returns 409 with { error, data: { automation_id, email, status, customer_id, error } }, where status is already_enrolled or max_entries_reached. An invalid email returns 400. The automation must be active (otherwise 400) and have at least one step after the trigger.

Bulk mode always returns 200 with a summary (total, enrolled, skipped, failed) and one result per email. skipped counts already_enrolled and max_entries_reached; failed counts invalid_email and error.

response.json
JSON
01{
02 "data": {
03 "automation_id": "aut_xxxxxxxx",
04 "summary": { "total": 2, "enrolled": 1, "skipped": 1, "failed": 0 },
05 "results": [
06 { "email": "jane@example.com", "status": "enrolled", "customer_id": "cst-xxx", "enrollment_id": "aen_xxx", "customer_created": false },
07 { "email": "john@example.com", "status": "already_enrolled", "customer_id": "cst-yyy" }
08 ]
09 }
10}
UTF-8 · LF · JSONtickos.dev

List enrollmentsGET/v1/automations/{id}/enrollments

Permission: read. Lists contacts enrolled in the automation, newest first (by enrolled_at). Query params: status (active, completed, exited, paused, failed; other values return 400), email (exact match, case-insensitive), limit (default 20, max 100) and offset.

response.json
JSON
01{
02 "data": [
03 {
04 "id": "aen_xxx",
05 "status": "active",
06 "current_step_id": "ast_xxx",
07 "enrolled_at": "2026-07-10T12:00:00Z",
08 "completed_at": null,
09 "exited_at": null,
10 "exit_reason": null,
11 "next_execute_at": "2026-07-11T12:00:00Z",
12 "trigger_context": { "trigger_type": "api", "plan": "pro" },
13 "customer": { "id": "cst-xxx", "email": "jane@example.com", "name": "Jane" }
14 }
15 ],
16 "count": 1,
17 "limit": 20,
18 "offset": 0
19}
UTF-8 · LF · JSONtickos.dev

Trigger / enroll a contactPOST/v1/automations/trigger

Permission: write. Fires the api trigger for the automation referenced by automation_id. The customer is looked up by exact email match and created (source api) if not found. The endpoint only checks that the automation exists (404) and is active (400); the contact is enrolled only if the automation trigger_type is api. Prefer POST /v1/automations/{id}/enrollments: it works with any trigger type, supports bulk and reports the result per contact.

Always 200
Once the customer is resolved, the response is always 200 with { "success": true, "customer_id": "cst-xxx", "automation_id": "aut_xxx" }, even when nothing was enrolled: if the automation trigger_type is not api, the contact already has an active enrollment, it reached max_entries_per_contact, or the trigger is not connected to a next step, the call is skipped silently. Check GET /v1/automations/{id}/enrollments?email=... to confirm the enrollment.
FieldTypeRequiredDescription
automation_idstringYesID of the automation to trigger (must be active).
customer_emailstringYesCustomer email. Creates the customer if not found.
customer_namestringNoCustomer name (used when creating).
contextobjectNoAdditional context data passed to the enrollment.
terminal
Bash
01curl https://api.tickos.dev/v1/automations/trigger \
02 -H 'Authorization: Bearer sk_your_api_key' \
03 -H 'Content-Type: application/json' \
04 -d '{
05 "automation_id": "aut_xxxxxxxx",
06 "customer_email": "jane@example.com",
07 "context": { "plan": "pro", "action": "upgrade" }
08 }'
UTF-8 · LF · Bashtickos.dev