Skip to main content

Tailing request-log events — worked samples

GET /api/v1/tail opens a Server-Sent Events (SSE) connection and streams every API request handled for your tenant — status code, latency, and request metadata — sub-second after each response completes. It is the data source behind the Live tail toggle on the developer Request Logs page. The stream excludes health, readiness, and metrics probes, provider webhook ingress, and the tail endpoint itself, so what you see is your traffic only. Because the response is an event stream — not a JSON document — this page documents the frames and fields below; copy the cURL sample as written and the stream prints to your terminal.
GET /api/v1/tail
Authentication: API key (X-API-Key) or session JWT. The caller must hold the owner, admin, or developer role. If you cannot send headers — a browser EventSource — pass the credential as the token query parameter instead: a dv_-prefixed value is treated as an API key, any other value as a bearer JWT. From server code, always use the header; a credential in a URL ends up in access logs and proxy history. Query parameters

Open the stream with cURL

-N disables curl’s output buffering. Without it, the frames arrive but stay invisible until curl’s output buffer fills. The Accept header makes the SSE intent explicit. The query-parameter table above documents token and lastEventId; use headers whenever your client can send them.

Read the stream from Node.js

Use fetch when you need the raw SSE response; the generated SDK request helper buffers responses and cannot consume a never-ending stream. The parser below keeps the { event, id, data } frame shape and sends the latest lastEventId cursor on the next connection.

Read the stream from Python

requests exposes each SSE line as it arrives. Keep event, id, and data until the blank line terminates a frame, then parse the JSON data.

Frame sequence

After connecting, the server sends, in order:
  1. An event: connected handshake frame carrying the organization id.
  2. A backfill of up to the most recent ~100 request rows, each an event: log frame.
  3. New event: log rows live, as each request completes.
Comment lines (: heartbeat) arrive every 30 seconds to keep the connection open. The first frames
The response has no { data, meta } envelope — it is Content-Type: text/event-stream, so every frame above IS the response body. Log event fields — each log frame’s data: line is one JSON object: Adding fields is backwards-compatible — ignore keys you don’t recognize.

Subscribe from Node.js with EventSource

Named frames mean a generic onmessage handler never sees the rows — register a log listener:

Resume after a disconnect

Every log frame carries an id (a stream cursor). To resume without losing rows, reconnect with Last-Event-ID or pass the same value as lastEventId. The loop below rehydrates the cursor after a dropped socket and increases the delay between attempts.
Node.js
EventSource sends Last-Event-ID automatically when it reconnects. For an explicit request, pass the same cursor as lastEventId:
cURL
The server replays the rows newer than that cursor, then continues the live tail.

Subscribe from a browser

EventSource cannot set request headers, so use the documented token query parameter only in browser code. Prefer a short-lived credential and do not use this form in server code, where URLs may be logged.
Browser

Limits and errors

This is a live tail, not durable history. For a longer-horizon export of the same rows, route through your own sink (as in the Node.js sample) instead of polling the stream.

Response sample — first frames as JSON

The SSE stream on the wire is the text sample above. For tooling that wants the same two frames as a parsed body, treat each frame’s data: line as one JSON object in arrival order:
The second frame (event: log) carries the per-request row documented in the field table above; id advances so Last-Event-ID resumes cleanly after a reconnect.