Docs Integrations
Vercel AI SDK
Wrap the model and tools you pass to generateText or streamText to record every step, and let a policy stop a tool before it runs. 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) and the AI SDK (ai) v6. - An OpenAI API key (the example uses
@ai-sdk/openai; any AI SDK provider works).
Steps
Install the SDK
terminalShell npm install @runback/sdk ai @ai-sdk/openai zodExpected outputnpm finishes without errors.aiis a peer dependency you may already have.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 OPENAI_API_KEY="sk-..."Expected outputNothing is printed.echo $RUNBACK_API_KEYshould print your key.Wrap the model and tools, then run it
dbg.modelrecords each model call, streamed or not;dbg.tools(...)records each tool call and is where a policy can stop one before it runs. Save this asagent.mjsand runnode agent.mjs.agent.mjsJavaScript // agent.mjs: npm install @runback/sdk ai @ai-sdk/openai zod import { withDebugger } from "@runback/sdk"; import { generateText, tool, stepCountIs } from "ai"; import { openai } from "@ai-sdk/openai"; import { z } from "zod"; const task = "What's the weather in Paris?"; const dbg = withDebugger(openai("gpt-4o-mini"), { runName: "weather-agent", input: task }); const res = await generateText({ model: dbg.model, tools: dbg.tools({ get_weather: tool({ description: "Weather for a city", inputSchema: z.object({ city: z.string() }), execute: async ({ city }) => ({ city, forecast: "sunny" }), }), }), stopWhen: stepCountIs(3), prompt: task, }); await dbg.finish({ output: res.text, status: "success" }); console.log(res.text);Expected outputThe agent's answer 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
weather-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. |
OpenAIError: Missing credentials | OPENAI_API_KEY (or your provider's key) is not set. | Run the agent with the provider key set, e.g. OPENAI_API_KEY=sk-... 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.
What Runback records
withDebugger around the Vercel AI SDK's generateText, scripted model, one tool. 5 events, captured 19 Oct 2026.
Runstart · weather-agent
{ "schema_version": 1, "run_id": "01M59REJ7HE9EBFAT3KA2YJ22R", "span_id": "01M59REJ7JWGK5ZJ6RWWPAJ7ZK", "parent_span_id": null, "seq": 0, "ts_start": "2026-10-19T09:38:57.394Z", "ts_end": null, "type": "run", "phase": "start", "name": "weather-agent", "input": "Weather in Paris? cc [redacted:email]", "output": null, "status": "running", "error": null, "metadata": {} }Modelgpt-4o-mini → get_weather
{ "ts_start": "2026-10-19T09:38:57.430Z", "ts_end": "2026-10-19T09:38:57.430Z", "model": { "provider": "openai", "model_id": "gpt-4o-mini" }, "request": { "system": "You are a weather bot.", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Weather in Paris? cc [redacted:email]" } ] } ], "tools": [ { "name": "get_weather", "description": "Weather for a city", "parameters": { "type": "object", "properties": { "city": { "type": "string" } }, "required": [ "city" ], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" } } ], "params": {} }, "response": { "text": null, "reasoning": null, "finish_reason": "tool-calls", "tool_calls": [ { "tool_call_id": "call_1", "tool_name": "get_weather", "input": { "city": "Paris" } } ] }, "usage": { "input_tokens": 12, "output_tokens": 5, "total_tokens": 17 }, "latency_ms": 0, "error": null, "schema_version": 1, "run_id": "01M59REJ7HE9EBFAT3KA2YJ22R", "span_id": "01M59REJ8PJA1RY80H1W2769SY", "parent_span_id": "01M59REJ7JWGK5ZJ6RWWPAJ7ZK", "seq": 1, "type": "llm" }Toolget_weather
{ "schema_version": 1, "run_id": "01M59REJ7HE9EBFAT3KA2YJ22R", "span_id": "01M59REJ8S94YYN6AWFMYKKKK2", "parent_span_id": "01M59REJ8PJA1RY80H1W2769SY", "seq": 2, "ts_start": "2026-10-19T09:38:57.433Z", "ts_end": "2026-10-19T09:38:57.433Z", "type": "tool", "tool_name": "get_weather", "tool_call_id": "call_1", "input": { "city": "Paris" }, "output": { "city": "Paris", "forecast": "sunny" }, "latency_ms": 0, "error": null }Modelgpt-4o-mini
{ "ts_start": "2026-10-19T09:38:57.434Z", "ts_end": "2026-10-19T09:38:57.434Z", "model": { "provider": "openai", "model_id": "gpt-4o-mini" }, "request": { "system": "You are a weather bot.", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Weather in Paris? cc [redacted:email]" } ] }, { "role": "assistant", "content": [ { "type": "tool-call", "toolCallId": "call_1", "toolName": "get_weather", "input": { "city": "Paris" } } ] }, { "role": "tool", "content": [ { "type": "tool-result", "toolCallId": "call_1", "toolName": "get_weather", "output": { "type": "json", "value": { "city": "Paris", "forecast": "sunny" } } } ] } ], "tools": [ { "name": "get_weather", "description": "Weather for a city", "parameters": { "type": "object", "properties": { "city": { "type": "string" } }, "required": [ "city" ], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" } } ], "params": {} }, "response": { "text": "It is sunny in Paris.", "reasoning": null, "finish_reason": "stop", "tool_calls": [] }, "usage": { "input_tokens": 20, "output_tokens": 7, "total_tokens": 27 }, "latency_ms": 0, "error": null, "schema_version": 1, "run_id": "01M59REJ7HE9EBFAT3KA2YJ22R", "span_id": "01M59REJ8T86BGPWDR3DYP9N58", "parent_span_id": "01M59REJ7JWGK5ZJ6RWWPAJ7ZK", "seq": 3, "type": "llm" }Runend · weather-agent
{ "schema_version": 1, "run_id": "01M59REJ7HE9EBFAT3KA2YJ22R", "span_id": "01M59REJ8V9Z66RXMXBQ9WP97G", "parent_span_id": "01M59REJ7JWGK5ZJ6RWWPAJ7ZK", "seq": 4, "ts_start": "2026-10-19T09:38:57.435Z", "ts_end": "2026-10-19T09:38:57.435Z", "type": "run", "phase": "end", "name": "weather-agent", "input": null, "output": "It is sunny in Paris.", "status": "success", "error": null, "metadata": { "cassette_digest": "d2556ed7eba1ac4ec6c06303f0cbc1b609955ed2ea5ab5b698f00190f19f178b", "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
Vercel AI SDK
| Question | Answer | Why |
|---|---|---|
| Context | Full | System prompt, messages, tools and parameters, streamed or not. |
| Redaction | In your process | On by default, before anything is buffered or sent. |
| Stop before it runs | Yes | A policy rule is checked before a wrapped tool executes. |
| Replay | Whole run | Every tool result is kept. Re-running the run with those results held fixed is on the Enterprise plan. |
| Signed record | Yes | Each run is hash-chained and can be exported as a signed record. |