> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dacard.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Unsubscribe from emails

> Public route used by the unsubscribe link in transactional and ritual emails. Token-bound; no auth.

Public route used by the unsubscribe link in transactional and ritual emails. Token-bound; no auth required. Tokens are minted per recipient on first email and persisted in `email_preferences`.

## GET (preview)

```bash theme={null}
curl -X GET "https://app.dacard.ai/api/unsubscribe?token=usb_..."
```

Returns the email address the token belongs to and the current preference state. Used by the unsubscribe landing page.

## POST (commit)

```bash theme={null}
curl -X POST https://app.dacard.ai/api/unsubscribe \
  -H "Content-Type: application/json" \
  -d '{
    "token": "usb_...",
    "categories": ["product_updates", "ritual_emails"],
    "all": false
  }'
```

| Property       | Value                               |
| -------------- | ----------------------------------- |
| **Auth**       | Not required (token is the binding) |
| **Idempotent** | Yes                                 |

## Body

| Field        | Type      | Required | Notes                                                                             |
| ------------ | --------- | -------- | --------------------------------------------------------------------------------- |
| `token`      | string    | yes      | The token from the email link.                                                    |
| `categories` | string\[] | no       | Categories to opt out of.                                                         |
| `all`        | boolean   | no       | If `true`, opts out of all marketing categories. Transactional emails still send. |

## Categories

`product_updates`, `ritual_emails`, `score_decay_reminders`, `peer_benchmark_updates`, `weekly_digest`. Transactional categories (account, billing, security) cannot be opted out of.

## Errors

| Status | Code             | When                   |
| ------ | ---------------- | ---------------------- |
| 400    | `Token required` | No token.              |
| 404    | `Token invalid`  | Bad or aged-out token. |
| 500    | `INTERNAL_ERROR` | Write failed.          |

Source: PR #368 reworked this to mint real tokens (the prior implementation substituted the literal string `placeholder`, breaking CAN-SPAM compliance).
