Skip to content
WaaruDocs

Message changes

Reconcile later message observations while retaining your native reply handler.

Reviewed

GET /v1/messages/changes requires messages:read. Query fields are only after (opaque, up to 2,048 characters) and limit (1 to 100, default 50). It follows immutable canonical revisions for this key's number, including later status observations on older messages. Message occurrence-time filters are not a substitute.

The first request without after returns empty items, nextCursor: null, a current resumeCursor and history: "retained_only". Track history-reconciliation completion durably, separately from the cursor. For every fresh bootstrap, clear completion and old scan checkpoints before saving its cursor, or save the incomplete marker and cursor atomically. Save the bootstrap cursor before scanning historical conversations and messages. Keep that original cursor until the full scan succeeds. The scan must safely repeat or resume from durable checkpoints; after interruption, a saved cursor does not mean history is complete. Mark completion only after every history page is durably reconciled, then poll from the bootstrap cursor to catch changes committed during the scan. Run one reconciler per number, and do not poll while history remains incomplete. Use separate number-bound keys for multiple numbers and preserve both number and conversation identity in your stored records.

Each item has id (stable change deduplication ID), decimal-string revision, messageId, type (message.created or message.status_changed), status and occurredAt. Revisions order observations; occurredAt is evidence time, not a cursor. Creation status is received for inbound messages and null for outbound messages. Status observations use the public message states; duplicate states and late observations are possible. Read the message directly to reconcile current status instead of overwriting newer state from event order alone.

Pass resumeCursor as the next after, follow every nextCursor until null even when items is empty, and persist each resumeCursor with durably processed changes. The page limit bounds scanned activities, so filtered activities can produce empty pages. A catch-up cycle fixes its watermark; later commits appear in the next cycle. Deduplicate on change id; message IDs identify the current record.

Malformed, changed-version, tampered, wrong-number or wrong-workspace cursors return 400 invalid_cursor. A cursor's seven-day lease expires with 410 cursor_expired; every returned cursor renews the lease. Signing-key changes can invalidate older cursors. For either recovery, obtain a fresh bootstrap, reset durable history completion and old scan checkpoints before saving its new cursor (or write both atomically), then finish retained-history/current-state reconciliation before polling. On restart, repeat or resume an incomplete scan using its original bootstrap cursor, never skip it because a cursor is present. Do not invent a timestamp cursor or silently discard the gap.

history: "retained_only" is always returned. Physical deletion, deleted contacts and canonical retention can remove evidence even within a valid cursor lease. There are no deletion tombstones, permanent history guarantee or complete replay. The seven-day lease is not a seven-day evidence-retention promise.