Skip to main content
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 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:
  • statusOPTED_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:
For multi-tenant workspaces, add the optional tenant_id query parameter to any of these endpoints to scope the preferences to one tenant.

Endpoints

Get a user’s preferences

GET /users/{user_id}/preferences returns every topic the user has an override for:
cURL
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:
cURL
To set the channels a user wants for a topic, include routing:
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.
cURL
The response lists what’s now set, and deleted shows any overrides that were reset because they weren’t in the request:

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.
cURL

Replace vs update

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 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

Next steps

User Preferences API

Full endpoint reference, parameters, and language snippets

Preferences Editor

Create the subscription topics your preferences reference

User Preference Logs

Review a user’s preferences and history in the dashboard

Configure preferences tutorial

A worked example in curl, Node, and Python