---
title: "Update a contact's topic preferences"
description: "Subscribe or unsubscribe a contact for several topics in one call: topics lists up to 50 preferences, each topic once. `PATCH /api/v1/contacts/{id}/topics`"
url: https://www.unitpost.com/docs/email/patch-contacts-id-topics
section: "Email API reference"
updated: 2026-10-13
---
# Update a contact's topic preferences

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

Subscribe or unsubscribe a contact for several topics in one call: `topics` lists up to 50 preferences, each topic once. Topics you leave out keep their current preference. The contact and every topic are checked before any preference is set, so an unknown topic is a `404` naming it and nothing changes. Each preference is then recorded as [Set a contact's topic preference](https://www.unitpost.com/docs/email/post-contacts-id-topics) records one (`topic.subscribed` / `topic.unsubscribed` only when it actually changes), and it's idempotent. The answer lists the resolved subscriptions in the order sent. Any key other than `topics`, in the body or in an item, is a `422`. Requires the `topics:write` capability.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | — |

## Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `topics` | object[] | yes | The preferences to change, up to 50, each topic once. Topics left out keep theirs. |

## Response fields (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | string | yes | — |
| `topic_id` | string | yes | — |
| `channel` | enum | yes | The topic's channel: manage it under `/email/topics` or `/sms/topics`. One of: `email`, `sms`. |
| `subscribed` | boolean | yes | — |
| `status` | enum | yes | One of: `subscribed`, `unsubscribed`. |
| `source` | enum | yes | One of: `user`, `manual`, `import`, `api`. |

## Responses

| Status | Description |
| --- | --- |
| 200 | The resolved subscriptions, in the order sent. |
| 400 | The body isn't JSON. |
| 401 | Missing or invalid API key. |
| 403 | Insufficient scope. |
| 404 | Contact not found, or the first unknown topic (nothing is set). |
| 422 | Validation error: a topic listed twice, more than 50, or an unknown key. |

## Example request

```bash
curl -X PATCH 'https://www.unitpost.com/api/v1/contacts/con_123/topics' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'User-Agent: my-app/1.0' \
  -H 'Content-Type: application/json' \
  -d '{
    "topics": [
      {
        "topic_id": "top_123",
        "subscribed": false
      },
      {
        "topic_id": "top_456",
        "subscribed": true
      }
    ]
  }'
```

## Example response (200)

```json
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "contact_topic",
      "topic_id": "topic_123",
      "channel": "email",
      "subscribed": false,
      "status": "subscribed",
      "source": "user"
    }
  ]
}
```

## Links

- HTML reference (with Node.js, Python, Ruby, PHP, Laravel, Go, Java, Rust, .NET and CLI examples): https://www.unitpost.com/docs/email/patch-contacts-id-topics
- OpenAPI 3.1 (full schemas): https://www.unitpost.com/api/v1/openapi.json
