Docs Integrations
Anthropic
Wrap the Anthropic client, in TypeScript or Python, to record every Messages call your agent makes. About five minutes.
Before you begin
- A Runback account and a Send only key. In the app, open Settings › Keys and access › API key, keep Send only selected, select Generate API key and copy it (it is shown once and starts
rb_cloud_). The quickstart walks through it. -
Node.js 20 or later (
node --version). Python 3.9 or later (python3 --version). - An Anthropic API key.
Steps
Install the SDK and the Anthropic client
terminalShell npm install @runback/sdk @anthropic-ai/sdkterminalShell pip install "runback-sdk[anthropic]"Expected outputThe install finishes without errors.Set your keys
Runback reads its key from
RUNBACK_API_KEY; the model client reads its own.terminalShell export RUNBACK_API_KEY="<RUNBACK_API_KEY>" export ANTHROPIC_API_KEY="sk-ant-..."Expected outputNothing is printed.echo $RUNBACK_API_KEYshould print your key.Wrap the client and run it
Wrap your existing client once; every
messages.createandmessages.streamcall through it is recorded. Save this asagent.mjsand runnode agent.mjsagent.pyand runpython agent.py.agent.mjsJavaScript // agent.mjs: npm install @runback/sdk @anthropic-ai/sdk import Anthropic from "@anthropic-ai/sdk"; import { startRun } from "@runback/sdk/core"; import { wrapAnthropic } from "@runback/sdk/anthropic"; const task = "Say hello in three words."; const run = startRun({ runName: "support-agent", input: task }); const claude = wrapAnthropic(new Anthropic(), run); // messages.create and messages.stream const msg = await claude.messages.create({ model: "claude-sonnet-4-6", max_tokens: 256, messages: [{ role: "user", content: task }], }); const answer = msg.content.map((b) => (b.type === "text" ? b.text : "")).join(""); await run.finish({ output: answer, status: "success" }); console.log(answer);agent.pyPython # agent.py: pip install "runback-sdk[anthropic]" import anthropic from runback import Collector from runback.anthropic import wrap_anthropic task = "Say hello in three words." run = Collector(run_name="support-agent", input=task) claude = wrap_anthropic(anthropic.Anthropic(), run) # or anthropic.AsyncAnthropic() msg = claude.messages.create( model="claude-sonnet-4-6", max_tokens=256, messages=[{"role": "user", "content": task}], ) answer = "".join(b.text for b in msg.content if b.type == "text") run.finish(output=answer) print(answer)Expected outputYour agent's normal output and no[runback]line. A[runback]line means the run was not sent; see Troubleshooting.Check the run arrived
Open Runs in the app. Your run is at the top, named
support-agent. Open it to step through what the agent did.
Troubleshooting
| You see | What it means | Fix |
|---|---|---|
[runback] no RUNBACK_API_KEY set — dropped 3 events | The key is not set in the terminal you ran the agent from. | Export RUNBACK_API_KEY in the same terminal (a new tab starts without it), then run the agent again. |
[runback] ingest failed 401 {"error":"Invalid API key"} | Runback rejected the key: mistyped, revoked or expired. | Copy the whole key again, or create a new one in Settings › Keys and access › API key. |
Error: Could not resolve authentication method. Expected one of apiKey, authToken, … | ANTHROPIC_API_KEY is not set. | Run the agent with the key set, e.g. ANTHROPIC_API_KEY=sk-ant-... node agent.mjs. |
Failed to load the ES module (Node 22+) or Cannot use import statement outside a module (Node 20) | The file is not treated as an ES module. | Name it .mjs, or add "type": "module" to package.json. |
| The agent ran, but the run is not in Runs | The key belongs to a different workspace, or you are looking at the shared demo. | Check the workspace name in the top right of the app is the one the key came from. |
Your agent never fails because of Runback: if recording fails, the model call still returns and Runback prints one [runback] line saying why.
| You see | What it means | Fix |
|---|---|---|
[runback] no RUNBACK_API_KEY set — dropped 3 events | The key is not set in the terminal you ran the agent from. | Export RUNBACK_API_KEY in the same terminal (a new tab starts without it), then run the agent again. |
[runback] ingest failed 401 {"error":"Invalid API key"} | Runback rejected the key: mistyped, revoked or expired. | Copy the whole key again, or create a new one in Settings › Keys and access › API key. |
ModuleNotFoundError: No module named 'runback' | The package went into a different Python environment. | Activate the virtual environment you installed into, or install with the same python you run the agent with: python -m pip install …. |
TypeError: "Could not resolve authentication method. Expected one of api_key, auth_token, … | ANTHROPIC_API_KEY is not set. | Run the agent with the key set, e.g. ANTHROPIC_API_KEY=sk-ant-... python agent.py. |
| The agent ran, but the run is not in Runs | The key belongs to a different workspace, or you are looking at the shared demo. | Check the workspace name in the top right of the app is the one the key came from. |
Your agent never fails because of Runback: if recording fails, the model call still returns and Runback prints one [runback] line saying why.
What Runback records
Per call: messages, system prompt, tool definitions, parameters, response text, tool use, stop reason, tokens and latency, for messages.create and messages.stream. Tool results you send back become tool steps paired with the model's call.
wrap_anthropic on anthropic.Anthropic(), messages, scripted model. 5 events, captured 9 Oct 2026.
Runstart · py-anthropic
{ "error": null, "input": "for [redacted:email]", "metadata": {}, "name": "py-anthropic", "output": null, "parent_span_id": null, "phase": "start", "run_id": "01M4G4WC0QX80S2SNE1RJH1KG5", "schema_version": 1, "seq": 0, "span_id": "01M4G4WC0QX80S2SNE1RJH1KG6", "status": "running", "ts_end": null, "ts_start": "2026-10-09T10:55:57.463Z", "type": "run" }Modelclaude-mock-1 → get_weather
{ "error": null, "latency_ms": 55, "metadata": { "framework": "anthropic" }, "model": { "model_id": "claude-mock-1", "provider": "anthropic" }, "parent_span_id": "01M4G4WC0QX80S2SNE1RJH1KG6", "request": { "messages": [ { "content": "Weather in Paris? cc [redacted:email]", "role": "user" } ], "params": { "max_output_tokens": 256 }, "system": "You are a weather bot.", "tools": [ { "description": "Weather for a city", "name": "get_weather", "parameters": { "properties": { "city": { "type": "string" } }, "required": [ "city" ], "type": "object" } } ] }, "response": { "finish_reason": "tool_use", "reasoning": null, "text": null, "tool_calls": [ { "input": { "city": "Paris" }, "tool_call_id": "toolu_1", "tool_name": "get_weather" } ] }, "run_id": "01M4G4WC0QX80S2SNE1RJH1KG5", "schema_version": 1, "seq": 1, "span_id": "01M4G4WECN5KHERP8QJ5AEWW7D", "ts_end": "2026-10-09T10:55:59.893Z", "ts_start": "2026-10-09T10:55:59.838Z", "type": "llm", "usage": { "input_tokens": 12, "output_tokens": 5, "total_tokens": 17 } }Toolget_weather
{ "error": null, "input": { "city": "Paris" }, "latency_ms": null, "output": { "forecast": "sunny" }, "parent_span_id": "01M4G4WECN5KHERP8QJ5AEWW7D", "run_id": "01M4G4WC0QX80S2SNE1RJH1KG5", "schema_version": 1, "seq": 2, "span_id": "01M4G4WEDHWTQG91S7PBGMJDSR", "tool_call_id": "toolu_1", "tool_name": "get_weather", "ts_end": "2026-10-09T10:55:59.921Z", "ts_start": "2026-10-09T10:55:59.920Z", "type": "tool" }Modelclaude-mock-1
{ "error": null, "latency_ms": 2, "metadata": { "framework": "anthropic" }, "model": { "model_id": "claude-mock-1", "provider": "anthropic" }, "parent_span_id": "01M4G4WC0QX80S2SNE1RJH1KG6", "request": { "messages": [ { "content": "Weather in Paris? cc [redacted:email]", "role": "user" }, { "content": [ { "caller": null, "id": "toolu_1", "input": { "city": "Paris" }, "name": "get_weather", "toolset_name": null, "type": "tool_use" } ], "role": "assistant" }, { "content": [ { "content": "{\"forecast\": \"sunny\"}", "tool_use_id": "toolu_1", "type": "tool_result" } ], "role": "tool" } ], "params": { "max_output_tokens": 256 }, "system": null, "tools": [ { "description": "Weather for a city", "name": "get_weather", "parameters": { "properties": { "city": { "type": "string" } }, "required": [ "city" ], "type": "object" } } ] }, "response": { "finish_reason": "end_turn", "reasoning": null, "text": "It is sunny in Paris.", "tool_calls": [] }, "run_id": "01M4G4WC0QX80S2SNE1RJH1KG5", "schema_version": 1, "seq": 3, "span_id": "01M4G4WEDMBHJ5EAAR2YQXVJZB", "ts_end": "2026-10-09T10:55:59.924Z", "ts_start": "2026-10-09T10:55:59.921Z", "type": "llm", "usage": { "input_tokens": 20, "output_tokens": 8, "total_tokens": 28 } }Runend · py-anthropic
{ "error": null, "input": null, "metadata": { "cassette_digest": "9b16481ca418ca70cb443a6ff46d9e989c8c76b3ad10943b1d43d8c7a8891c3b", "cassette_entries": 3, "redaction_by_type": { "email": 3 }, "redaction_count": 3 }, "name": "py-anthropic", "output": "It is sunny in Paris.", "parent_span_id": "01M4G4WC0QX80S2SNE1RJH1KG6", "phase": "end", "run_id": "01M4G4WC0QX80S2SNE1RJH1KG5", "schema_version": 1, "seq": 4, "span_id": "01M4G4WEDMBHJ5EAAR2YQXVJZC", "status": "success", "ts_end": "2026-10-09T10:55:59.924Z", "ts_start": "2026-10-09T10:55:59.924Z", "type": "run" }
The events this integration really sent, redacted in its own process, from a run in Runback's test suite. The model is scripted, so its name and its answers are the test's. The suite re-checks them on every change.
wrapAnthropic on new Anthropic(), messages, scripted model. 5 events, captured 2 Nov 2026.
Runstart · support-agent
{ "schema_version": 1, "run_id": "01M6DEG69TBA43RR308GZ88XHZ", "span_id": "01M6DEG69T6QHFHJT5THBCT9MT", "parent_span_id": null, "seq": 0, "ts_start": "2026-11-02T06:17:44.506Z", "ts_end": null, "type": "run", "phase": "start", "name": "support-agent", "input": "Weather in Paris? cc [redacted:email]", "output": null, "status": "running", "error": null, "metadata": {} }Modelclaude-sonnet-4-6 → get_weather
{ "ts_start": "2026-11-02T06:17:44.507Z", "ts_end": "2026-11-02T06:17:44.510Z", "model": { "provider": "anthropic", "model_id": "claude-sonnet-4-6" }, "request": { "system": null, "messages": [ { "role": "user", "content": "Weather in Paris? cc [redacted:email]" } ], "tools": [ { "name": "get_weather", "description": "Weather for a city", "parameters": { "type": "object", "properties": { "city": { "type": "string" } }, "required": [ "city" ] } } ], "params": { "max_output_tokens": 256 } }, "response": { "text": null, "reasoning": null, "finish_reason": "tool_use", "tool_calls": [ { "tool_call_id": "toolu_1", "tool_name": "get_weather", "input": { "city": "Paris" } } ] }, "usage": { "input_tokens": 12, "output_tokens": 5, "total_tokens": 17 }, "latency_ms": 3, "error": null, "metadata": { "framework": "anthropic" }, "schema_version": 1, "run_id": "01M6DEG69TBA43RR308GZ88XHZ", "span_id": "01M6DEG69Y1A4RFNPQ2RVQ84ER", "parent_span_id": "01M6DEG69T6QHFHJT5THBCT9MT", "seq": 1, "type": "llm" }Toolget_weather
{ "schema_version": 1, "run_id": "01M6DEG69TBA43RR308GZ88XHZ", "span_id": "01M6DEG69YHFTKF1831ZMEJ612", "parent_span_id": "01M6DEG69Y1A4RFNPQ2RVQ84ER", "seq": 2, "ts_start": "2026-11-02T06:17:44.510Z", "ts_end": "2026-11-02T06:17:44.510Z", "type": "tool", "tool_name": "get_weather", "tool_call_id": "toolu_1", "input": { "city": "Paris" }, "output": { "city": "Paris", "forecast": "sunny" }, "latency_ms": null, "error": null }Modelclaude-sonnet-4-6
{ "ts_start": "2026-11-02T06:17:44.510Z", "ts_end": "2026-11-02T06:17:44.510Z", "model": { "provider": "anthropic", "model_id": "claude-sonnet-4-6" }, "request": { "system": null, "messages": [ { "role": "user", "content": "Weather in Paris? cc [redacted:email]" }, { "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_1", "name": "get_weather", "input": { "city": "Paris" } } ] }, { "role": "tool", "content": [ { "type": "tool_result", "tool_use_id": "toolu_1", "content": "{\"city\":\"Paris\",\"forecast\":\"sunny\"}" } ] } ], "tools": [ { "name": "get_weather", "description": "Weather for a city", "parameters": { "type": "object", "properties": { "city": { "type": "string" } }, "required": [ "city" ] } } ], "params": { "max_output_tokens": 256 } }, "response": { "text": "It is sunny in Paris.", "reasoning": null, "finish_reason": "end_turn", "tool_calls": [] }, "usage": { "input_tokens": 20, "output_tokens": 8, "total_tokens": 28 }, "latency_ms": 0, "error": null, "metadata": { "framework": "anthropic" }, "schema_version": 1, "run_id": "01M6DEG69TBA43RR308GZ88XHZ", "span_id": "01M6DEG69YE9044BMNEH111V8G", "parent_span_id": "01M6DEG69T6QHFHJT5THBCT9MT", "seq": 3, "type": "llm" }Runend · support-agent
{ "schema_version": 1, "run_id": "01M6DEG69TBA43RR308GZ88XHZ", "span_id": "01M6DEG69Z0MA8DZTMCSNFF717", "parent_span_id": "01M6DEG69T6QHFHJT5THBCT9MT", "seq": 4, "ts_start": "2026-11-02T06:17:44.511Z", "ts_end": "2026-11-02T06:17:44.511Z", "type": "run", "phase": "end", "name": "support-agent", "input": null, "output": "It is sunny in Paris.", "status": "success", "error": null, "metadata": { "cassette_digest": "4cc37c883ae9a5cf9270fd004f7a51d55dc6761a590bedb095795ddba4d789aa", "cassette_entries": 3, "redaction_count": 3, "redaction_by_type": { "email": 3 }, "truncated_fields": 0 } }
The events this integration really sent, redacted in its own process, from a run in Runback's test suite. The model is scripted, so its name and its answers are the test's. The suite re-checks them on every change.
What it can and cannot do
Anthropic, Python wrapper
| Question | Answer | Why |
|---|---|---|
| Context | Full | Every messages call, streamed or not, with tools and tokens. |
| Tool steps | Inferred | Read from the tool result in the next request, so a tool's latency isn't known. |
| Redaction | In your process | On by default. |
| Stop before it runs | No | The client never runs your tools, so there is no hook to block in. |
| Replay | Re-issue only | A model step can be re-sent to a live model. The whole run can't be re-run with the same tool results: they aren't kept. |
| Signed record | Yes | Each run is hash-chained and can be exported as a signed record. |