Email types
Transactional email is part of a service the recipient uses: receipts, booking confirmations, password resets, security notices. It carries no unsubscribe link and is stopped only by a hard bounce or a manual suppression. Marketing email is everything else — newsletters, product updates, promotions. It must link to {{@unsubscribeUrl}}, and it is also stopped by spam complaints and by opt-outs.
The type lives on the template: it is set from the email-type choice at generation and confirmed in the builder's Sending section. An API send may override it per request with type; campaigns are always marketing. An API send without type uses the template's type only when you set it (or the builder set it from the email-type chip); otherwise the send is transactional, so an integration built before email types existed keeps delivering service email without a code change. Templates that predate this release carry a type guessed from their footer, shown as such in the templates list; a guess is never used for API sends until you confirm it in the builder's Sending section.
Only mark email transactional when the recipient expects it as part of a service they use. A newsletter sent as transactional reaches people who asked you to stop, which is a complaint waiting to happen.
Topics
Topics let a recipient stop one kind of marketing email and keep the rest. Every workspace starts with Newsletter, Product updates and Promotions; manage them under Audience → Topics. Each topic has a slug (newsletter, product-updates, promotions) that the API uses.
- A campaign picks one topic in its Template section, prefilled from the template's default
- A template has a default topic, used when an API send names none
- A marketing send with no topic at all is general marketing: it skips only recipients who stopped all marketing email
Sends that name an archived or unknown topic are refused with TOPIC_NOT_FOUND.
How recipients unsubscribe
- The footer link. Marketing templates must link to
{{@unsubscribeUrl}}; a campaign cannot launch without it, and API sends render it per recipient. The page shows the one topic the email was sent under and one button. After confirming it offers Undo, the workspace's other marketing topics, and “Stop all marketing emails”. It never offers transactional email and cannot resubscribe anyone. - Gmail and Yahoo one-click. Marketing sends carry
List-UnsubscribeandList-Unsubscribe-Postheaders, so the Unsubscribe button in the mail client works and is recorded the same way. - Your ESP's own unsubscribe. Unsubscribe webhooks from the provider are mapped onto the message they belong to and recorded as an opt-out.
- Your product. Call
unsubscribeContact/POST /api/v1/contacts/unsubscribefrom your own preferences page.
Each unsubscribe is recorded as an event on the message and shows on the contact's page and in the campaign's stats. Resubscribing happens only from the dashboard or the API, never through a link.
What blocks what
| Block | Transactional send | Marketing send |
|---|---|---|
| Hard bounce | blocked | blocked |
| Manual suppression | blocked | blocked |
| Spam complaint | sent | blocked |
| Opt-out from a topic | sent | blocked under that topic; other topics and general marketing are sent |
| Opt-out from all marketing | sent | blocked |
Hard bounces, complaints and manual suppressions are delivery blocks (suppressed: true on a contact; details.kind: "suppression" on a refused send). Opt-outs are marketing opt-outs (optOuts on a contact; details.kind: "opt_out"). A refused send is recorded with a Suppressed status and does not count against your quota. Temporary bounces — a full mailbox, greylisting — block nothing.
In the API and SDK
| Field | Where | Meaning |
|---|---|---|
| type | send request and response | transactional or marketing. Omitted: the template's confirmed type, else transactional. Declare it on marketing sends. The response echoes what the server resolved |
| topic | send request and response | Topic slug for a marketing send; omitted: the template's default, else general marketing. Not allowed with type: "transactional" (400) |
| RECIPIENT_SUPPRESSED | 422 from send | details.kind is suppression or opt_out, details.reason is HARD_BOUNCE, MANUAL, COMPLAINT, OPT_OUT_TOPIC (with details.topic) or OPT_OUT_ALL. The SDK exposes it as isOptedOut() and suppressionDetails() |
| optOuts | contact responses | [{ topic: slug | null, createdAt }]; null is all marketing. suppressed now means a delivery block only |
| unsubscribe / resubscribe | POST /api/v1/contacts/unsubscribe, /resubscribe | { email, topic? }; omitted topic = all marketing. SDK: unsubscribeContact, resubscribeContact |
// A receipt: delivered even to someone who unsubscribed from marketing
await client.send({
to: 'maya@example.com',
subject: 'Your receipt',
template: 'receipt',
type: 'transactional',
idempotencyKey: `receipt-${order.id}`,
variables: { total: order.total },
});
// A newsletter: skipped for anyone who stopped the newsletter or all marketing
await client.send({
to: 'maya@example.com',
subject: 'This month at Linen',
template: 'newsletter',
type: 'marketing',
topic: 'newsletter',
variables: { firstName: 'Maya' },
});Integrating
- Pass
type: 'transactional'on every system send — receipts, confirmations, resets — so a marketing unsubscribe can never stop it. Do not rely on the template's type for these; the request wins. - After upgrading, open the templates list and review every template shown as guessed: confirm its type in the builder's Sending section, and give marketing templates a default topic.
- Marketing templates need the
{{@unsubscribeUrl}}link. Variable names starting with@are reserved and rejected with400, so it cannot be overridden. - Handle
422withisOptedOut(): an opted-out marketing send is not an error to retry, and the same recipient still receives transactional email.
Some ESPs keep their own complaint list and may refuse a transactional send to someone who reported an earlier email as spam. That surfaces as a Failed message with the provider's error, not as RECIPIENT_SUPPRESSED.