Skip to main content
Pull events from Relay’s durable log with GET /v1/events. No public URL, no signing secret: the Agent Token is the only credential, and it travels outbound only. Choose long polling when your backend cannot accept inbound HTTPS: a laptop, a host behind NAT, or a process you do not want exposed. If your backend has a public HTTPS endpoint, use webhooks instead. The two transports are mutually exclusive per Agent Token.

The poll loop

The response carries events past the cursor, oldest first, and the cursor to persist: Loop immediately after each 200. The timeout hold provides the pacing, so a successful poll needs no sleep between requests.

Cursor rules

Passing a cursor durably acknowledges every event at or below it, and advances message delivery receipts.
  1. Handle or durably enqueue every event on the page.
  2. Persist the handled state and next_cursor together, atomically.
  3. Only then poll again with the new cursor.
A cursor advanced before its events are durable loses those events on a crash. A cursor never advanced replays the same page forever.
Cursors are scoped to the agent, not the token. Rotating an Agent Token never resets the ledger.

One consumer per token

Relay allows one held-open poll per agent. Starting a newer poll terminates an older one with 409 terminated_by_other_consumer. Run exactly one poller; a second process with the same token steals the slot, and restarting does not win it back.

Webhooks exclude polling

Polling while any webhook endpoint is enabled returns 409 conflict. Disable or delete the webhooks to poll. Around a registration or disable, both paths may briefly observe the same events; deduplicate on event_id.

Failure and recovery

Recover from 410 cursor_expired

Do not reset the cursor to zero: that replays everything still retained.
  1. Reconcile state from conversation history.
  2. Call POST /v1/events/reconcile with expired_cursor set to error.details.highest_delivered_cursor from the 410 response, and history_reconciled: true.
  3. Persist the returned resume_cursor, then poll from it.

Next steps

  • Delivery model for the concepts behind cursors, watermarks, and at-least-once delivery
  • Quickstart for the full receive-and-reply loop on either transport
  • Webhooks for the push transport
  • Errors for every status and code