Skip to content
Oct 15, 2026NewsletterRSS

Courier Tells Apps the Moment a User Unsubscribes

Courier now sends a webhook on every opt-in, opt-out or channel change, and a new API returns the full preference history with old and new values.

3 min read

CourierNotifications

Isometric illustration of a light switch flipped to off beside an envelope on a hot pink background

Courier now sends a webhook whenever a user opts in, opts out or changes which channels a notification topic uses, the company said in an Oct. 14 blog post. A new endpoint, GET /preferences/logs, returns the full history of those changes.

The webhook fires wherever the change happens

The preferences:user:updated event reaches a customer’s outbound webhook for changes made on Courier’s hosted preference page, through an unsubscribe link, in an app’s own preference center and through the API, Courier said. One event type covers opting in, opting out and editing a topic’s channels.

Each event describes one change to one topic. It carries the user ID, the topic, the new status and a previous block with the status and routing it replaced. A tenant_id appears when the change was made inside a tenant.

Courier previously gave only the current state of a user’s preferences, the company said. A team that wanted its CRM or data warehouse to learn about an unsubscribe had to poll every user and compare the results.

The history API fills in gaps

GET /preferences/logs returns every change in an environment, newest first. Callers can pass user_id for one user, add tenant_id to narrow it, or pass since to start from a date. Pages hold 25 entries by default and up to 100 with limit. The endpoint allows one request every two seconds per environment.

A webhook payload is the same entry the history API returns, with the same id. Courier’s suggested sync loads each user’s current preferences once and applies each event as it arrives. After an outage, read the log with since set to the last applied timestamp. Apply entries oldest first. Skip any id already seen.

The feature is in all seven server SDKs (Node, Python, Ruby, Go, Java, PHP and C#), the Courier CLI and the Courier MCP server, according to the post.

Resetting a topic leaves no trace

Resetting a topic to its default removes the user’s override instead of changing it, Courier said. That adds no history entry and sends no event. The rule covers DELETE /users/{user_id}/preferences/{topic_id} and the topics a bulk replace lists as deleted. Courier said developers should update their own copy when their code makes those calls.

Key takeaways

  • Every opt-in, opt-out and channel edit now produces a preferences:user:updated webhook.
  • GET /preferences/logs returns the history, newest first, with old and new values.
  • Resetting a topic through the API produces neither an event nor a log entry.
  • The log endpoint is limited to one request every two seconds per environment.

The take

We think the useful part of this release is the old value. A webhook that says “opted out” tells a team where a user stands. One that also says what the user had before tells the team what changed. Support and product staff ask about exactly that.

The gap is the reset. A sync that trusts events alone will drift the first time code resets a topic. That path stays silent. Update your own copy in the same call, or reload current state on a schedule. Then check that unsubscribes reach the mailing list within minutes. A person who opted out and still gets mail has no reason to trust the next message.