Agents
Agent runs
Send two headers with your agent's requests and Leanroute groups them into runs: how many runs each agent made, what each run cost, which models it used and which tools it called. Nothing to install, and the gateway stays stateless: your agent keeps its own state and sends an id.
The headers
| Header | What it means | If absent |
|---|---|---|
X-Agent-Name | Which agent sent the request, e.g. support-bot. | The API key's name from your dashboard. |
X-Agent-Session-Id | Which run the request belongs to. Use one id per run (a UUID is fine). Every request with the same id is one run. | Claude Code's session id if present; otherwise not part of a run. |
X-Agent-Id | Optional. A subagent within the run. | Claude Code's agent id if present. |
X-Leanroute-Client | Optional. The integration sending the request, e.g. langgraph/0.4. | Not recorded. |
X-Leanroute-Agent-Name, X-Leanroute-Session-Id and X-Leanroute-Agent-Id work as aliases. Values are trimmed to 128 characters. The headers work on /v1/chat/completions and /anthropic/v1/messages, streaming or not.
Agent names and session ids show up in your dashboard, so don't put personal data (emails, user names) in them.
What is recorded
For every request, alongside the usual model, tokens, cost and latency:
- The agent name, run id, subagent id and client, from the headers above.
- Tools the gateway ran. When a request uses
mcp_servers, each tool call the gateway made: tool name, MCP server, whether it succeeded, and how long it took. - Tools the model asked your agent to run. The names in
tool_callson the response, streamed or not.
Tool arguments and tool results are never stored, only names and timings. Prompts and completions are handled as they are for every other request.
Where to see it
- Dashboard: Agent runs shows runs, agents, cost per run and top tools, with a page per run listing every request and tool call.
- API (admin key,
gw_admin_*):
# Runs in the last 7 days, newest first (?from, ?to, ?agent, ?limit, ?before)
curl https://api.leanroute.dev/platform/v1/runs?agent=support-bot \
-H "Authorization: Bearer $LEANROUTE_ADMIN_KEY"
# One run: every request, models, cost, and a per-tool rollup
curl https://api.leanroute.dev/platform/v1/runs/RUN_ID \
-H "Authorization: Bearer $LEANROUTE_ADMIN_KEY"- MCP: the hosted Leanroute MCP server has
list_runsandget_runtools, so a coding agent can answer "what did my agents spend today?" itself.
Discovering MCP servers
An agent can ask which MCP servers it is allowed to attach, instead of having URLs pasted into its config. The list is your organization's MCP allowlist; only enabled entries are returned.
curl https://api.leanroute.dev/v1/mcp/servers \
-H "Authorization: Bearer $LEANROUTE_API_KEY"
{"object":"list","data":[
{"object":"mcp_server","type":"url","name":"github","url":"https://mcp.example.com/github"}
]}URLs come back without query strings or credentials. Pass any token in the entry's authorization_token when you use it in mcp_servers. The same list is available as the list_mcp_servers MCP tool.
Recipes
OpenAI SDK (Python)
import os, uuid
from openai import OpenAI
client = OpenAI(
base_url="https://api.leanroute.dev/v1",
api_key=os.environ["LEANROUTE_API_KEY"],
default_headers={"X-Agent-Name": "support-bot"},
)
run_id = str(uuid.uuid4()) # one per run
client.chat.completions.create(
model="deepseek/deepseek-v4-1-flash",
messages=[{"role": "user", "content": "..."}],
extra_headers={"X-Agent-Session-Id": run_id},
)OpenAI SDK (TypeScript)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.leanroute.dev/v1",
apiKey: process.env.LEANROUTE_API_KEY,
defaultHeaders: { "X-Agent-Name": "support-bot" },
});
const runId = crypto.randomUUID(); // one per run
await client.chat.completions.create(
{ model: "deepseek/deepseek-v4-1-flash", messages },
{ headers: { "X-Agent-Session-Id": runId } },
);LangChain / LangGraph (Python)
import os
from langchain_openai import ChatOpenAI
def model_for_run(run_id: str) -> ChatOpenAI:
return ChatOpenAI(
model="deepseek/deepseek-v4-1-flash",
base_url="https://api.leanroute.dev/v1",
api_key=os.environ["LEANROUTE_API_KEY"],
default_headers={
"X-Agent-Name": "research-agent",
"X-Agent-Session-Id": run_id,
"X-Leanroute-Client": "langgraph",
},
)Vercel AI SDK
import { createOpenAI } from "@ai-sdk/openai";
import { generateText } from "ai";
const leanroute = createOpenAI({
baseURL: "https://api.leanroute.dev/v1",
apiKey: process.env.LEANROUTE_API_KEY,
headers: { "X-Agent-Name": "support-bot" },
});
await generateText({
model: leanroute("deepseek/deepseek-v4-1-flash"),
prompt,
headers: { "X-Agent-Session-Id": runId },
});Claude Code
Claude Code already sends its session id, so each session is a run with no setup. To name it, add a header through Claude Code's ANTHROPIC_CUSTOM_HEADERS setting:
export ANTHROPIC_BASE_URL=https://api.leanroute.dev/anthropic
export ANTHROPIC_CUSTOM_HEADERS="X-Agent-Name: claude-code"Good to know
- A run has no start or end call. It begins with the first request carrying its id, and its duration is first to last request.
- Session ids are scoped to your organization. Two organizations using the same id never see each other's runs.
- Requests without a session id still count toward the agent (by name) on the dashboard, just not toward any run.
- Access control is unchanged: the MCP allowlist, spend caps and guardrails apply to every request, agent or not.