Skip to main content

Unsubscribe

MethodPathAuth
GET/api/v1/unsubscribe?c=<contactId>&t=<token>Signed token — no API key
POST/api/v1/unsubscribe?c=<contactId>&t=<token>Signed token — no API key

This is the endpoint behind the unsubscribe link in every marketing email. You do not normally call it: Mailroom injects the fully-formed URL as unsubscribeUrl when it renders a marketing send, and sets List-Unsubscribe / List-Unsubscribe-Post: List-Unsubscribe=One-Click headers so mail clients can offer their own unsubscribe button.

Authentication

No API key. The t token is an HMAC over the contact id, signed with the project's own unsubscribe secret, so a token minted for one project never verifies under another and one cannot be forged from a contact id alone.

An invalid, mismatched, or missing token is rejected — there is no way to enumerate or opt out arbitrary contacts through this route.

GET — the human path

Returns a small HTML page, not JSON:

  • 200 — a confirmation page; the contact's status is now unsubscribed.
  • 400 — an "invalid link" page when the token does not verify.

POST — the one-click path

For List-Unsubscribe-Post. Bare status, no body:

  • 200 — unsubscribed.
  • 400 — token did not verify.

Mail clients call this without user interaction, so it must stay idempotent: unsubscribing an already-unsubscribed contact is still a 200.

Side effects

The contact's status flips to unsubscribed and a contact.unsubscribed webhook fires — subscribe to it to mirror the opt-out into your own store. Only the first transition dispatches the event; repeat hits are no-ops.

Programmatic opt-out

To unsubscribe someone from your own code, use the authenticated endpoint instead: POST /api/v1/contacts/:id/unsubscribe.

Because the URL carries ?c=<contactId>, links already in inboxes keep working only if the contact id stays the same. Pass your existing uuid as id when you upsert — see contacts.