Appearance
Create a topic
Create a subscription category a contact can opt in and out of on its own.
http
POST /v1/topicsRequires a Full access API key — topics shape your audience, so a sending key embedded in an app cannot create them.
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The topic name, as a contact sees it on the preference page. Max 120 characters. |
description | string | null | No | One line telling a contact what they're signing up for. Max 255 characters. |
default_subscription | string | Yes | opt_in or opt_out. What a contact who has never made a choice counts as. Cannot be changed later — see below. |
visibility | string | No | public or private. Whether the topic is offered on the public preference page. Defaults to private. |
You do not supply key. It is derived from the name — see below.
Example
bash
curl -X POST 'https://api.sendgrail.com/v1/topics' \
-H "Authorization: Bearer $SENDGRAIL_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"name": "Product updates",
"description": "New features and improvements, about once a month",
"default_subscription": "opt_out",
"visibility": "public"
}'js
const res = await fetch('https://api.sendgrail.com/v1/topics', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SENDGRAIL_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Product updates',
description: 'New features and improvements, about once a month',
default_subscription: 'opt_out',
visibility: 'public',
}),
});
const topic = await res.json();Response
json
{
"object": "topic",
"id": "7b2e6f90-3c11-4a5d-9b8e-1f0c4d6a2e59"
}Fetch the full object — including the derived key — with Retrieve a topic.
A topic is not the global unsubscribe
Unsubscribing globally takes a contact out of everything you send. A topic is narrower: it is one category among several, and a contact can leave one and keep the others. Someone can drop "Product updates" and still receive "Billing alerts", because those are two topics and they made two different choices.
The two live side by side. A globally unsubscribed contact is out regardless of what their topic subscriptions say — the global switch wins. See Suppressions and Audiences.
The key is derived from the name
key is a URL-safe slug of name, made unique within your team. You never send it; SendGrail generates it.
Create a topic called "News" and you get the key news. Create a second topic called "News" and you get news-2 — the name is allowed to collide, the key is not, because the key is what identifies a topic in an unsubscribe link and a collision there would point two topics at one link.
default_subscription decides what silence means
Most contacts never touch the preference page. default_subscription is the answer for all of them:
| Mode | A contact who never chose is… |
|---|---|
opt_out | subscribed. They receive this topic until they opt out. |
opt_in | not subscribed. They receive nothing on this topic until they opt in. |
Once a contact makes an explicit choice, that choice is what counts and the default no longer applies to them.
Pick carefully: default_subscription is immutable. So is key. Sending either one to Update a topic returns 422, and the page explains why.