Skip to content
Linq Copy agent prompt

Stream events (SSE)

GET/v1/streams/events

Streams the brand’s events as server-sent events, one data: frame per event, id: carrying the event’s cursor. Reconnect with Last-Event-ID (or the cursor query parameter) to resume without a gap — the stream carries the same events the webhooks deliver, byte for byte, which is why a webhook signature and a stream frame can never disagree.

A frame. id: is the resume cursor and nothing else; event: is the event’s kind; data: is one JSON object {"id","type","timestamp","data":{...}}id there is the event’s own stable identity (the same value a webhook of it carries, so both sinks deduplicate against one key), and the inner data addresses what moved: chat_id plus the seq_from/seq_to range of that chat’s event sequence. Read message content and history from GET /v1/chats/{chat}/events using that range. Kinds today are message.received, message.sent, message.delivered, message.read, message.failed, chat.read and chat.window.opened; the set grows additively, so skip a kind you do not handle rather than failing on it, and expect new members inside data too.

One message can produce several events. An inbound message produces message.received and, when it opens the customer window, chat.window.opened. Your reply then produces message.sent and may later produce message.delivered. Do not treat every event as a reason to reply; choose the event kinds that are actual triggers for your integration.

Connecting. Without a cursor the stream starts at the present moment — it does NOT replay history. During ordinary operation, use the account event log to recover missed account events and GET /v1/chats/{chat}/events to read chat content. Account-event history has no fixed minimum retention period in beta, and an environment reset may erase it. The server sends a retry: hint on connect and a : keepalive comment while idle; neither is an event. There is no scheduled account-event pruning today. If history is no longer available, a cursor older than its retained beginning is refused with HTTP 410 cursor_expired rather than answered with silence. A cursor beyond the latest committed event is refused with HTTP 422 invalid_paging, while a cursor exactly at the latest event is the ordinary caught-up position. An empty cursor is refused too: omit it to start at the present position.

Cursors are scoped to the brand and the test/live partition selected by the key. Each partition has its own dense sequence from 1, while that partition is deliberately absent from the wire. If you reconnect at cursor 42 with a key for the other partition, the server answers 200 and resumes at that partition’s event 42; cursor_expired cannot signal the swap. Keep separate cursors for sk_test_ and sk_live_ keys.

Webhooks. The same events are delivered as signed webhooks (Standard Webhooks: webhook-id, webhook-timestamp, webhook-signature, verifiable with any off-the-shelf library). Register a destination for them under /v1/webhook_endpoints.

Query ParametersExpand Collapse
cursor: optional string

Resume cursor — the last event id you processed. Empty and beyond-latest values are refused. The header form wins when both are set.

Header ParametersExpand Collapse
"Last-Event-ID": optional string

Stream events (SSE)

curl https://whatsapp.messages.api.linqapp.com/v1/streams/events \
    -H "Authorization: Bearer $LINQ_WHATSAPP_API_KEY"
Returns Examples