# Sequences

## Use case

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

## Endpoint

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

Send your API key in the `x-api-key` header. See [Authentication](https://docs.clodo.ai/api-reference/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](https://docs.clodo.ai/guides/credits-and-pricing) for credit costs and charging behavior.

## Errors

For error responses and retry guidance, see [Handling Errors](https://docs.clodo.ai/api-reference/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](https://docs.clodo.ai/api-reference/endpoint/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). |

```bash
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`:

```json
{
  "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. |

```bash
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`:

```json
{
  "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}/`

```json
{
  "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/`

```json
{
  "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

- [Outreach Emails](https://docs.clodo.ai/api-reference/endpoint/outreach-emails) — the one-off flow and per-step edit semantics
- [Sending Accounts](https://docs.clodo.ai/api-reference/endpoint/sending-accounts)
- [Credit Semantics](https://docs.clodo.ai/guides/credits-and-pricing)
