Email

Manage topics and subscriptions

Group mail into topics, choose opt-in or opt-out, and record each contact's subscription per channel.

A topic is a named category of messages a contact agrees to receive, such as Product updates or Weekly digest. A campaign names one topic, and the unsubscribe link and preference page in its email act on that topic. Leaving one topic never leaves another.

Choose a default

Each topic has a default that decides what a contact with no subscription record means:

defaultA contact with no record isUse it for
opt_outEligibleMail recipients expect, with an unsubscribe link
opt_inNot eligibleNewsletters and anything people must ask to receive

A topic defaults to opt_out. Changing a topic's default does not change any explicit subscription. Every organization has one default topic, named Marketing until you rename it; make another topic the default with Set the default topic.

Subscription records

A subscription belongs to one contact, one topic, and one channel. It carries a status (subscribed or unsubscribed), a source that says how it changed, and subscribedAt and unsubscribedAt timestamps. The source is double_opt_in after an authenticated double opt-in request, link for a recipient's unsubscribe link or preference page, and the value you pass when you set it yourself.

Preview · Available to selected organizations.

A confirmed public form signup records source form, with the form key, page and referrer available in its subscription event. A confirmed signup from a subscribe wall records source wall, with the issue URL as its page. A confirmed signup from a publication's home page form or the form after an open issue records source publication, with the home page or issue URL as its page.

List topics and Get a topic return per-channel counts in subscriptions. For email, subscriptions.email.subscribed and subscriptions.email.unsubscribed count explicit subscription records with each status. A topic with no email subscription records returns zero for both counts.

These counts describe recorded preferences, not the number of eligible recipients. An opt_out topic can reach contacts without a record, including an all-contacts campaign audience, so its subscribed count can be smaller than its reach. An opt_in topic requires an explicit subscribed status. Suppression, blocking, available addresses, and audience filters still affect who receives each send.

Who a topic reaches

A contact is eligible for a topic when its subscription is subscribed, or when it has no record and the topic is opt_out. A send or campaign run also skips contacts that are suppressed, blocked, or have no address on the channel. A campaign's audience is the topic's eligible contacts that match the audience source, minus excludeTags, and the audience preview and the run count the same contacts.

Create and manage topics

topics.ts
import { createClient } from "samva";

const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });

const topic = await samva.topics.create({
  name: "Weekly digest",
  description: "One email a week with what shipped",
  default: "opt_in",
});

await samva.topics.setSubscriptions({
  id: topic.id,
  contacts: [{ email: "ada@example.com" }, { contactId: "contact_k9mvt4znw8rh7q2x" }],
  channel: "email",
  status: "subscribed",
  source: "import-2026-08",
});

const check = await samva.topics.checkSubscription({
  email: "ada@example.com",
  channel: "email",
});
console.log(check.topics);
curl -X POST https://api.samva.dev/v1/topics \
  -H "x-api-key: $SAMVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Weekly digest","default":"opt_in"}'

curl -X POST https://api.samva.dev/v1/topics/<topic-id>/subscriptions \
  -H "x-api-key: $SAMVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contacts":[{"email":"ada@example.com"}],"channel":"email","status":"subscribed"}'

Set subscriptions takes up to 1,000 contacts, each named by contactId, email, or phone, and writes the same status for all of them. An address no contact holds gets a new contact in the topic's namespace. Repeating a status is idempotent. Setting subscribed records the status you give; to collect consent from the recipient, use double opt-in.

List subscriptions pages through a topic's records and filters by channel and status. Check subscription takes exactly one of contactId, email, or phone and a channel, and returns the effective status for each topic, using the topic's default when no record exists. An address no contact holds returns contactId: null with those defaults. Update changes the name, description, default, and the sender, reply mailbox, and template a topic carries as defaults; delete refuses the default topic and any topic a scheduled or running campaign uses.

Recipient controls

Every campaign email links to a hosted preference page that shows the contact's topics and lets them subscribe or unsubscribe from each. The one-click unsubscribe link and List-Unsubscribe header change only the topic of the email. Both write subscription records with source link, and each change emits contact.unsubscribed or contact.resubscribed with the topicId, topicName, and channel.

Send under a topic

A campaign names its topic in audience.topicId; see Choose the topic. A single email names a topic with topicId, or its template does, and Samva then skips recipients who are not eligible, reporting them in skippedRecipients with reason topic-unsubscribe and the topicId. A direct send that resolves no topic carries no topic unsubscribe link and is not filtered by subscriptions.

Preview · Available to selected organizations.

A topic also owns its newsletter issues: Markdown documents you preview, save, and pin to a campaign by path and commit.

Namespaces

A topic belongs to the organization or to one namespace. A key scoped to a namespace lists and targets only that namespace's topics, and contacts created at confirmation or by setting subscriptions land in the topic's namespace. A topic's default sender, reply mailbox, and template must be in the same scope.

CLI

samva topics create --name "Weekly digest" --default opt_in
samva topics subscriptions set unsub_1234567890abcdef \
  --email ada@example.com --channel email --status subscribed
samva topics subscriptions check --email ada@example.com --channel email
samva subscriptions subscribe --email ada@example.com --topic-id unsub_1234567890abcdef \
  --from newsletter@your-domain.com
samva campaigns create --topic-id unsub_1234567890abcdef --name "Digest" \
  --subject "This week" --html "<p>Hello</p>" --include-tags customers

An authenticated MCP agent manages topics with list_topics, get_topic, create_topic, update_topic, delete_topic, set_default_topic, list_topic_subscriptions, set_topic_subscriptions, and check_topic_subscriptions, and requests confirmation with contacts_subscribe.

Related documentation

On this page