Audience & broadcasts

Topics

Let contacts choose which kinds of email they receive, with a default subscription per topic and a per-contact override.

A topic is a category of email, such as "Product updates" or "Weekly digest". Contacts opt in to or out of each topic separately, so someone can stop one kind of email without leaving everything. Topics belong to a project.

You can attach a topic to a broadcast and to a transactional email with topic_id.

Default subscription

Every topic has a default_subscription that applies to contacts who have not made a choice.

ValueContacts with no recorded choice
opt_inReceive the topic.
opt_outDo not receive the topic until they opt in.

A contact's own choice always wins over the default. You cannot change a topic's default after you create it, so choose it carefully. Create a new topic if you need the other behavior.

Create a topic

In the dashboard, open Audience and choose Topics. With the API, use a full_access key:

Shell
curl -X POST https://api.sooner.dev/topics \
  -H "Authorization: Bearer $SOONER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Product updates",
    "description": "New features and improvements",
    "default_subscription": "opt_in"
  }'
JSON
{ "id": "jt3b6n9q2w5e8r1y4u7i0o2p" }

name (1 to 100 characters) and default_subscription are required. description is optional, up to 500 characters, and is shown to contacts on the preferences page.

Set a contact's preference

Pass topics when you create a contact, or update the preferences later:

Shell
curl -X PATCH https://api.sooner.dev/contacts/ada@example.com/topics \
  -H "Authorization: Bearer $SOONER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[{ "id": "jt3b6n9q2w5e8r1y4u7i0o2p", "subscription": "opt_out" }]'
JSON
{ "id": "jx7r4k9m2c5v8n1b3q6w0e2t" }

The body is a JSON array. Read the current state with GET /contacts/{identifier}/topics, which returns every topic in the project with the contact's effective subscription, whether it comes from their choice or from the topic default.

Topics on broadcasts

A broadcast without a topic_id goes to every eligible contact in the segment. With a topic_id, contacts who are opted out of that topic are skipped, whether they chose that or inherited it from default_subscription: "opt_out". Contacts who are globally unsubscribed are skipped either way. See Broadcasts.

Topics on transactional email

Pass topic_id to POST /emails to make a single email respect a topic:

  • Sooner looks up the contact for the first address in to. If that contact is unsubscribed or opted out of the topic, the email is not sent. It is recorded as failed with last_event set to suppressed, and it still counts toward your quota.
  • If no contact exists for that address, the email is sent.
  • When a contact is found, the email carries List-Unsubscribe headers so mail clients can offer one-click unsubscribe. See Unsubscribes.

Emails without a topic_id ignore contact preferences entirely. Transactional email such as receipts and password resets should not use a topic.

Update and delete

PATCH /topics/{id} changes name and description. DELETE /topics/{id} deletes the topic and every contact's preference for it. Deleting happens in the background. New requests that reference a deleted topic return 404 not_found. In the dashboard, deleting a topic needs the admin role.

Where contacts change topics themselves

Every broadcast includes an unsubscribe link that opens a hosted preferences page. It lists your topics with their descriptions so contacts can choose. See Unsubscribes.

See the topics API reference for every endpoint.