---
title: "Update a topic"
method: PATCH
path: "/api/v1/topics/{id}"
tags: ["Topics"]
---

# Update a topic

`PATCH /api/v1/topics/{id}`

## Request

- Base URL: `https://api.sendly.now`
- URL: `https://api.sendly.now/api/v1/topics/{id}`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `better-auth.session_token`
  - OAuth 2 (authorization code, authorization URL `https://app.sendly.now/api/auth/oauth2/authorize`, token URL `https://app.sendly.now/api/auth/oauth2/token`, scopes: `topics:write`)

Rename it, re-describe it, flip `default_opt_in`, or archive it.

`key` is absent from the body on purpose. Every stored preference and every integration refers to a topic by its key, so changing one would orphan them silently. There is no DELETE for the same reason — `archived: true` removes the topic from the preference centre and from new sends while every opt-out recorded against it survives.

Requires the `topics:write` scope — Create and edit topics, and change what your contacts are subscribed to — this decides who your campaigns reach.

## Path parameters

- `id` string, uuid, required: Resource id.

## Request body

- TopicUpdateV1: `key` is deliberately absent. It is the name every stored preference and every integration refers to, so changing it would silently orphan them.
  - `archived` boolean
  - `default_opt_in` boolean
  - `description` string, nullable
  - `name` string

## Response `200`

The updated topic

- TopicV1: One subject this project mails about.
  - `archived` boolean, required: Hidden from the preference centre and from new sends, WITHOUT discarding the opt-outs recorded against it. There is no delete for the same reason: deleting a topic would delete the choices people made about it.
  - `created_at` string, date-time, required
  - `default_opt_in` boolean, required: What a contact with NO answer counts as. True is the honest default for a topic added after a list already exists: those contacts consented to hear from you, and inventing an opt-out they never asked for would silence mail they expect. False means the topic must be opted INTO, and absence means `not asked` rather than `no`.
  - `description` string, nullable, required
  - `id` string, required
  - `key` string, required: Stable, URL-safe, unique within the project. This is what a preference form and an API caller name the topic by, so it must survive a rename of `name` — which is the whole reason it exists beside one.
  - `name` string, required
  - `subscribed_count` integer, required: Contacts who explicitly said yes. Excludes those covered only by `default_opt_in`.
  - `unsubscribed_count` integer, required

## Other responses

- `401`: `invalid_api_key` or `invalid_session` — missing or invalid credentials.
- `403`: `scope_missing`, `project_access_denied`, or `project_disabled`.
- `404`: `resource_not_found` — no topic with this id belongs to the authenticated project.
- `422`: `validation_error` — query, path, or body parameters did not match the schema.
- `429`: `rate_limited` — see `Retry-After` and the `RateLimit` headers.
- `500`: `internal_error`.

---

[API](https://skmtc.dev/sendly/apis/sendly-api.md) · [All operations](https://skmtc.dev/sendly/apis/sendly-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sendly/apis/sendly-api/revisions/ce7cfb7f7c7e?raw)
