Docs
Connect an agent, watch it with your team, and let its fixes become lessons. Most agents need one line.
Quickstart
- Sign in and add an agent under Agents. You get a key starting
oo_. - Point your model client at DoubleOh and send the key in
x-doubleoh-key. Your provider key stays where it is. - Run your agent. It shows up in Live; actions that change something wait for your team.
import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://api.doubleoh.ai/v1/proxy/anthropic", // was https://api.anthropic.com defaultHeaders: { "x-doubleoh-key": process.env.DOUBLEOH_API_KEY, "x-doubleoh-agent": "ap-clerk", "x-doubleoh-read-only": "read_purchase_order,read_receipt", "x-doubleoh-irreversible": "confirm_bill", }, });
import os from openai import OpenAI client = OpenAI( base_url="https://api.doubleoh.ai/v1/proxy/openai", # was https://api.openai.com/v1 default_headers={ "x-doubleoh-key": os.environ["DOUBLEOH_API_KEY"], "x-doubleoh-agent": "ap-clerk", "x-doubleoh-irreversible": "confirm_bill", }, )
That's the whole integration. Every tool call the model asks for becomes a step in the run's live room, and the team's saved lessons for this agent are added to its system prompt.
Try an agent, no code
In the portal, Try an agent lets you test before you integrate:
- Build one here: a name, instructions in plain words, and a practice copy to work on.
- Connect your own: your agent's AG-UI address and, if it needs one, a header such as
Authorization(stored encrypted).
Each message is a practice run. Held actions appear right in the chat, with the agent's screen next to them.
Model proxy
The proxy speaks each provider's own API, so your SDK, streaming and tool format don't change.
| Provider | Base URL |
|---|---|
| Anthropic | https://api.doubleoh.ai/v1/proxy/anthropic |
| OpenAI and compatible | https://api.doubleoh.ai/v1/proxy/openai |
For other OpenAI-compatible providers, add x-doubleoh-upstream: gemini, mistral, groq, together, openrouter or deepseek.
Headers
| Header | What it does |
|---|---|
x-doubleoh-key | Your DoubleOh key. Required. Never sent in Authorization, which stays your provider's. |
x-doubleoh-agent | Which agent is calling. Lessons and trust are kept per agent and task. |
x-doubleoh-read-only | Tools that only read, comma separated. Never held. |
x-doubleoh-irreversible | Tools that can't be undone. Held until supervised trust, and checked against your rules. |
x-doubleoh-run | Optional run id, to group calls into one run yourself. |
x-doubleoh-upstream | Optional OpenAI-compatible provider name. |
Guarded tools
Wrap the function itself, so it asks first whatever the model does. A refused call never reaches your code; a corrected one runs with the team's arguments.
import { DoubleOh, guard } from "@doubleoh/sdk"; const doubleoh = new DoubleOh(); // reads DOUBLEOH_API_KEY const refund = guard(doubleoh, issueRefund, { tool: "refund", action: "Refund order {orderId}", amount: (r) => ({ amountCents: r.cents, currency: "USD" }), }); await refund({ orderId: "4821", cents: 48000 });
Python: pip install doubleoh. TypeScript: npm i @doubleoh/sdk.
MCP
Agents that use MCP servers can put DoubleOh in front of them with doubleoh-mcp guard, so the same rules and holds apply to MCP tools. Install with npm i -g doubleoh-mcp.
Runs and notes
The proxy opens runs for you. With the SDK you can open them yourself and read your team's notes between steps:
await doubleoh.startRun({ task: "Enter Gemini Furniture invoice GF-0950", agent: "ap-clerk", environment: "practice", onNote: (note) => plan.push(note.text), // "Over 30 days goes to a person" }); await doubleoh.step("Read purchase order P00002"); await doubleoh.endRun();
Holds
When an action is held, your team sees what it would do and answers in the live room or the chat:
- Approve: it goes ahead as the agent asked.
- Fix it / Take over: it goes ahead with the person's values. Amounts can be lowered, not raised.
- Undo: it doesn't happen, and the agent is told why.
Fixes and undos draft a lesson.
Lessons
A lesson is one sentence a person wrote or approved, kept per agent and task. Saved lessons go into the agent's prompt through the proxy, and through skillsFor for any other setup:
const lessons = await doubleoh.skillsFor("enter vendor bill", { agent: "ap-clerk" });
Edit, keep or retire them on the Lessons page. They're yours: never shared with other customers, and exportable.
Trust levels
| Level | What waits for a person |
|---|---|
| Watched | Every change. Where every task starts. |
| Supervised | Only what can't be undone. Suggested after 10 clean runs. |
| Trusted | Nothing, rules aside. Suggested after 25 clean runs at Supervised. |
A person always clicks the promotion. Any undo or fix drops the task one level at once. Trust is kept per task and per amount size.
Rules
Rules are sentences: what an agent may do, what must ask a person, and what it must never do. They're checked on every action, at every trust level, and every decision lands in the ledger.
Self-host
One container carries the app, the API, the browser your agents drive and its own database.
git clone https://github.com/cheedli/doubleoh.git && cd doubleoh cp .env.example .env # fill it in docker compose up -d # open http://localhost:3001
Source available under the Sustainable Use License: free to run for your own agents.