> For the complete documentation index, see [llms.txt](https://boundaryai.gitbook.io/boundaryai-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://boundaryai.gitbook.io/boundaryai-docs/api-and-webhooks/sending-invites.md).

# Sending invites (email API)

The invite API turns BAI Analytics into the sending arm of your own workflow: your system decides *who* to email and *when* (a closed support ticket, a delivered order, a finished onboarding), and BAI Analytics renders the invite template, sends it, tracks delivery, and joins the responses back to your contacts.

The split of responsibilities is deliberate: **design lives in the dashboard, triggering lives in the API.** The email template, branding, and survey are built in the platform where they can be previewed and reviewed; the API only supplies recipients and variables. That keeps a compromised key from ever rewriting what your customers receive.

***

### Prerequisites

Three things must be true before the first send works. All three are inspectable via `GET /sources/{id}/invite-template`, so wire that into your setup checks:

1. **A `send`-scoped API key.** Emailing humans is scoped separately from pushing data; `all` includes it. See [Authentication & API keys](/boundaryai-docs/api-and-webhooks/authentication.md).
2. **Your organisation's own verified sending domain.** API sends never use the shared BAI Analytics sender: automated volume rides your sending reputation, not ours. Until the domain is verified you'll get a `SENDER_DOMAIN_NOT_VERIFIED` error. Set it up under **Organisation details → Email sending** ([Settings](/boundaryai-docs/account-and-administration/updating-settings.md#email-sending)).
3. **An invite template on the source**, designed in the platform. The template check returns its available **variable keys** (say, `first_name`, `order_id`) so your integration can validate its data before sending.

***

### The sending flow

#### 1. Check readiness

```bash
curl https://app.boundary-ai.com/api/input/sources/9021/invite-template \
  -H "Authorization: Bearer $BAI_API_KEY"
```

Returns whether a template is `configured`, its `variables`, `sender_domain_verified`, `sending_enabled`, and the day's quota usage.

#### 2. Preview with `dry_run`

```bash
curl -X POST https://app.boundary-ai.com/api/input/sources/9021/invites \
  -H "Authorization: Bearer $BAI_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "dry_run": true,
    "recipients": [
      {"email": "jamie@example.com",
       "external_id": "cus_310",
       "variables": {"first_name": "Jamie", "order_id": "A-1042"}}
    ]
  }'
```

A dry run renders the emails and reports what *would* happen (`would_accept`, `skipped_already_invited`, `invalid`) without sending anything. Use it in CI and before the first production send.

#### 3. Send

Drop `dry_run` and the same call returns **202** with a `distribution_id`. Sends are transactional: 1 to 1,000 recipients per call, each with their own `variables`. Recipients are appended to the source's respondent list, and each gets a **personalized survey link**, which is what joins their eventual response back to your `external_id`.

#### 4. Track the outcome

* `GET /invites/{distribution_id}`: overall status and counts (`total`, `sent`, `failed`, `skipped`).
* `GET /invites/{distribution_id}/recipients`: per-recipient outcomes, including the ESP **delivery status** (delivered, bounced, deferred) and your `external_id`, cursor-paginated.
* Or skip polling: the **`invites.completed`** webhook fires when the distribution finishes, and **`invite.bounced`** fires per bounce or spam complaint. See [Webhooks](/boundaryai-docs/api-and-webhooks/webhooks.md).

***

### Safety rails (and why they're not optional)

These exist so an automation bug can't damage your sending reputation, which takes months to rebuild:

* **Never-double-invite by default.** An address that already received this survey is skipped; pass `resend: true` only when you explicitly mean it.
* **Suppression is always enforced.** Unsubscribed, bounced, or complaining addresses are never emailed again, even if your system keeps sending them.
* **Rolling 24-hour quota** per organisation (default 2,000 invites; raisable on request). The template check shows today's usage, and over-quota sends fail cleanly rather than queueing.
* **Complaint-rate auto-pause.** If spam complaints spike over a 30-day window, sending pauses before mailbox providers penalise your domain.
* **1,000 recipients per call.** Batch bigger lists into multiple calls; each returns its own `distribution_id`.

***

### Joining responses back to your data

The `external_id` you set per recipient flows through the whole loop: it's on the recipient outcome rows, on the `invite.bounced` payload, and, because the survey link is personalized, on the response itself. Use the same identifier you use in [feedback pushes](/boundaryai-docs/api-and-webhooks/pushing-feedback.md) (`customer_id`) and you can connect *invited → responded → what they said* entirely in your own warehouse.
