Skip to main content

Notifications

A Notification is one in-app message addressed to one user inside one organisation — a flagged trace receipt, a team invitation, a security event, or an admin broadcast. Notifications are per-user scoped (a user only sees their own), TTL'd at 90 days, and rendered in the dashboard's notification panel. Tested against API version v1. JWT auth on every endpoint; broadcast requires the admin or owner role.

The Notification object

FieldTypeRequiredDescription
_id / idstringyesMongoDB ObjectId
userIdstringyesThe recipient (chains never mix across users)
organizationIdstringyesTenancy scope
typeenumnosystem (default), security, invite, broadcast, alert, info
titlestringyesSubject line; max 200 characters
bodystringnoDetail; max 1,000 characters
isReadbooleannoDefault false; flips to true on read
linkstringnoDashboard URL the notification routes to on click
sentBystringnoUser ObjectId of the sender (populated on broadcasts)
createdAtstring (ISO 8601)yesDefault now; TTL: notifications older than 90 days are auto-purged
updatedAtstring (ISO 8601)noStandard timestamp

A MongoDB TTL index drops notifications 90 days after createdAt. This is operational policy, not a bug; long-term audit retention lives on the Operations Audit Log, not on this resource.

Endpoints

GET    /api/v1/notifications
DELETE /api/v1/notifications
PATCH /api/v1/notifications/read-all
PATCH /api/v1/notifications/:id/read
DELETE /api/v1/notifications/:id
POST /api/v1/notifications/broadcast

GET, DELETE-all, and the per-id endpoints are scoped to the caller's own notifications. Broadcasts require the admin or owner role.

List notifications

GET /api/v1/notifications
Query parameterDefaultDescription
page11-indexed page number
limit30Page size; capped server-side
unreadOnlytrue filters to isRead: false
curl
curl https://api.adjudon.com/api/v1/notifications \
-H "Authorization: Bearer $ADJUDON_API_KEY"
Python — only unread, first page
import os, requests
r = requests.get(
"https://api.adjudon.com/api/v1/notifications",
params={"unreadOnly": "true", "limit": 30},
headers={"Authorization": f"Bearer {os.environ['ADJUDON_API_KEY']}"},
)
data = r.json()
print(f"Unread: {data['unreadCount']}")

Response (200 OK):

{
"success": true,
"data": [/* Notification[] */],
"unreadCount": 4,
"pagination": { "page": 1, "limit": 30, "total": 47, "pages": 2 }
}

unreadCount is the total unread count for the user, independent of the current page filter. Use this for the dashboard badge. Errors: 401, 500 INTERNAL_ERROR.

Mark one as read

PATCH /api/v1/notifications/:id/read

Returns the updated notification with isRead: true. The lookup filters by (_id, userId); another user's notification returns 404, never their content. Errors: 401, 404 NOT_FOUND, 500.

Mark all as read

PATCH /api/v1/notifications/read-all

Bulk-flips every unread notification for the caller to isRead: true. Returns the count of records updated.

curl
curl -X PATCH https://api.adjudon.com/api/v1/notifications/read-all \
-H "Authorization: Bearer $ADJUDON_API_KEY"

Errors: 401, 500.

Delete one

DELETE /api/v1/notifications/:id

Hard-deletes a notification. Lookup is (_id, userId); a cross-user attempt returns 404. The TTL index already collects old notifications after 90 days, so explicit DELETE is for users who want to clear the inbox sooner.

Errors: 401, 404, 500.

Clear all

DELETE /api/v1/notifications

Hard-deletes every notification for the caller in the active organisation. Use carefully; there is no undo. Returns the count of records deleted.

curl
curl -X DELETE https://api.adjudon.com/api/v1/notifications \
-H "Authorization: Bearer $ADJUDON_API_KEY"

Errors: 401, 500.

Broadcast to the org

POST /api/v1/notifications/broadcast
Body fieldRequiredDescription
titleyesSubject line; max 200 characters
bodyyesDetail; max 1,000 characters
sendEmailnotrue also dispatches a transactional email to every recipient

Creates one Notification per active OrgMember in the caller's organisation. Each notification is stamped with type: 'broadcast' and sentBy: <caller-userId>; the dashboard link defaults to /dashboard/notifications. With sendEmail: true the same content fans out to email via the transactional-email sub-processor (Resend, EU). Admin/owner role only.

curl
curl -X POST https://api.adjudon.com/api/v1/notifications/broadcast \
-H "Authorization: Bearer $ADJUDON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Maintenance window 12 May 22:00 UTC",
"body": "Read-only mode on api.adjudon.com for 30 minutes; trace ingestion will fail open per SDK contract.",
"sendEmail": true
}'

Errors: 400 VALIDATION_ERROR (missing title or body), 401, 403 (role), 500.

Programmatic creation

The notification model is the same one Adjudon's other controllers write to internally — a flagged trace creates a notification of type alert; a team invitation creates one of type invite. The public API does not expose targeted single-notification creation (use broadcast for org-wide sends, or call the underlying controllers via the dashboard's review / invitation paths).

Common gotchas

  • 90-day auto-purge. The MongoDB TTL index drops every notification 90 days after creation. If you need an event log that survives, use the Operations Audit Log; notifications are an in-app inbox, not an audit trail.
  • Per-user scope. Every endpoint filters by userId = caller. Even an admin cannot fetch another user's notifications via this API; admin observability lives on the Operations Audit Log.
  • Idempotency on broadcast. The Idempotency-Key middleware is wired only on POST /traces; a retried broadcast call creates duplicate per-member notifications. Clients should retry defensively (catch the duplicates server-side via sentBy + createdAt proximity is not implemented today).
  • Email opt-out. The sendEmail: true path obeys per-user email-preference settings stored on the User document. A user who has unsubscribed from broadcast emails still receives the in-app notification but no email.

See also

  • Reviews API — flagged decisions surface here as type: 'alert' notifications when a reviewer is assigned
  • Webhooks Overview — the external-system equivalent: subscribe to alert.triggered to fan out to Slack / PagerDuty
  • Audit Log API — the durable event log for compliance reviewers (90-day TTL on notifications does not apply)
  • Sub-Processors — Resend (EU) is the transactional-email path for sendEmail: true
  • Error Codes — the full error taxonomy