# Currents trace API

Serves the contents of a Playwright trace zip over HTTP using ranged reads, so reading one part does not download the whole zip. All trace endpoints live under one time-limited base URL:

```
https://<host>/<slug>/<endpoint>
```

Links are minted by the Currents backend, which stores the trace URL under the slug. Once the link expires, every endpoint returns 404 and a new link has to be minted; with `placeholder=1`, `filmstrip`, `frames/failure` and `snapshots/failure?format=html` answer 200 with a placeholder instead. Responses are edge-cached per trace and send CORS headers, so browsers, scripts, and agents can call them directly. Timestamps are the trace’s monotonic clock in ms; get valid values from `/frames` or the links in `/digest?format=md`.

Machine-readable spec: [`/openapi.json`](/openapi.json)

## `GET /<slug>`

Interactive player for this trace. A self-contained HTML page: scrubbable screencast player with fullscreen, action list (click a failing action to seek to its frame), and frame thumbnails that open a full-size lightbox, all fetched from this API. Opening the access URL in a browser just works.

Returns `text/html`.

```
GET /<slug>
```

## `GET /<slug>/download`

Download the trace zip. The trace archive, for Playwright’s own trace viewer (https://trace.playwright.dev/?trace=<this URL>) or any tool that reads the zip. Supports Range requests.

Returns `application/zip`.

```
GET /<slug>/download
```

## `GET /<slug>/digest`

The trace summary: what the attempt did and what failed. Browser name, the Playwright version that recorded the trace (playwrightVersion) and its trace format version (traceFormatVersion), each null when the trace does not record it. Actions (runner-step titles, nested, deduped against browser-side calls), failing actions with errors and frame/snapshot links, page errors (exceptions the page did not catch, with the first stack frame and t=), console errors, network failures, attachments, screencast summary. networkOmitted names a .network entry too large to read, whose requests are missing from the counts and from /requests. The markdown format links every t= to the frame and snapshot endpoints.

- `format` — json (default) or md (json|md)
- `timeline` — auto (default) keeps the timeline focused on failures — every failing action with its ancestor steps and nearby rows, gaps elided; full returns every action (and lifts the JSON action cap) (auto|full)

Returns `application/json | text/markdown`.

```
GET /<slug>/digest?format=md
```

## `GET /<slug>/filmstrip`

The whole run as one contact sheet. Samples frames evenly across the attempt (up to the failure with end=failure) and composes them into one WebP, a near-square grid or a single row — one image request that shows the run, sized for vision models and previews. A strip wider or taller than 16383px (the WebP limit) is a 400.

- `frames` — tile count (2..25), default 9
- `width` — per-tile width (64..1280), default 320
- `height` — cap on each tile’s height: a frame is scaled to fit within width × height, so portrait frames stay short (16..1280)
- `layout` — grid (default, near-square) or row (every tile in one row) (grid|row)
- `end` — `failure`: sample only up to the frame frames/failure serves and outline that last tile in red; when nothing failed, the whole attempt with no outline (failure)
- `placeholder` — `1` returns 200 with a grey SVG of one 16:9 tile fitted within width × height (width 320 when `width` is missing or invalid; no height cap when `height` is missing or invalid) in place of an error response. The `x-placeholder` header names the reason: `routeError` for no screencast frames, a bad parameter, a strip over 16383px, cached like any response; `notFound` for an unknown, malformed or expired link and `unavailable` for a storage or read error, an unexpected error or too much trace work waiting, both sent with `cache-control: no-cache`. Default: unset, so errors keep their status. Other values are ignored. (1)

Returns `image/webp | image/svg+xml`.
Header `x-filmstrip-timestamps`: JSON array of each tile’s trace timestamp; not sent on a placeholder answer.
Header `x-placeholder`: the reason for a placeholder answer (routeError, notFound or unavailable); not sent on other answers.

```
GET /<slug>/filmstrip?frames=9&width=320
```

## `GET /<slug>/animation`

Animated screencast, rendered on demand. Samples up to frames= screencast frames and muxes them into an animated WebP (or GIF). Each parameter combination is cached as its own variant, so embeds can pick a size without a stored artifact.

- `format` — webp (default) or gif (webp|gif)
- `frames` — frames to sample (1..150), default 60
- `width` — resize to at most this width (16..2560), default 720
- `quality` — WebP quality (1..100), default 75
- `overlay` — progress draws a frame counter (e.g. 12/60) in the bottom-right corner of every frame (progress)

Returns `image/webp | image/gif`.
Header `x-animation-frames`: frames in the output.
Header `x-animation-source-frames`: screencast frames in the trace.

```
GET /<slug>/animation?frames=60&width=720
```

## `GET /<slug>/frames`

Screencast timeline metadata. Frame count, time bounds, dimensions, and per-frame timestamps — drives scrubbers and tells you which timestamps exist before requesting frames. Also playwrightVersion (e.g. 1.64.0-alpha-1790635538000) and traceFormatVersion (e.g. 10), each null when the trace does not record it, to pick a trace viewer that can open the trace. A trace without screencast frames answers with frameCount 0.

Returns `application/json`.
Header `x-dashboard-url`: The test’s page in the Currents dashboard, when the link names one. Opening it needs a login to the organization..

```
GET /<slug>/frames
```

## `GET /<slug>/frames/failure`

The screencast frame at the failure. The frame at or before the moment the test failed: the end of the last action that failed in the step where the test error was recorded, so a caught error in an earlier step or an earlier toPass retry does not count; if none failed there, the end of the action running when the test error was recorded, or the next action event if it never ends (a timeout fails no action); otherwise the next action event after the test error; with no test error, the first action that failed; failing that, the last frame. Takes the same parameters as frames/{t}, plus placeholder, and sends the same headers, except on a placeholder answer.

- `format` — jpeg (default, stored bytes as-is) or webp (jpeg|webp)
- `width` — scale to exactly this width (implies webp) (16..3840)
- `placeholder` — `1` returns 200 with a grey 16:9 SVG at the requested width (480 without a valid one) in place of an error response. The `x-placeholder` header names the reason: `routeError` for no screencast frames, a frame missing from the zip, a bad parameter, cached like any response; `notFound` for an unknown, malformed or expired link and `unavailable` for a storage or read error, an unexpected error or too much trace work waiting, both sent with `cache-control: no-cache`. Default: unset, so errors keep their status. Other values are ignored. (1)

Returns `image/jpeg | image/webp | image/svg+xml`.
Header `content-location`: exact timestamp of the served frame, as a sibling URL.
Header `x-trace-timestamp`: trace timestamp of the served frame.
Header `x-placeholder`: the reason for a placeholder answer (routeError, notFound or unavailable); not sent on other answers.

```
GET /<slug>/frames/failure?width=720
```

## `GET /<slug>/frames/{t}`

One screencast frame as an image. The frame at or before trace timestamp {t}. By default the stored JPEG is passed through untouched; format=webp or width= re-encodes as WebP, scaled to exactly that width — upscaling beyond the stored frame size too.

- `format` — jpeg (default, stored bytes as-is) or webp (jpeg|webp)
- `width` — scale to exactly this width (implies webp) (16..3840)

Returns `image/jpeg | image/webp`.
Header `content-location`: exact timestamp of the served frame, as a sibling URL.
Header `x-trace-timestamp`: trace timestamp of the served frame.

```
GET /<slug>/frames/100529?width=720
```

## `GET /<slug>/snapshots`

Catalog of DOM snapshots. Every addressable snapshot: exact t, name, capture phase (before|input|after relative to its action), callId (joins the trace summary's actions), and frame url. Use the name or t as the {id} in the item route.

Returns `application/json`.

```
GET /<slug>/snapshots
```

## `GET /<slug>/snapshots/failure`

The DOM snapshot at the failure. The snapshot at or before the moment the test failed: the end of the last action that failed in the step where the test error was recorded, so a caught error in an earlier step or an earlier toPass retry does not count; if none failed there, the end of the action running when the test error was recorded, or the next action event if it never ends (a timeout fails no action); otherwise the next action event after the test error; with no test error, the first action that failed; failing that, the last snapshot. Takes the same formats as snapshots/{id}, plus placeholder, and sends the same headers, except on a placeholder answer.

- `format` — how to render the snapshot (json|dom|html|aria|text)
- `placeholder` — `1` returns 200 with a short HTML page (format=html only; other formats keep their error) in place of an error response. The `x-placeholder` header names the reason: `routeError` for no snapshot, a snapshot whose root refers to one the trace does not have, cached like any response; `notFound` for an unknown, malformed or expired link and `unavailable` for a storage or read error, an unexpected error or too much trace work waiting, both sent with `cache-control: no-cache`. Default: unset, so errors keep their status. Other values are ignored. (1)

Returns `application/json | text/plain | text/html`.
Header `content-location`: exact id of the snapshot used, as a sibling URL.
Header `x-trace-timestamp`: timestamp of the snapshot used.
Header `x-placeholder`: the reason for a placeholder answer (routeError, notFound or unavailable); not sent on other answers.

```
GET /<slug>/snapshots/failure?format=text
```

## `GET /<slug>/snapshots/{id}`

One DOM snapshot, rendered by format. {id} is a snapshot name from the catalog (exact) or a trace timestamp in ms (snapshot at or before). Formats: json (default) — the catalog entry plus available formats; dom — the reconstructed markup as inert text, for reading and diffing; html — a browser-renderable styled page like the Playwright trace viewer's (stylesheets inlined from the trace's CSSOM captures, subresources rewritten to captured request bodies, scripts stripped, CSP-locked); aria — an approximate aria tree (static roles/accessible names; no layout, so visibility is best-effort); text — readable page text with interactive markers.

- `format` — how to render the snapshot (json|dom|html|aria|text)

Returns `application/json | text/plain | text/html`.
Header `content-location`: exact id of the snapshot used, as a sibling URL.
Header `x-trace-timestamp`: timestamp of the snapshot used.

```
GET /<slug>/snapshots/after@call@10?format=html
```

## `GET /<slug>/requests`

Network requests the test made. Every request the page issued, from the trace: index (the {index} in the item routes — positions in the unfiltered list, stable under filters), method, url, status, mime type, body size, timing, and the response and request body hashes when captured. format=har returns a standard HAR 1.2 file (bodies excluded) that opens in any HAR viewer, including browser devtools. A .network entry too large to read is left out; the trace summary names it in networkOmitted.

- `status` — 4xx, 5xx, failed (>=400 or no response), or an exact code
- `url` — substring filter on the request url
- `requestBody` — substring filter on the body the page sent, like a GraphQL operation name or a value from a form; at most 1000 characters. 422 when the bodies to search are over 32 MB together: narrow with url or status first.
- `format` — json (default) or har (json|har)

Returns `application/json`.

```
GET /<slug>/requests?status=failed
```

## `GET /<slug>/requests/{index}`

One network request. The request at this index in the unfiltered list: method, url, status, mime type, body size, timing, and the response and request body hashes when captured.

Returns `application/json`.

```
GET /<slug>/requests/12
```

## `GET /<slug>/requests/{index}/body`

A response body the test saw. The captured response body of the request at this index, served with its recorded mime type. The payload behind "the API returned wrong data" — no reproduction needed. 404 when the trace did not capture this body.

Returns `response mimeType`.

```
GET /<slug>/requests/12/body
```

## `GET /<slug>/requests/{index}/request-body`

A request body the page sent. The body the page sent with the request at this index (POST data, a GraphQL query and its variables), served with its recorded mime type. Tells apart requests that share a url, like every call to /graphql. 404 when the request had no body.

Returns `request mimeType`.

```
GET /<slug>/requests/12/request-body
```

## `GET /<slug>/attachments`

Files embedded in the trace. Lists attachments recorded inside the trace (test.attach output, Playwright's error-context, screenshots, and from Playwright 1.63 the video) with name, contentType, and sha1. An attachment the runner left on disk without copying it into the zip is not served.

- `name` — substring filter on the attachment name

Returns `application/json`.

```
GET /<slug>/attachments?name=error
```

## `GET /<slug>/attachments/{id}`

One attachment's bytes. {id} is a sha1 content hash (from the attachments list or the trace summary) or an attachment name; by name, last match wins, since names repeat across retries. Served with the attachment's own content type. Playwright's error-context failure summary lives at ./attachments/error-context.

Returns `attachment contentType`.

```
GET /<slug>/attachments/error-context
```

## `POST /animations`

Renders the animated screencast and PUTs it to an upload URL — the artifact-generation entry point, called by the Currents backend with `Authorization: Bearer <AUTH_TOKEN>`. Body: `{ traceUrl, uploadUrl, format?, maxFrames?, maxWidth?, quality?, overlay? }` with the same ranges as `/<slug>/animation`.
