Realtime and webhooks
Account-scoped invalidations, resumable events and signed server delivery.
Cursor reads
GET /v1/accounts/{externalUserId}/updates?cursor=0&limit=100 requires
orders:read and a ready account. It returns events, compatibility alias
updates, next cursor, and hasMore. Limit 1–200. Persist cursor only after
processing. Deduplicate immutable event id; delivery is at least once.
Envelope fields: id, cursor (opaque decimal string), version: 1, type,
externalUserId, accountId, occurredAt, data. Supported event types:
account.status_changed, order.updated, trade.recorded, balance.updated,
funding.updated, withdrawal.updated, position.redeemed, settlement.updated,
reconciliation.completed. Cursors can have gaps. Cursor order is delivery order;
timestamps may be earlier when late upstream events reconcile.
Events are sanitized, not raw venue frames. Re-read the applicable resource for authoritative state. Confirm retention during onboarding; after a retention gap, refresh account resources instead of assuming no missed activity.
Browser realtime
Register exact frontend origins with CRISP. Your backend authenticates its user
and calls POST /v1/accounts/{externalUserId}/realtime-session with
realtime:session. Forward the returned short-lived session, not HMAC credentials.
// session is the data.session returned by your own authenticated backend.
const socket = new WebSocket(session.endpoint, [session.protocol, session.token]);
socket.addEventListener('open', () => {
socket.send(JSON.stringify({ type: 'resume', after: lastProcessedCursor || '0' }));
});
socket.addEventListener('message', ({ data }) => {
const message = JSON.parse(data);
// event: invalidate/re-read through your backend, then persist cursor.
// resync_required: full refresh, save supplied cursor and resume.
});Notices contain event ID/cursor/type/time, not balances you can sum. Refresh via your BFF, coalesce bursts and reconnect with backoff. Renew expiring sessions through your authenticated backend. The browser cannot choose another tenant or account topic. A realtime failure must not cause another order submission.
Signed server webhooks
CRISP configures approved HTTPS endpoint/secret. These are optional for backend automation; not required to build the normal browser UI. Headers:
X-Crisp-Event-Id: immutable event UUID
X-Crisp-Event-Cursor: event cursor
X-Crisp-Webhook-Timestamp: UTC timestamp
X-Crisp-Webhook-Signature: v1,{base64 HMAC-SHA-256}Signature input is timestamp + "." + exact_raw_body. It is different from
request signing. Verify bounded timestamp skew, compute HMAC with webhook secret,
compare equal-length digests in constant time, validate event/header identity,
then durably deduplicate before applying side effects. Preserve raw bytes before
JSON middleware. Return 2xx only after durable acceptance; acknowledge duplicates
with 2xx. CRISP follows no redirects and retries failed delivery with bounded
backoff before dead-letter handling. Agree replay/support procedures at onboarding.
Do not forward raw events containing user IDs to unauthenticated browsers or log webhook secrets. Treat external strings as data when rendering UI.