Goal
Subscribe to or unsubscribe from changelog release announcements with a single API call. The Astro Subscribe button on /<locale>/changelog uses these endpoints under the hood; the same flow is available to any cookie-authenticated client.
Both endpoints require cookie authentication (app_token). API keys are not accepted (this is a per-user setting, not a machine-to-machine surface).
Prerequisites
- An active user session (valid
app_tokencookie) — log in via/loginfirst. - The
app_csrfcookie is set by the same login flow and is read client-side for theX-CSRF-Tokenheader.
Steps
1. Read your current subscription status
curl -s -X GET 'https://api.example.com/v1/users/me/changelog/subscribe-status' \
-H 'Cookie: app_token=<your-token>'Response (200):
{
"data": {
"type": "changelog_subscription_status",
"id": "<user_id>",
"attributes": {
"subscribed": true,
"channels": {
"in_app": true,
"email": true
}
}
}
}subscribed is true only when both channels are enabled. The two-channel coupling is intentional — the Subscribe button is a coarse all-or-nothing control. If you need per-channel control (e.g. in-app only, no email), use the granular notification preferences API instead.
2. Toggle the subscription
curl -s -X POST 'https://api.example.com/v1/users/me/changelog/subscribe' \
-H 'Cookie: app_token=<your-token>; app_csrf=<your-csrf>' \
-H 'X-CSRF-Token: <your-csrf>' \
-H 'Content-Type: application/json' \
-d '{"enabled": true}'enabled: trueenables BOTHchangelog.release_published.in_appANDchangelog.digest_monthly.emailpreferences.enabled: falsedisables BOTH.- The response shape matches the
subscribe-statusendpoint, withsubscribedreflecting the new state.
The toggle is idempotent — calling with the same value as the current state is a no-op (no audit event is emitted when nothing changes).
Errors
| Status | Code | Cause |
|---|---|---|
| 401 | unauthorized | Missing or invalid cookie. |
| 401 | csrf_missing / csrf_mismatch | POST without a valid X-CSRF-Token header. |
| 422 | invalid_enabled | Body missing enabled, or value is not a boolean. |