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:
default | A contact with no record is | Use it for |
|---|---|---|
opt_out | Eligible | Mail recipients expect, with an unsubscribe link |
opt_in | Not eligible | Newsletters 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
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 customersAn 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
Send an email campaign
Create an email campaign for an audience, schedule a run, and inspect or control delivery progress.
Author and send newsletter issues (Preview)
Preview · Available to selected organizations. Write a topic's newsletter issues in Markdown, preview email and web output, and pin a saved issue to a campaign.