Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.manticscore.com/llms.txt

Use this file to discover all available pages before exploring further.

The toast endpoints power realtime in-app notifications. The flow is:
  1. Exchange a Clerk session for a short-lived WebSocket token.
  2. Open a WebSocket to the realtime gateway with that token.
  3. Subscribe to one or more topics (always within your own user namespace).
  4. (Optional) Register an APNS token so undelivered toasts fall back to a push notification.
Internal services publish toasts directly inside the API process. External services use the publish endpoint with a shared bearer secret.

Bootstrap a WebSocket token

Mint a short-lived WebSocket token bound to a (user, device) pair. Clients call this with their Clerk session, then open a WebSocket to the URL returned in the response. Rate limit: 30 requests per minute.
POST /v1/sessions/toast-bootstrap
clerk_jwt
string
Optional. Informational mirror of the Clerk JWT — the Authorization header is the source of truth and is verified by the auth middleware. Provided so callers can document the binding explicitly; the body field is not re-verified.
device_id
string
Stable per-device identifier. Optional — when omitted, the server synthesizes a deterministic id of the form clerk:<user_id> that is stable across reconnects. Pass an explicit value when two devices on the same Clerk account need to differentiate themselves (separate APNS tokens, distinct presence rows).
bundle_id
string
default:"com.bairisland.manticscore"
iOS bundle identifier of the calling app.
default_topics
array
Topics to pre-subscribe at connect. Topics outside the user’s own namespace are silently dropped — only user:<user_id>:* and arbitrary non-user:* topics are allowed. When the array is empty, the server inserts user:<user_id> so first-connect subscriptions work without any client-side topic plumbing.
200 response
{
  "token": "<opaque ws token>",
  "ws_url": "wss://api.manticscore.com/api/v1/realtime?token=<opaque ws token>",
  "expires_at": "2026-05-05T08:45:00+00:00",
  "default_topics": ["user:user_2abcXYZ:default"]
}
token
string
required
Short-lived WebSocket token (15-minute TTL). Treat as a credential.
ws_url
string
required
Pre-formatted WebSocket URL with the token embedded as a query parameter.
expires_at
string
required
ISO 8601 timestamp when the token expires. Re-bootstrap before this to maintain a continuous connection.
default_topics
array
required
The subset of default_topics that passed the namespace check.

Register a device for APNS fallback

Register (or refresh) an iOS device for APNS fallback delivery. Toasts dispatched while no foreground WebSocket is connected are forwarded to APNS instead of dropped. Rate limit: 30 requests per minute.
POST /v1/devices/register-toast
device_id
string
required
Stable per-device identifier; must match the one used in toast-bootstrap. If the bootstrap call omitted device_id, pass the synthesized clerk:<user_id> value here.
bundle_id
string
default:"com.bairisland.manticscore"
iOS bundle identifier.
apns
object
200 response
{
  "registered": true,
  "device_id": "device-uuid",
  "apns_fallback_enabled": true
}

Publish a toast from an external service

External publishers (separate services or scripts) call this endpoint to deliver a toast to a user. Internal services should call services.toasts.publish_toast() directly instead — no HTTP round-trip, no shared secret. Rate limit: 120 requests per minute.
POST /v1/toasts/publish
Authentication is a bearer token whose value is the shared TOAST_PUBLISH_SECRET_KEY:
Authorization: Bearer <toast_publish_secret>
user_id
string
required
Recipient Clerk user id.
topic
string
required
Topic to publish on. Subscribers receive only the topics they have explicitly subscribed to.
toast
object
required
dedupe_key
string
Optional. The server drops a toast if another with the same dedupe_key was delivered to this user recently.
collapse_key
string
Optional. The client collapses toasts that share a collapse_key, replacing the previous one in place.
ttl_ms
number
Optional. Server-side TTL after which the toast is dropped if not delivered.
fallback_apns
boolean
default:"true"
When true, the server falls back to APNS if the user has no foreground WebSocket connection.
202 response
{
  "delivered": true,
  "transport": "websocket",
  "session_count": 1
}
Returns 401 invalid_publish_key if the bearer is wrong, and 503 toast_publish_disabled if the secret is unset server-side.

List recent toasts for the current user

Replay surface for the authenticated user. Useful for a notification-center UI. Rate limit: 60 requests per minute.
GET /v1/toasts
limit
number
default:"50"
Page size, range 1–200.
200 response
{
  "items": [
    {
      "id": "01HXY7Z1...",
      "topic": "user:user_2abcXYZ:default",
      "title": "Research complete",
      "body": "AI expense tracker for freelancers",
      "style": "success",
      "priority": "normal",
      "deeplink": "manticscore://research/01HXY7AA...",
      "created_at": "2026-05-05T08:35:00+00:00"
    }
  ]
}

WebSocket realtime gateway

Open a multiplexed WebSocket to receive toasts in realtime.
GET wss://api.manticscore.com/api/v1/realtime?token=<ws_token>
The token is obtained from POST /v1/sessions/toast-bootstrap and expires after 15 minutes. Connections with an expired or unknown token receive an error frame and a close with code 4001. Connections without a token are closed with code 4002.

Server frames

On successful connect, the server immediately sends:
{
  "type": "connection_ack",
  "session_id": "<uuid>",
  "topics": ["user:user_2abcXYZ:default"]
}
Subsequent server frames carry typed messages — toasts, control envelopes, and protocol acks.

Client messages

The client may send the following JSON messages:
{"type": "subscribe", "topics": ["user:user_2abcXYZ:builds"]}
Topics outside the user’s own namespace are rejected with an error frame of code topic_not_allowed.

Close codes

CodeMeaning
4001Token expired or rejected after upgrade.
4002Missing token at connect.
The realtime gateway is multi-worker safe. The publisher uses a Redis pub/sub channel keyed on the user, so any worker holding a foreground connection delivers the toast — APNS fallback is only used when no worker has a connection.

Silent control envelopes

The same WebSocket also carries silent control messages with envelope {"type": "control", "control": {"kind": "...", "params": {...}, "ts": "..."}}. These are dispatched by the server when state the client cares about changes — for example:
  • usage.changed — fired when an entitlement transitions (RC webhook event, Apple server-to-server notification, or admin credit grant). The client refetches GET /v1/usage.
  • flags.changed — broadcast to all clients when CMS feature flags update.
Control envelopes are not persisted and have no APNS fallback — every control kind has a corresponding REST endpoint the client can refetch from on reconnect. SDKs route these to a separate onControl handler so they do not surface as toast overlays.