Docs menuOpenAI

Docs Integrations

OpenAI

Wrap the OpenAI client, in TypeScript or Python, to record every chat.completions and responses call. 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 OpenAI API key.

Steps

  1. Install the SDK and the OpenAI client

    terminalShell
    npm install @runback/sdk openai
    terminalShell
    pip install "runback-sdk[openai]"
    Expected output
    The install finishes without errors.
  2. 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 output
    Nothing is printed. echo $RUNBACK_API_KEY should print your key.
  3. Wrap the client and run it

    Wrap your existing client once; every call through it is recorded. Save this as agent.mjs and run node agent.mjsagent.py and run python agent.py.

    agent.mjsJavaScript
    // agent.mjs: npm install @runback/sdk openai
    import OpenAI from "openai";
    import { startRun } from "@runback/sdk/core";
    import { wrapOpenAI } from "@runback/sdk/openai";
    
    const task = "Say hello in three words.";
    const run = startRun({ runName: "support-agent", input: task });
    const openai = wrapOpenAI(new OpenAI(), run); // chat.completions and responses
    
    const res = await openai.responses.create({ model: "gpt-4o-mini", input: task });
    
    await run.finish({ output: res.output_text, status: "success" });
    console.log(res.output_text);
    agent.pyPython
    # agent.py: pip install "runback-sdk[openai]"
    import openai
    from runback import Collector
    from runback.openai import wrap_openai
    
    task = "Say hello in three words."
    run = Collector(run_name="support-agent", input=task)
    client = wrap_openai(openai.OpenAI(), run)  # or openai.AsyncOpenAI()
    
    res = client.responses.create(model="gpt-4o-mini", input=task)
    
    run.finish(output=res.output_text)
    print(res.output_text)
    Expected output
    Your agent's normal output and no [runback] line. A [runback] line means the run was not sent; see Troubleshooting.
  4. 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 seeWhat it meansFix
[runback] no RUNBACK_API_KEY set — dropped 3 eventsThe 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 credentialsOPENAI_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 RunsThe 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 seeWhat it meansFix
[runback] no RUNBACK_API_KEY set — dropped 3 eventsThe 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 ….
openai.OpenAIError: Missing credentialsOPENAI_API_KEY (or your provider's key) is not set.Run the agent with the provider key set, e.g. OPENAI_API_KEY=sk-... python agent.py.
The agent ran, but the run is not in RunsThe 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 calls, finish reason, tokens and latency, streamed or not. Errors are recorded, then re-thrown. For streams, pass stream_options: { include_usage: true } or the stream carries no token counts.

wrap_openai on openai.OpenAI(), chat.completions, scripted model. 5 events, captured 9 Oct 2026.

  1. Runstart · py-openai-chat
    {
      "error": null,
      "input": "weather for [redacted:email]",
      "metadata": {},
      "name": "py-openai-chat",
      "output": null,
      "parent_span_id": null,
      "phase": "start",
      "run_id": "01M4G4WRPYVWDE9VFXK1TRYWBD",
      "schema_version": 1,
      "seq": 0,
      "span_id": "01M4G4WRPYVWDE9VFXK1TRYWBE",
      "status": "running",
      "ts_end": null,
      "ts_start": "2026-10-09T10:56:10.462Z",
      "type": "run"
    }
  2. Modelgpt-4o-mini → get_weather17 tokens · 3 ms · called a tool
    {
      "error": null,
      "latency_ms": 3,
      "metadata": {
        "framework": "openai"
      },
      "model": {
        "model_id": "gpt-4o-mini",
        "provider": "openai"
      },
      "parent_span_id": "01M4G4WRPYVWDE9VFXK1TRYWBE",
      "request": {
        "messages": [
          {
            "content": "Weather in Paris? cc [redacted:email]",
            "role": "user"
          }
        ],
        "params": {
          "temperature": 0.2
        },
        "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_calls",
        "reasoning": null,
        "text": null,
        "tool_calls": [
          {
            "input": {
              "city": "Paris"
            },
            "tool_call_id": "call_25",
            "tool_name": "get_weather"
          }
        ]
      },
      "run_id": "01M4G4WRPYVWDE9VFXK1TRYWBD",
      "schema_version": 1,
      "seq": 1,
      "span_id": "01M4G4WRQ706J0WTYTRYZV2J15",
      "ts_end": "2026-10-09T10:56:10.471Z",
      "ts_start": "2026-10-09T10:56:10.468Z",
      "type": "llm",
      "usage": {
        "input_tokens": 12,
        "output_tokens": 5,
        "total_tokens": 17
      }
    }
  3. Toolget_weather
    {
      "error": null,
      "input": {
        "city": "Paris"
      },
      "latency_ms": null,
      "output": {
        "city": "Paris",
        "forecast": "sunny"
      },
      "parent_span_id": "01M4G4WRQ706J0WTYTRYZV2J15",
      "run_id": "01M4G4WRPYVWDE9VFXK1TRYWBD",
      "schema_version": 1,
      "seq": 2,
      "span_id": "01M4G4WRQ8Z2500Q02FQ3B3WEV",
      "tool_call_id": "call_25",
      "tool_name": "get_weather",
      "ts_end": "2026-10-09T10:56:10.472Z",
      "ts_start": "2026-10-09T10:56:10.472Z",
      "type": "tool"
    }
  4. Modelgpt-4o-mini28 tokens · 2 ms · finished
    {
      "error": null,
      "latency_ms": 2,
      "metadata": {
        "framework": "openai"
      },
      "model": {
        "model_id": "gpt-4o-mini",
        "provider": "openai"
      },
      "parent_span_id": "01M4G4WRPYVWDE9VFXK1TRYWBE",
      "request": {
        "messages": [
          {
            "content": "Weather in Paris? cc [redacted:email]",
            "role": "user"
          },
          {
            "content": [
              {
                "input": {
                  "city": "Paris"
                },
                "toolCallId": "call_25",
                "toolName": "get_weather",
                "type": "tool-call"
              }
            ],
            "role": "assistant"
          },
          {
            "content": [
              {
                "output": {
                  "city": "Paris",
                  "forecast": "sunny"
                },
                "toolCallId": "call_25",
                "type": "tool-result"
              }
            ],
            "role": "tool"
          }
        ],
        "params": {},
        "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": "stop",
        "reasoning": null,
        "text": "It is sunny in Paris.",
        "tool_calls": []
      },
      "run_id": "01M4G4WRPYVWDE9VFXK1TRYWBD",
      "schema_version": 1,
      "seq": 3,
      "span_id": "01M4G4WRQAEXKRV66F0YVMZYZ8",
      "ts_end": "2026-10-09T10:56:10.474Z",
      "ts_start": "2026-10-09T10:56:10.472Z",
      "type": "llm",
      "usage": {
        "input_tokens": 20,
        "output_tokens": 8,
        "total_tokens": 28
      }
    }
  5. Runend · py-openai-chatsucceeded
    {
      "error": null,
      "input": null,
      "metadata": {
        "cassette_digest": "ded351c0b41d719589dcdf05f870d1037028220907a85c38cd55168480bce9f7",
        "cassette_entries": 3,
        "redaction_by_type": {
          "email": 3
        },
        "redaction_count": 3
      },
      "name": "py-openai-chat",
      "output": "It is sunny in Paris.",
      "parent_span_id": "01M4G4WRPYVWDE9VFXK1TRYWBE",
      "phase": "end",
      "run_id": "01M4G4WRPYVWDE9VFXK1TRYWBD",
      "schema_version": 1,
      "seq": 4,
      "span_id": "01M4G4WRQAEXKRV66F0YVMZYZ9",
      "status": "success",
      "ts_end": "2026-10-09T10:56:10.474Z",
      "ts_start": "2026-10-09T10:56:10.474Z",
      "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.

wrapOpenAI on new OpenAI(), responses, scripted model. 5 events, captured 2 Nov 2026.

  1. Runstart · support-agent
    {
      "schema_version": 1,
      "run_id": "01M6DEG69951BRBTJGZ62C7E1D",
      "span_id": "01M6DEG699VM3HRKT87ZCXQWM5",
      "parent_span_id": null,
      "seq": 0,
      "ts_start": "2026-11-02T06:17:44.489Z",
      "ts_end": null,
      "type": "run",
      "phase": "start",
      "name": "support-agent",
      "input": "Weather in Paris? cc [redacted:email]",
      "output": null,
      "status": "running",
      "error": null,
      "metadata": {}
    }
  2. Modelgpt-4o-mini → get_weather17 tokens · 5 ms · called a tool
    {
      "ts_start": "2026-11-02T06:17:44.491Z",
      "ts_end": "2026-11-02T06:17:44.496Z",
      "model": {
        "provider": "openai",
        "model_id": "gpt-4o-mini"
      },
      "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": {}
      },
      "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": 5,
      "error": null,
      "metadata": {
        "framework": "openai"
      },
      "schema_version": 1,
      "run_id": "01M6DEG69951BRBTJGZ62C7E1D",
      "span_id": "01M6DEG69GQMGMZH7DFVY6CCGX",
      "parent_span_id": "01M6DEG699VM3HRKT87ZCXQWM5",
      "seq": 1,
      "type": "llm"
    }
  3. Toolget_weather
    {
      "schema_version": 1,
      "run_id": "01M6DEG69951BRBTJGZ62C7E1D",
      "span_id": "01M6DEG69HFNT53A9PDG6PNVRG",
      "parent_span_id": "01M6DEG69GQMGMZH7DFVY6CCGX",
      "seq": 2,
      "ts_start": "2026-11-02T06:17:44.496Z",
      "ts_end": "2026-11-02T06:17:44.497Z",
      "type": "tool",
      "tool_name": "get_weather",
      "tool_call_id": "call_1",
      "input": {
        "city": "Paris"
      },
      "output": {
        "city": "Paris",
        "forecast": "sunny"
      },
      "latency_ms": null,
      "error": null
    }
  4. Modelgpt-4o-mini28 tokens · 1 ms · finished
    {
      "ts_start": "2026-11-02T06:17:44.496Z",
      "ts_end": "2026-11-02T06:17:44.497Z",
      "model": {
        "provider": "openai",
        "model_id": "gpt-4o-mini"
      },
      "request": {
        "system": null,
        "messages": [
          {
            "role": "user",
            "content": "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",
                "output": {
                  "city": "Paris",
                  "forecast": "sunny"
                }
              }
            ]
          }
        ],
        "tools": [
          {
            "name": "get_weather",
            "description": "Weather for a city",
            "parameters": {
              "type": "object",
              "properties": {
                "city": {
                  "type": "string"
                }
              },
              "required": [
                "city"
              ]
            }
          }
        ],
        "params": {}
      },
      "response": {
        "text": "It is sunny in Paris.",
        "reasoning": null,
        "finish_reason": "completed",
        "tool_calls": []
      },
      "usage": {
        "input_tokens": 20,
        "output_tokens": 8,
        "total_tokens": 28
      },
      "latency_ms": 1,
      "error": null,
      "metadata": {
        "framework": "openai"
      },
      "schema_version": 1,
      "run_id": "01M6DEG69951BRBTJGZ62C7E1D",
      "span_id": "01M6DEG69HP9SCZA801GKAGTJK",
      "parent_span_id": "01M6DEG699VM3HRKT87ZCXQWM5",
      "seq": 3,
      "type": "llm"
    }
  5. Runend · support-agentsucceeded
    {
      "schema_version": 1,
      "run_id": "01M6DEG69951BRBTJGZ62C7E1D",
      "span_id": "01M6DEG69JQDF81QVJRQYGAQ6W",
      "parent_span_id": "01M6DEG699VM3HRKT87ZCXQWM5",
      "seq": 4,
      "ts_start": "2026-11-02T06:17:44.498Z",
      "ts_end": "2026-11-02T06:17:44.498Z",
      "type": "run",
      "phase": "end",
      "name": "support-agent",
      "input": null,
      "output": "It is sunny in Paris.",
      "status": "success",
      "error": null,
      "metadata": {
        "cassette_digest": "243799279d37043958e3c3b08ae9be720adcef70a75fce406151ce4455808d74",
        "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

OpenAI, Python wrapper

QuestionAnswerWhy
ContextFullEvery chat.completions and responses call, streamed or not, with tools and tokens.
Tool stepsInferredRead from the tool result in the next request, so a tool's latency isn't known.
RedactionIn your processOn by default.
Stop before it runsNoThe client never runs your tools, so there is no hook to block in.
ReplayRe-issue onlyA 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 recordYesEach run is hash-chained and can be exported as a signed record.

Next steps

  • See what a run holds: Capture.
  • Stop a risky tool call before it runs: Control.
Last updated 2 Nov 2026.