Create a topic

POST /api/v1/topics Creates a topic. Answers 201 with the created topic — its id is the topicId you attach to a campaign or a sequence. A name already taken — case-insensitive, so Freelancing collides with freelancing — answers 409.

Authorization

Role in the organizationThis endpoint
OWNERAllowed
ADMINAllowed
MEMBERRefused — 403
A key carries the role its creator holds in this organization — never a role on AgentsMail itself. See API keys.

Request body

FieldTypeRequiredDescription
namestringyes1 to 50 characters
json
{"name": "freelancing"}

Example

bash
curl -X POST -H "x-api-key: $AGENTMAIL_API_KEY" -H "content-type: application/json" \
  -d '{"name":"freelancing"}' \
  "https://www.agentsmail.io/api/v1/topics"

Response

  • 201 Created — the created topic.
  • 400 Bad Request — the name is missing, empty or over 50 characters; details carries the field-level issues.
  • 401 Unauthorized — missing or invalid key, or its owner left the organization.
  • 403 Forbidden — the key's owner is a member of the organization, not an admin.
  • 409 Conflict — another topic already carries that name, case ignored. Nothing was created; list the topics and reuse the existing id.
  • 429 Too Many Requests — over 120 requests in a minute for this key.

Response fields

FieldTypeDescription
successbooleanIndicates if the operation was successful
data.idstringUnique identifier for the topic
data.namestringName of the topic, unique in the organization
data.createdAtstringISO creation date
data.updatedAtstringISO date of the last change

Example response

json
{
  "success": true,
  "data": {
    "id": "7c2e4b19",
    "name": "freelancing",
    "createdAt": "2026-08-23T10:22:05.000Z",
    "updatedAt": "2026-08-23T10:22:05.000Z"
  }
}

Agent recipe

Reuse before creating — a second call for a name already taken just costs you a 409:
js
const existing = await list('/api/v1/topics')
const topic =
  existing.data.find((t) => t.name.toLowerCase() === 'freelancing') ??
  (await post('/api/v1/topics', {name: 'freelancing'})).data

await post('/api/v1/campaigns', {
  name: 'Freelance news',
  subject: 'This month for freelancers',
  templateId,
  listId,
  topicId: topic.id,
})

Notes

  • Creating a topic changes nothing on its own. It starts mattering the day a campaign or a sequence carries it.