Skip to main content
People search, enrichment, and outreach. Get started with the clodo API
Outreach API

Sequences

Create, review, and run personalized email sequences with automatic follow-ups.

#Use case

Create, review, and run personalized email sequences with automatic follow-ups.

#Endpoint

POST https://api-public.clodo.ai/api/public/v1/sequences/

Send your API key in the x-api-key header. See Authentication.

This endpoint returns 202 Accepted. Poll the resource to follow its progress. Outreach does not require a webhook signing secret.

#Pricing

See Credits & Pricing for credit costs and charging behavior.

#Errors

For error responses and retry guidance, see Handling Errors.

Enroll up to 100 contacts in a multi-step drafted sequence — every step is drafted up front, you review the whole thing, and one approve sets it running. 2 credits per drafted email (e.g. 5 contacts × 3 steps = 30 credits), reserved when you create the sequence and settled when drafting finishes; failed drafts are released.

Sequences require a connected Google or Microsoft sending account (reply detection drives stop-on-reply; SMTP can't support it — you'll get a 409).

#How follow-ups behave

  • Step timing is automatic: follow-ups go out 3 / 7 / 14 days after the previous step actually sends (tunable per-account in the web app), at a sensible hour, skipping weekends.
  • A step only sends after the prior step has sent.
  • A reply or bounce stops everything: remaining steps for that contact are cancelled automatically.
  • Each enrolled contact becomes a lead in your workspace, and the whole sequence is visible on your Emails page in the app.

#Lifecycle

POST /sequences/              -> 202  status: drafting
GET  /sequences/{id}/         -> poll until status: pending_review
PATCH /emails/{em_id}/        -> optional per-step edits (steps are regular em_ emails)
POST /sequences/{id}/approve/ -> status: active (steps queued on cadence)
POST /sequences/{id}/contacts/-> enroll more contacts, any time after drafting settles
GET  /sequences/{id}/replies/ -> inbound replies as they arrive
Sequence status Meaning
drafting Steps are being written (~30–60s per email).
pending_review All drafts ready — waiting for approve.
active Approved and running.
completed / cancelled / failed Terminal. Per-enrollment statuses carry the detail (replied, bounced, ...).

#Create — POST /sequences/

Field Type Notes
contacts array, 1–100 Same contact shape as Outreach Emails (email + first_name required; company_name, professional_url, context optional).
instructions string, ≤2000 What the sequence should pitch/do.
name string, ≤200 Optional label for your own bookkeeping (e.g. "LegalHR1"). Echoed on every sequence payload; not shown to recipients.
steps int, 1–4 Total steps including the intro. Default 3.
from_account string Optional ea_... sender pin.
reply_to string Optional Reply-To address for every step. Must be one of your connected email accounts (any of them — not just the sender); anything else is a 400. Replies routed there are still detected: the sequence stops on reply as usual.
cc string[] Optional CC recipients, max 3, applied to every step. Visible to everyone on the thread. A reply from a CC'd address does not stop the sequence — only the lead's reply does.
bcc string[] Optional BCC recipients, max 3, applied to every step (e.g. a CRM logging address). Never visible to the other recipients.
review string "required" (default) or "none" (auto-approve after drafting).
curl -X POST https://api-public.clodo.ai/api/public/v1/sequences/ \
  -H "x-api-key: ck_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "contacts": [
      {"email": "x@acme.com",   "first_name": "Xola",  "company_name": "Acme"},
      {"email": "y@globex.com", "first_name": "Yuri",  "company_name": "Globex"},
      {"email": "z@initech.io", "first_name": "Zadie", "company_name": "Initech"}
    ],
    "instructions": "3-step sequence pitching our outbound API. Casual tone, developer audience.",
    "name": "DevRel-Q3-1",
    "steps": 3
  }'

Returns 202:

{
  "id": "sq_9c41d2...",
  "name": "DevRel-Q3-1",
  "status": "drafting",
  "created_at": "2026-07-13T00:00:00Z",
  "contacts_accepted": 3,
  "contacts_rejected": [],
  "total_emails": 9,
  "drafted": 0
}

Partial accept: ineligible contacts are skipped, not fatal — contacts_rejected lists each with a reason (already_in_automation — the person is in another active sequence; not_enriched; recently_contacted; invalid_contact). Only if no contact is eligible does the call fail.

#Add contacts — POST /sequences/{id}/contacts/

Enroll 1–100 additional contacts into an existing sequence — including after it's active. New enrollments inherit the sequence's original instructions and step plan and start at step 0 on their own cadence clock; existing enrollments are untouched. 2 credits per newly drafted email, reserved on the call and settled when the new drafts finish.

Field Type Notes
contacts array, 1–100 Same contact shape as create.
from_account string Optional ea_... sender pin for the new enrollments.

New enrollments also inherit the sequence's reply_to, cc, and bcc (if set at create time) — appended contacts' threads behave the same as everyone else's. | review | string | "required" (default) or "none". "none" auto-approves every pending enrollment in the sequence once drafting finishes. |

curl -X POST https://api-public.clodo.ai/api/public/v1/sequences/sq_9c41d2.../contacts/ \
  -H "x-api-key: ck_live_..." \
  -H "Content-Type: application/json" \
  -d '{"contacts": [{"email": "w@umbrella.co", "first_name": "Wren", "company_name": "Umbrella"}]}'

Returns 202:

{
  "id": "sq_9c41d2...",
  "name": "DevRel-Q3-1",
  "status": "drafting",
  "contacts_accepted": 1,
  "contacts_rejected": [],
  "emails_added": 3
}

Behavior notes:

  • The sequence's top-level status returns to drafting while the new enrollments draft, then settles back (per-enrollment statuses stay accurate throughout — poll the detail).
  • With review: "required", call approve/ again after drafting — it activates only the newly drafted enrollments (already-active ones are skipped). Per-step copy edits via PATCH /emails/{em_id}/ work on the new drafts before approval, same as at create.
  • 409 while a drafting pass is running (retry shortly), and for cancelled / failed sequences (create a new one instead).
  • Contacts already enrolled in this sequence are rejected with reason already_enrolled; the other rejection reasons match create.
  • A sequence holds at most 500 enrollments.

#Read — GET /sequences/{id}/

{
  "id": "sq_9c41d2...",
  "name": "DevRel-Q3-1",
  "status": "pending_review",
  "instructions": "3-step sequence pitching...",
  "total_emails": 9,
  "drafted": 9,
  "failed": 0,
  "enrollments": [
    {
      "contact": {"email": "x@acme.com", "name": "Xola"},
      "status": "pending_review",
      "emails": [
        {"id": "em_...", "step": 0, "status": "pending_review", "subject": "...", "body_plain": "...", ...},
        {"id": "em_...", "step": 1, ...},
        {"id": "em_...", "step": 2, ...}
      ]
    }
  ]
}

Each step is a regular outreach email — the em_ ids work against GET /emails/{id}/ and PATCH /emails/{id}/ for per-step edits. Edits work before approving AND after — any step whose status is pending_review or queued accepts a PATCH, so you can rewrite upcoming follow-ups on a live sequence. Every other status (drafting, sending, sent, failed, cancelled) returns 409.

GET /sequences/ lists your sequences (paginated, no enrollments — fetch the detail).

#Approve — POST /sequences/{id}/approve/

Approves every drafted enrollment: step 0 queues at its computed best time, later steps follow the cadence after each prior send. 409 while still drafting.

#Cancel — POST /sequences/{id}/cancel/

Cancels all non-terminal enrollments and their unsent steps. Already-sent steps are unaffected. No refunds for drafted emails (the drafting work happened).

#Replies — GET /sequences/{id}/replies/

{
  "replies": [
    {
      "email_id": "em_...",
      "from_email": "x@acme.com",
      "from_name": "Xola",
      "subject": "Re: your outbound API",
      "body_plain": "Interested — got time Thursday?",
      "classification": "reply",
      "received_at": "2026-07-16T14:03:00Z"
    }
  ]
}

classification is reply, bounce, or auto_reply. The corresponding enrollment will already show status replied/bounced with its remaining steps cancelled.

#Status mapping

Outcome HTTP Charged
Sequence created 202 2 credits × drafts that succeed
All contacts rejected 400 invalid_request 0
No Google/Microsoft account 409 conflict 0
Approve while drafting 409 conflict 0
Unknown sq_ id 404 not_found 0

#See also

clodo Docs