Developer guideAutomations
Sequences
Event-triggered email sequences in Couryo, with triggers, steps (send, wait, condition, exit), automatic exits, quiet hours and a per-contact limit. No charge per contact.
A sequence sends a series of emails to each contact that enters it: welcome on sign-up, trial reminders, abandoned cart. You build the steps as a list in the dashboard (under Sequences) or through the API, and Couryo takes care of timing, conditions and exits.
No charge per contact. Sequence emails count in the plan quota and nothing else.
Triggers#
- Event: the contact enters when an event with that name arrives (
POST /v1/eventsor the MCPtrack_eventtool). Each contact enters a sequence once. - Manual: you put the contact in from the dashboard or with
POST /v1/sequences/{id}/enrollments. A contact that finished can enter again this way. - Conversion event (optional): when it arrives (for example
order.paid), the contact leaves the sequence as converted.
Steps#
| Step | What it does |
|---|---|
send |
sends an email: a saved template (template_id + variables) or its own subject, HTML and text, made in the editor |
wait |
waits minutes, hours or days (amount, unit); with until, then waits until HH:mm in the account's or the contact's time zone (timezone: account or contact) |
condition |
checks whether the contact opened or clicked an earlier email, whether an event happened since entry, or whether an attribute equals a value; if_true and if_false continue (next), exit (exit) or jump to a later step ({ "goto": "<step id>" }) |
exit |
the contact leaves the sequence there |
In the emails you can use the contact attributes ({{name}}, also as {{contact.name}}), {{email}}, the properties of the event that started the sequence ({{event.plan}}) and {{unsubscribe_url}}.
A contact without an attribute#
Not every contact has every attribute, and that does not break the sequence:
- A variable with a fallback uses it:
Hi {{name|there}}!reads "Hi there!" for a contact withoutname. - A variable without a value and without a fallback comes out empty and the email is still sent, both with its own content and with a template. The enrollment gets a
warning(for example, "Variables without a value in step 1: name"), shown in the enrollments list of the dashboard, and the contact stays in the sequence. - Only an email that is really empty is an error: an empty subject after filling the variables, or empty content. Then the contact leaves with reason
send_failedand the explanation inlast_error. - In the builder, the dashboard warns when a step uses a variable without a fallback: "Contacts without
{{x}}get this passage empty. Use{{x|fallback}}." - Sending through the API (
POST /v1/emailswithtemplateorvariables) stays strict: a variable without a value and without a fallback is refused.
curl https://api.couryo.com/v1/sequences \
-H "Authorization: Bearer $COURYO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Onboarding",
"trigger": { "type": "event", "event": "user.signed_up" },
"conversion_event": "order.paid",
"from": "Shop <hi@example.com>",
"steps": [
{ "id": "welcome", "type": "send", "template_id": "welcome" },
{ "type": "wait", "amount": 2, "unit": "days", "until": "09:00" },
{ "type": "condition", "check": { "kind": "opened", "step_id": "welcome" }, "if_true": "next", "if_false": "exit" },
{ "type": "send", "subject": "A tip for you, {{name}}", "html": "<p>Hi {{name}}!</p>" }
]
}'The sequence starts as a draft. Activate it with POST /v1/sequences/{id}/activate: it needs a sender on a verified domain and at least one email.
Automatic exits#
The contact leaves the sequence, and no other email of it goes out, when:
- they unsubscribe (one-click link or mailbox provider request): reason
unsubscribed; - the address goes on the suppression list:
suppressed; - there is a hard bounce:
bounced, or a spam complaint:complained; - the conversion event arrives: the status becomes
converted; - the sequence is deleted (
sequence_deleted) or paused withexit_enrollments: true(sequence_paused). Paused without that option, nobody gets anything and each contact continues where it stopped when it comes back.
Other reasons: condition (the condition said exit), exit_step, removed (you took it out), contact_deleted and send_failed.
Sending rules#
- Quiet hours: by default no sequence email goes out from 21:00 to 08:00 in the account time zone; the email waits for the end of the window. Set it in
settings.quiet_hours(ornullto turn it off). - At most 1 sequence email per contact every 12 hours, across all sequences of the project. Set it in
settings.min_hours_between_emails. - Marketing stream by default, with one-click unsubscribe (
List-Unsubscribe) and an unsubscribe link added at the end of the email when the content has none. Thetransactionalstream is only for account onboarding sequences, which start from an event. - Everything goes through the same path as
POST /v1/emails: plan quota, trust levels, suppressions and the content check. If a daily limit or a pause holds the send, Couryo retries every hour for up to 3 days. - No duplicate sends: each step runs once per contact, even if the server restarts in the middle.
Plans#
| Plan | Sequences |
|---|---|
| Free | 1 active sequence, with up to 3 emails |
| Pro | same as Free; with the Automations add-on, unlimited |
| Scale and Enterprise | unlimited, included |
Drafts do not count. Activating beyond the limit returns 403 automations_limit_reached, with upgrade_options (the add-on and Scale, with prices). The add-on is turned on and off under Billing in the dashboard.
Metrics and enrollments#
GET /v1/sequences/{id}/metricsreturns, per step: sent, delivered, opens, clicks, exits and conversions.GET /v1/sequences/{id}/enrollmentslists who is in or went through the sequence (status:active,completed,exited,converted), with the current step, the next send (next_run_at), the exit reason, the last error (last_error) and thewarning, such as variables that came out empty. Filter bystatusand part of the email (q).
Endpoints#
| Method and path | Scope | What it does |
|---|---|---|
GET and POST /v1/sequences |
read / admin |
lists and creates (draft) |
GET, PATCH and DELETE /v1/sequences/{id} |
read / admin |
one sequence, update (steps replaces the whole list) and delete |
POST /v1/sequences/{id}/activate |
admin |
activates or resumes |
POST /v1/sequences/{id}/pause |
admin |
pauses; { "exit_enrollments": true } also takes everyone out |
POST /v1/sequences/{id}/duplicate |
admin |
copy as a draft |
GET /v1/sequences/{id}/metrics |
read |
metrics per step |
GET and POST /v1/sequences/{id}/enrollments |
read / send |
lists and puts a contact in by hand |
GET and DELETE /v1/sequences/{id}/enrollments/{eid} |
read / admin |
one enrollment and taking the contact out |
Through MCP: create_sequence (scope admin, creates a draft) and list_sequences (read).