Point your agent’s OpenAI or Anthropic SDK at a recording and run it for real: your code, your prompts, your control flow. Every model answer comes from the tape, in the shape the SDK expects. Nothing is sent to a model provider.
Record the run with prompt and completion capture on, the default for OpenLLMetry. A replay can only serve answers that were recorded, and it says which step is missing one.
Create a replay of the run. It snapshots the tape and lives for an hour. Creating one changes nothing in the store, so a read key is enough.
Point the SDK at it with two environment variables and run your agent. It receives each recorded answer as an ordinary chat completion or message, streamed if it asks for a stream.
exportOPENAI_BASE_URL=https://api.trazadb.com/v1/replays/rp_ac879985abef9cba12bb342bdc3b8bd1/openai/v1
exportOPENAI_API_KEY=$TRAZA_API_KEY
python weather_agent.py
# Anthropic SDK: it sends x-api-key, which replay routes acceptexportANTHROPIC_BASE_URL=https://api.trazadb.com/v1/replays/rp_ac879985abef9cba12bb342bdc3b8bd1/anthropic
exportANTHROPIC_API_KEY=$TRAZA_API_KEY
Strict by default
A regression shows up as a 409 at the step that changed.
In strict mode each request must be the recorded request: its messages are normalised and digested, then
compared with the recording. A changed prompt, a different tool result or a new branch means the agent asked a
different question, and serving the recorded answer would answer the wrong one. So the replay refuses, names the
step, and does not consume it. The response tells both SDKs not to retry.
Loose mode serves the steps in order regardless, and records whether each call matched.
A divergence, from the Replay guide
HTTP/1.1 409 Conflict
x-should-retry: false
{"error":{"type":"replay_divergence","step":2,
"message":"this request's messages do not match recorded step 2: the agent diverged from the recording, so replaying the recorded answer would answer a different question",
"expected_digest":"6db28425e9e1…","got_digest":"cd1ff438bb75…"}}
Diff
Where did this run part from that one?
GET /v1/diff aligns two runs’ steps by their signatures, so an extra or missing step shifts nothing
after it, then classifies each pair and names the first divergence: a different input, a different decision for
the same input, a configuration change such as a model swap, or a step only one run has.
GET /v1/recall finds earlier runs that dealt with the same words, successful ones first, with a
skeleton of what each did.
What differs
Reported as
Nothing
same
The input to the same step
changed · input
The output, for the same input
changed · decision
The model or a request parameter
changed · config
A step only one run has
insert / delete · structure
Fork & Rewind
Change one step, and let the agent carry on for real.
A fork serves a different model answer or tool result at any step, then goes live: past the cut, every call answers
409 replay_live, which is the harness’s signal to send it, and every later one, to the real provider.
The Python and TypeScript clients do that for you, and tag the forked run so it can be found again with how it
ended and what it cost, compared with its parent.
Rewind is a live point alone: replay a long run’s first forty steps for free, then continue live
from there. Traza never calls a model; a fork’s live half is your agent’s own traffic.
Fork the demo Claude Code session at its failing test run
{"run":"11111111…","edits":[{"step":5,"tool_result":"test result: ok. 3 passed; 0 failed"}],"live_after":5}
Step 5 (Bash) failed in the recording.
The fork holds steps 0–4, 3 of them model calls served from the
tape, answers step 5 with the edit, and goes live after step 5.
Demo corpus
Real output of traza-server 0.28.0 over the engine’s synthetic Claude Code and Codex transcripts (4 sessions), imported with traza-server import, recorded 2026-10-18 by
scripts/demo-numbers.mjs. Synthetic runs with known outcomes, not customer data.
Tape tests
Turn the runs that went right into tests.
A tape test is a strict replay the agent must walk step for step, failing with the message, field and character
where it left the path — from pytest, vitest or jest, or for every golden run at once with
traza test, which writes a JUnit report for CI.
Diff across the demo map’s fatal fork
refund_happy-1 (success, 5 steps) against search_loop-0
(failure, 6 steps): first divergence at step 2,
a structure change — run b has step 2 (tool:search_orders) where run a has no matching step.
Demo corpus
Real output of traza-server 0.28.0 over the engine’s seeded behaviour corpus (226 runs, 1,342 spans), recorded 2026-10-18 by
scripts/demo-numbers.mjs. Synthetic runs with known outcomes, not customer data.
On Traza Cloud
From a run in the console to a replay in your terminal.
The console’s Replay tab lists recent runs. Pick one and it creates the replay and shows the two environment
lines for the OpenAI or Anthropic SDK, with a Python snippet, all pointing at api.trazadb.com. Use any
read or full key of the project; the console never holds key secrets.
Stated plainly
What Replay does not do.
Tools execute live. A replay answers model calls. When the replayed model asks for a tool, your agent runs the real tool with its real side effects, unless your harness takes the recorded result from tools/next.
Streams are reconstructed. No instrumentation records how a provider chunked its stream, so the text is re-chunked; the text the SDK reassembles is exactly the recorded text.
Only chat completions and messages are replayed. The OpenAI Responses API, embeddings and legacy completions answer 400.
Strict mode compares messages only, not the model name, tools or temperature. A request with the recorded messages and a different model is served.
Replays live in memory for an hour. A restart forgets them; creating another is just another read. An erasure withdraws any replay holding erased data. A fork is a replay, under the same rules.
After an edit without a live point, the recording plays on loosely, answering a conversation it was not recorded in. That exercises your code down the recorded path; it is not what a model would have said.