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

# Notifications

> What Custos tells people about, and how email opt-in works.

Notifications are in-app messages for the signed-in user, with optional email delivery. They exist
for events someone has to *act* on, not as a feed of everything that happens — that is what the
[audit trails](/custos/custos/guides/auditing) are for.

## Kinds

| Kind | Raised when | Goes to |
| - | - | - |
| `credential.permission_requested` | a member asks for access to a gated credential | active admins |
| `credential.permission_resolved` | that request is approved or rejected | the requester |
| `group.membership_changed` | a member's group permissions change | the member |
| `invitation.accepted` | someone accepts an invitation | active admins |
| `user.suspension_credential_review` | a suspension or removal raises a rotation review | active admins, except whoever triggered it |

The last exclusion is deliberate: the admin who just suspended someone does not need telling they
did it.

`group.membership_changed` is emitted once per permission change, from the before/after state of
`PUT /groups/{id}/members/{userID}/permissions`. That endpoint exists partly for this — reconciling
permissions in one call rather than several `/grants` calls avoids a burst of notifications for what
is really one decision.

## Reading them

```bash theme={null}
curl -sS "$CUSTOS_URL/notifications" -H "Authorization: Bearer $TOKEN"
curl -sS "$CUSTOS_URL/notifications/unread-count" -H "Authorization: Bearer $TOKEN"
```

Mark one read with `PATCH /notifications/{id}` and `{"read": true}`, or clear the lot with
`POST /notifications/read-all`. Only `true` is accepted — there is no marking something unread.

Each notification carries an optional `resource` (kind and id) and `action` (label and href), so a
client can link straight to the thing needing attention.

## Email

Email is per-user opt-in through the profile:

```bash theme={null}
curl -sS -X PATCH "$CUSTOS_URL/me" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email_notifications": true}'
```

Sends go through the outbox and are processed per recipient, so a retry re-sends only the deliveries
that actually failed rather than spamming everyone who already received it.

Email needs `RESEND_API_KEY` and `CUSTOS_EMAIL_FROM`. Without them the control plane falls back to
logging, which is what makes development workable — see
[Configuration](/custos/custos/control-plane/configuration).

## Retention

Notifications and their email delivery records are purged after **30 days**, swept daily. Audit
trails are not affected; notifications are a to-do list, not the record.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.