> ## Documentation Index
> Fetch the complete documentation index at: https://www.courier.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Get and set user preferences

> Read, update, and bulk-import a user's notification preferences over Courier's API, including replacing or additively updating every topic in one request.

The User Preferences API reads and writes a user's notification preferences: which subscription topics they're opted into, and which channels they want for each. You can update one topic at a time, or set a user's entire preference set in a single request, which is the usual way to import existing subscription data when migrating to Courier.

## How user preferences work

A user's preference is an **override** on a subscription topic's default. Each topic you define in the [Preferences Editor](/platform/preferences/preferences-editor) has a default status, and a user falls back to that default until they set their own choice. When they do, Courier stores it as an override on that topic.

This shapes how the API behaves:

* **Reading** returns each topic with the user's `status` and the topic's `default_status`.
* **Writing** sets overrides. You only need to send topics where the user differs from the default.
* **Deleting** an override reverts the topic to its default.

A preference has three parts:

* **`status`** — `OPTED_IN` or `OPTED_OUT`.
* **`custom_routing`** — the channels the user wants for the topic (`email`, `sms`, `push`, `inbox`, `direct_message`, `webhook`). Set `has_custom_routing: true` to apply it.
* **`topic_id`** — the subscription topic the preference belongs to. You get topic IDs from the Preferences Editor.

## Authentication

Call the API server-side with your workspace API key as a bearer token:

```bash theme={null}
Authorization: Bearer $COURIER_API_KEY
```

For multi-tenant workspaces, add the optional `tenant_id` query parameter to any of these endpoints to scope the preferences to one tenant.

## Endpoints

| Method   | Path                                                                                                                                             | Purpose                                                      |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ |
| `GET`    | [`/users/{user_id}/preferences`](/api-reference/user-preferences/get-users-preferences)                                                          | List a user's preferences.                                   |
| `PUT`    | [`/users/{user_id}/preferences`](/api-reference/user-preferences/replace-user-preferences-in-bulk)                                               | Replace a user's entire preference set (bulk).               |
| `POST`   | [`/users/{user_id}/preferences`](/api-reference/user-preferences/update-user-preferences-in-bulk)                                                | Create or update preferences without touching others (bulk). |
| `GET`    | [`/users/{user_id}/preferences/{topic_id}`](/api-reference/user-preferences/get-user-subscription-topic)                                         | Get one topic's preference.                                  |
| `PUT`    | [`/users/{user_id}/preferences/{topic_id}`](/api-reference/user-preferences/update-or-create-user-preferences-for-a-specific-subscription-topic) | Create or update one topic's preference.                     |
| `DELETE` | [`/users/{user_id}/preferences/{topic_id}`](/api-reference/user-preferences/delete-user-subscription-topic)                                      | Reset one topic to its default.                              |

## Get a user's preferences

`GET /users/{user_id}/preferences` returns every topic the user has an override for:

```bash cURL icon=terminal wrap theme={null}
curl -X GET "https://api.courier.com/users/user-123/preferences" \
  -H "Authorization: Bearer $COURIER_API_KEY"
```

```json theme={null}
{
  "paging": { "cursor": null, "more": false },
  "items": [
    {
      "topic_id": "marketing",
      "topic_name": "Marketing",
      "status": "OPTED_OUT",
      "default_status": "OPTED_IN",
      "has_custom_routing": false,
      "custom_routing": []
    }
  ]
}
```

To read a single topic, use `GET /users/{user_id}/preferences/{topic_id}`.

## Set a single preference

`PUT /users/{user_id}/preferences/{topic_id}` creates or updates one topic. Wrap the preference in a `topic` object:

```bash cURL icon=terminal wrap theme={null}
curl -X PUT "https://api.courier.com/users/user-123/preferences/marketing" \
  -H "Authorization: Bearer $COURIER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "topic": {
      "status": "OPTED_OUT"
    }
  }'
```

To set the channels a user wants for a topic, include routing:

```json theme={null}
{
  "topic": {
    "status": "OPTED_IN",
    "has_custom_routing": true,
    "custom_routing": ["email", "push"]
  }
}
```

`DELETE /users/{user_id}/preferences/{topic_id}` removes the override and reverts the topic to its default.

## Bulk update preferences

The two bulk endpoints set many topics for a user in one request, which is how you import a user's existing subscriptions when migrating to Courier. Both take a `topics` array of the same preference objects. They differ in what happens to the topics you *don't* send.

### Replace preferences (PUT)

`PUT /users/{user_id}/preferences` makes the request body the user's **complete** override set. Topics you send are created or updated, and any existing override you leave out is reset to its default. An empty `topics` array clears every override. Validation is atomic: if one topic is invalid, the whole request fails and nothing changes.

```bash cURL icon=terminal wrap theme={null}
curl -X PUT "https://api.courier.com/users/user-123/preferences" \
  -H "Authorization: Bearer $COURIER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "topics": [
      { "topic_id": "marketing", "status": "OPTED_OUT" },
      { "topic_id": "product-updates", "status": "OPTED_IN", "has_custom_routing": true, "custom_routing": ["email"] }
    ]
  }'
```

The response lists what's now set, and `deleted` shows any overrides that were reset because they weren't in the request:

```json theme={null}
{
  "items": [
    { "topic_id": "marketing", "status": "OPTED_OUT", "has_custom_routing": false, "custom_routing": [] },
    { "topic_id": "product-updates", "status": "OPTED_IN", "has_custom_routing": true, "custom_routing": ["email"] }
  ],
  "deleted": ["billing"]
}
```

### Update preferences (POST)

`POST /users/{user_id}/preferences` is additive. It only touches the topics you send and leaves every other override untouched. It processes each topic independently: successful ones come back in `items`, and any that can't be applied are collected in `errors` with a reason.

```bash cURL icon=terminal wrap theme={null}
curl -X POST "https://api.courier.com/users/user-123/preferences" \
  -H "Authorization: Bearer $COURIER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "topics": [
      { "topic_id": "product-updates", "status": "OPTED_IN" },
      { "topic_id": "does-not-exist", "status": "OPTED_OUT" }
    ]
  }'
```

```json theme={null}
{
  "items": [
    { "topic_id": "product-updates", "status": "OPTED_IN", "has_custom_routing": false, "custom_routing": [] }
  ],
  "errors": [
    { "topic_id": "does-not-exist", "reason": "Topic does-not-exist not found" }
  ]
}
```

### Replace vs update

|                      | Replace (`PUT`)                         | Update (`POST`)                      |
| -------------------- | --------------------------------------- | ------------------------------------ |
| **Scope**            | The user's entire override set          | Only the topics you send             |
| **Omitted topics**   | Reset to their default                  | Left untouched                       |
| **Failure handling** | Atomic. One bad topic fails the request | Partial. Bad topics land in `errors` |
| **Best for**         | Syncing a user to a source of truth     | Adding or changing a few topics      |

### Importing existing preferences

Bulk replace is the usual way to bring subscription and opt-out data from another system into Courier. Define your topics in the [Preferences Editor](/platform/preferences/preferences-editor) first so you have their IDs, then send one `PUT` per user with their overrides, iterating over your user base. You only need to send topics where a user differs from the default; the rest fall back automatically.

Because replace is idempotent, you can re-run the import safely or use it to keep users in sync with your source of truth. If you'd rather not reset the topics you omit, use `POST` instead and check its `errors` array for anything that didn't apply, like a `topic_id` that doesn't exist yet.

## Field reference

| Field                | Type      | Description                                                                                            |
| -------------------- | --------- | ------------------------------------------------------------------------------------------------------ |
| `topic_id`           | string    | The subscription topic the preference applies to.                                                      |
| `status`             | string    | `OPTED_IN` or `OPTED_OUT`. (`REQUIRED` is a topic-level default set in the editor, not a user choice.) |
| `has_custom_routing` | boolean   | Whether the user picked specific channels for this topic.                                              |
| `custom_routing`     | string\[] | The channels to deliver on: `email`, `sms`, `push`, `inbox`, `direct_message`, `webhook`.              |
| `default_status`     | string    | The topic's default, returned on reads. Applies when the user has no override.                         |
| `topic_name`         | string    | The topic's display name, returned on reads.                                                           |

## Next steps

<CardGroup cols={2}>
  <Card title="User Preferences API" icon="code" href="/api-reference/user-preferences/get-users-preferences">
    Full endpoint reference, parameters, and language snippets
  </Card>

  <Card title="Preferences Editor" icon="gear" href="/platform/preferences/preferences-editor">
    Create the subscription topics your preferences reference
  </Card>

  <Card title="User Preference Logs" icon="list" href="/platform/preferences/user-preferences-logs">
    Review a user's preferences and history in the dashboard
  </Card>

  <Card title="Configure preferences tutorial" icon="book" href="/tutorials/preferences/how-to-configure-user-preferences">
    A worked example in curl, Node, and Python
  </Card>
</CardGroup>
