Search the docs
Find a page, a section, or an endpoint.
Events
Every events endpoint, generated from the API's own schemas.
List events
get/api/v1/events
The same events a webhook delivers, readable after the fact — so a missed delivery is recoverable without replaying anything. **Two directions.** `cursor` pages backwards through history, newest first, which is what a dashboard shows. `after` tails the log forwards, oldest first, which is what a caller that cannot receive a webhook does: ask for everything since the last event you saw, act on it, and keep the last `created_at` as the next cursor. Naming both is refused — they are different reads. **Starting from nothing: pass `after=1970-01-01T00:00:00Z`.** With neither parameter this answers newest first, so a first-time consumer that takes the last row as its high-water mark stores the *oldest* event in the log and never advances. Any timestamp at or before the oldest event you hold will do; the epoch is the one nobody has to think about. A forward page is ordered oldest first precisely so its **last** row is the high-water mark to resume from; `next_cursor` is that row either way.
Parameters
- limitnumber
- cursorstring
- typestring
- identity_iduuid
- afterdate-time
Responses
- 200A page of events.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/events \
-H "Authorization: Bearer $PIDGEON_API_KEY"Events over a WebSocket
get/api/v1/events/socket
The event log over a WebSocket — `wss://pidgeon.ai/api/v1/events/socket` — with the same `type`, `identity_id` and `after` as `GET /events/stream`. All frames are JSON text. **Authenticate** with the `Authorization` header on the upgrade, or — from a browser, which cannot set one — send `{"type":"auth","api_key":"pgn_live_…"}` as the first message within ten seconds. Never put the key in the URL. **The server sends** `{"type":"ready","cursor"}`, then `{"type":"event","event":Event}` per event, oldest first. Before about 280 seconds it sends `{"type":"reconnect","cursor"}` and closes with 4000: reconnect with `after` set to that cursor and nothing is missed. 4401 means no key or a bad one; 4500 means the read failed — reconnect with the last cursor you saw. Like the stream, it polls the log on our side of the wire every two seconds.
Parameters
- limitnumber
- cursorstring
- typestring
- identity_iduuid
- afterdate-time
Responses
- 101Switching protocols: the socket is open.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/events/socket \
-H "Authorization: Bearer $PIDGEON_API_KEY"Stream events as they happen
get/api/v1/events/stream
The event log held open, for a caller that cannot host a webhook endpoint — a script on a laptop, a notebook, an agent runtime with no public address. **Server-sent events.** Each message carries the event as `data`, the event type as `event:`, and the cursor to resume from as `id:`. Authentication, filtering by `type` and `identity_id`, and the `after` cursor are all exactly as on `GET /events` — this is that read, held open. **Reconnection is automatic and lossless.** A compliant client resends the last `id:` it saw as `Last-Event-ID`, which is preferred over `after`, so a dropped connection resumes with no gap and no repeat. The connection closes itself after about 280 seconds so that the last thing you receive is a cursor rather than a socket error; reconnect and carry on. **It polls, every two seconds, on our side of the wire.** No push exists in the database. What this buys is one held connection instead of a request from your network on a timer, and being woken only when something happened.
Parameters
- limitnumber
- cursorstring
- typestring
- identity_iduuid
- afterdate-time
Responses
- 200An open stream of events.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/events/stream \
-H "Authorization: Bearer $PIDGEON_API_KEY"Fetch one event
get/api/v1/events/{id}
Parameters
- idstring
Responses
- 200The event.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/events/{id} \
-H "Authorization: Bearer $PIDGEON_API_KEY"