# Overview

Author campaigns in the console, send to an audience, and track delivery through the REST API.

Campaigns are authored in the BuildBase console: pick a template, pick an
audience, send or schedule. The API is for driving and observing that pipeline
from your backend, not for composing mail in code.

> **Note:**
  **There is no `bb.email.send()`.** The server SDK has no email namespace. To
  send one transactional message to one user, use
  [`bb.notification.send()`](/notifications/overview) — it renders the event's
  email template and respects the user's preferences. Campaigns are for
  audience-scale sends.


```typescript
// Send a campaign that was prepared in the console
const response = await fetch(
  `https://api.console.buildbase.app/api/emails/campaigns/${campaignId}/send`,
  {
    method: 'POST',
    headers: { Authorization: process.env.BUILDBASE_API_TOKEN! },
  }
);
```

> **Note:**
  Before a campaign can send, you need a verified [sending
  domain](/email/sending-domains), a sender address on that domain, and a
  template. The console blocks the send until all three exist.


## The pipeline

1. **Template** — the reusable body, with merge tags. See
   [templates and merge tags](/email/templates-and-merge-tags).
2. **Sender and domain** — who it comes from, on a verified domain.
3. **Campaign** — a template plus an audience plus a schedule.
4. **Drafts** — one queued message per recipient, materialized from the campaign.
5. **Send** — the queue delivers, and history records the outcome per recipient.

Drafts are the step that surprises people. A campaign does not send directly to
an audience: it first expands into per-recipient drafts, and the send walks
those. That is what makes partial retry possible.

## Campaign endpoints

All require an `Authorization` header — see the [admin API](/reference/admin-api)
for the token format and list-query parameters.

| Method   | Path                                      | Purpose                                       |
| -------- | ----------------------------------------- | --------------------------------------------- |
| `GET`    | `/api/emails/campaigns`                   | List campaigns, paginated                     |
| `GET`    | `/api/emails/campaigns/:id`               | One campaign                                  |
| `POST`   | `/api/emails/campaigns`                   | Create                                        |
| `PATCH`  | `/api/emails/campaigns/:id`               | Update                                        |
| `POST`   | `/api/emails/campaigns/:id/create-drafts` | Expand the audience into per-recipient drafts |
| `POST`   | `/api/emails/campaigns/:id/send`          | Send the campaign                             |
| `POST`   | `/api/emails/campaigns/:id/retry-failed`  | Re-queue only the recipients that failed      |
| `DELETE` | `/api/emails/campaigns/:id`               | Delete                                        |

`retry-failed` re-queues failures without touching recipients who already
received the message, so it is safe to call more than once after a partial
delivery failure. It returns the counts it acted on:

```json
{
  "message": "Retried 12 failed emails, 3 skipped",
  "retried": 12,
  "skipped": 3
}
```

## Campaign status

A campaign is always in one of five states, and the action endpoints are
guarded by it:

| Status     | Meaning                      |
| ---------- | ---------------------------- |
| `draft`    | Editable, not yet dispatched |
| `sending`  | Dispatch in progress         |
| `sent`     | Dispatch finished            |
| `failed`   | Dispatch failed              |
| `archived` | Retired                      |

| Endpoint         | Allowed from        | Rejected with                                            |
| ---------------- | ------------------- | -------------------------------------------------------- |
| `/create-drafts` | `draft` only        | `400` — `Cannot create drafts for campaign in "X" state` |
| `/send`          | `draft` or `failed` | `400` — `Cannot send campaign in "X" state`              |

`/send` accepting `failed` is what makes a whole-campaign retry possible after
a dispatch collapses. Use `/retry-failed` instead when the campaign partly
succeeded — re-sending from `failed` state is coarser.

Both rejections use the `{ error: true, message, path: "status" }` shape.

## Delivery history

Every send writes a history row per recipient with a status, and tracking
records opens and clicks when the template includes the tracking pixel. Both are
exposed as chart endpoints the console renders, and as ordinary list endpoints
you can page through.

Delivery is asynchronous. `/send` returns `{ "message": "Campaign dispatch
started" }` as soon as the work is queued — that is not a delivery
confirmation. Poll the history rows for per-recipient outcomes.

Recipients are resolved from the campaign's audience list and expanded in
batches of 500, so a large list produces drafts progressively rather than in
one pass.

## Unsubscribes

Two merge tags render opt-out links. Both resolve at send time to a page on
your **application URL** — not the API — carrying a per-recipient token, so the
link identifies the recipient without a login:

| Merge tag               | Resolves to                                  |
| ----------------------- | -------------------------------------------- |
| `{{unsubscribe}}`       | `<your-app>/unsubscribe-email-group?token=…` |
| `{{manage-preference}}` | `<your-app>/manage-email-preference?token=…` |

The preference center is backed by its own token-validated API under
`/api/manage-email-preference`, which is what the page calls to read and update
preferences. You do not call it directly.

Every audience campaign should carry at least one of these tags.

## Providers

BuildBase sends through Google, Mailgun, and custom SMTP.
Domain verification is Mailgun-backed, so a verified sending domain requires
Mailgun regardless of which provider delivers a given sender.

## Next Steps

- [Templates and merge tags](/email/templates-and-merge-tags) — compose the body.
- [Sending domains](/email/sending-domains) — verify a domain before the first send.
- [Notifications](/notifications/overview) — one-off transactional mail.
