Skip to content

Campaigns

A series of emails that people join on their own: steps, waits, branches, send windows and going live.

View as Markdown

A campaign is an audience plus a series of emails, called steps. People who match the audience of a live campaign join it on their own and get its steps, in order, with waits between them.

Steps and waits

  • Each step has a delay_days: the wait before it is sent, counted from the step before (or from joining, for the first step). Up to 365 days.
  • A campaign has up to 50 steps.
  • Each step’s content is HTML, markdown or a copy of a template; see Email content.
  • A step’s id never changes. Edit, move or remove steps one at a time with the step routes; people already waiting keep their place, and stats stay with the step.

Add a step after another one, at the start, or first on a branch path:

Add a step
curl -X POST "https://sendocado.com/api/v1/campaigns/$CAMPAIGN_ID/steps" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "insert_after": {
    "type": "if_true",
    "step_id": "j8gxbenyjqwx4hh5344tfjgvq4k7bn7x"
  },
  "delay_days": 0,
  "subject": "Three things Acme does that you might miss",
  "preview_text": null,
  "html": null,
  "markdown": "Hi {{ first_name | there }}, here are three shortcuts.",
  "template_id": null
}'

Branches

After a step, a campaign can wait and then take one of two paths:

  • opened_step or clicked_step: did the person open, or click a link in, a step on their path?
  • has_tag: does the contact have a tag now?
  • attribute_equals: does the contact have this attribute value now?

The step’s next shows what follows it: another step, the end, or a branch with an if_true_step_id and an if_false_step_id (null ends that path). Set a branch with the step update:

Branch on whether the welcome email was opened
curl -X PUT "https://sendocado.com/api/v1/campaign-steps/$STEP_ID" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "delay_days": 0,
  "subject": "Welcome to Acme, {{ first_name | there }}",
  "preview_text": "The one thing to do first.",
  "html": "<p>Hi {{ first_name | there }},</p>\n<p>Thanks for trying Acme. Start with the <a href=\"https://acme.example/setup\">two-minute setup</a>.</p>\n",
  "markdown": null,
  "template_id": null,
  "branch": {
    "delay_days": 3,
    "condition": {
      "type": "opened_step",
      "step_id": "j8gxbenyjqwx4hh5344tfjgvq4k7bn7x"
    }
  }
}'

Send windows and time zones

send_window limits sending to some days and hours, for example weekdays from 9 to 17. Days run from 0 (Sunday) to 6. Hours use the contact’s time zone when it has one, else the campaign’s. An email due outside the window waits for the next window start. Sendocado looks for due emails every 60 seconds.

Status

From To What happens
draft live Readiness checks pass, then every matching contact joins
live paused Nothing more is sent; people keep their place
paused live Sending picks up where it stopped

Change the status with an update to the campaign. Any other move is 409 invalid_status_transition. Edits to a live campaign apply at once.

Readiness

A campaign can go live when it has at least one step with a subject and content, the project has a working sender (with a verified domain, for providers that use one), and the project has a postal address. Ask what is missing before you try:

Check readiness
curl "https://sendocado.com/api/v1/campaigns/$CAMPAIGN_ID/readiness" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY"
Response
{
  "problems": ["domain_not_verified"]
}

Enrollments

A person in a campaign is an enrollment. A contact joins each campaign at most once. An enrollment ends with one of these exit reasons:

Exit reason Why
completed The last step was sent
replied The person replied and the campaign stops on reply (is_stopped_on_reply)
unsubscribed The person unsubscribed
blocked The address bounced, reported spam or was blocked
filter_mismatch The contact no longer matches the audience
send_failed An email to them could not be sent
step_removed The step they waited for was removed and nothing followed it
contact_deleted The contact was deleted
removed You took them out

Automatic replies, like out-of-office messages, never end an enrollment.

Drafting with AI

Describe who should get what, and Claude writes the audience and the emails. Drafting runs in the background: create a draft, then read it until it is ready. A ready draft’s result is the body of a campaign create, without the schedule. Drafts have their own limit of 10 an hour.

Draft a campaign
curl -X POST "https://sendocado.com/api/v1/projects/$PROJECT_ID/campaign-drafts" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "prompt": "Welcome new trial users over their first week and get them to finish setup."
}'

Stats

Campaign stats count each email on the UTC day it was sent, so the rates for a range are that range’s emails and what they led to. Stats come per step, per day and per link. See the Campaigns reference for every route.