Description
Users need to unsubscribe from notifications both from the logged-in account
screen and from a link in an email, with no end-user login required for the
email case. This issue covers the API work to support those flows, including
unsubscribing from a single subscription and from all of a user's subscriptions.
Design constraints:
- The unsubscribe capability is keyed on the subscription ID
(notification_subscription.id, a UUID) — the value embedded in email
unsubscribe links. It is distinct from the user ID and the notification-type ID.
- The email-link flow requires no end-user authentication: the subscription
ID is the capability. The call is still made server-side by an authorized IAP
principal (the API has no open-internet endpoints).
api.announcements is delivered via Brevo, so its opt-in state lives in three
places that must stay in sync — the notification_subscription row, Brevo list
membership, and app_user.is_registered_to_receive_api_announcements.
Unsubscribing this type disables the row (never deletes it) and removes the
Brevo contact.
Implementation
API spec
- Add a
scope query parameter (one default / all) to
DELETE /v1/subscriptions/{id}.
- Clarify the
{id} path parameter across the subscription endpoints as the
subscription ID (notification_subscription.id).
API implementation
api/src/user_service/impl/subscriptions_api_impl.py
- Honor
scope=all: resolve user_id from the given subscription, then
unsubscribe the user from every type they hold — hard-delete normal types,
route api.announcements through set_announcements_optin(subscribe=False).
scope=one (default) keeps the current single-subscription behavior.
- Return
204 on success; 404 for an unknown subscription ID; 502 if Brevo
is unreachable during an announcements update.
Email templates
api.announcements campaign: build the unsubscribe link from the contact's
MDB_SUBSCRIPTION_ID attribute.
- Transactional templates (
api/src/shared/notifications/templates/*.j2): embed
the subscription ID in the unsubscribe link.
Description
Users need to unsubscribe from notifications both from the logged-in account
screen and from a link in an email, with no end-user login required for the
email case. This issue covers the API work to support those flows, including
unsubscribing from a single subscription and from all of a user's subscriptions.
Design constraints:
(
notification_subscription.id, a UUID) — the value embedded in emailunsubscribe links. It is distinct from the user ID and the notification-type ID.
ID is the capability. The call is still made server-side by an authorized IAP
principal (the API has no open-internet endpoints).
api.announcementsis delivered via Brevo, so its opt-in state lives in threeplaces that must stay in sync — the
notification_subscriptionrow, Brevo listmembership, and
app_user.is_registered_to_receive_api_announcements.Unsubscribing this type disables the row (never deletes it) and removes the
Brevo contact.
Implementation
API spec
scopequery parameter (onedefault /all) toDELETE /v1/subscriptions/{id}.{id}path parameter across the subscription endpoints as thesubscription ID (
notification_subscription.id).API implementation
api/src/user_service/impl/subscriptions_api_impl.pyscope=all: resolveuser_idfrom the given subscription, thenunsubscribe the user from every type they hold — hard-delete normal types,
route
api.announcementsthroughset_announcements_optin(subscribe=False).scope=one(default) keeps the current single-subscription behavior.204on success;404for an unknown subscription ID;502if Brevois unreachable during an announcements update.
Email templates
api.announcementscampaign: build the unsubscribe link from the contact'sMDB_SUBSCRIPTION_IDattribute.api/src/shared/notifications/templates/*.j2): embedthe subscription ID in the unsubscribe link.