Integrations
Agent onboarding
An agent that provisions its own key, its own budget and its own base URL, no console visit.
When the thing integrating is an agent rather than a person, the console is in the way. Two operations are all your backend needs: mint a key under a bundle you allow (that provisions the agent as an end user), and put a ceiling on that key. or pass the ceiling on mint.
The flow
agent your backend seams
───── ──────────── ─────
"provision me" → validate the request
POST /v1/keys → upsert end user + mint ak_ (+ cap)
← { id, key, capMicros }
← { baseUrl, apiKey }
call the base URL ──────────────────────────────→ entitled, routed, cappedThe agent never sees your sk_. Your backend holds it and mints on the agent's behalf, which is what keeps one runaway agent from being able to mint itself a bigger budget.
Provisioning endpoint
- 1
Take the request and decide what it is allowed
The agent identifies itself however your product already does it. You choose the bundle and the ceiling; the agent does not.
// app/api/agents/provision/route.ts export async function POST(req: Request) { const { agentName, ownerId } = await verifiedBody(req) const key = await seams.keys.mint({ endUserId: `${ownerId}:${agentName}`, name: agentName, bundle: "agent-tier", capMicros: "2000000", }) return Response.json({ baseUrl: "https://<app>.gw.ourseams.com/v1", apiKey: key.key, capMicros: key.capMicros, }) }# routes/agents.py @router.post("/agents/provision") async def provision(request: Request): body = await verified_body(request) agent_id = f"{body.owner_id}:{body.agent_name}" key = seams.keys.mint( end_user_id=agent_id, name=body.agent_name, bundle="agent-tier", cap_micros="2000000", # $2.00 ) return { "baseUrl": "https://<app>.gw.ourseams.com/v1", "apiKey": key.key, "capMicros": key.cap_micros, } - 2
The agent stores what it got and calls
Two values are all it needs. OpenAI, Anthropic, and Gemini clients all work from here, same host, same
ak_.pythonimport os, httpx from openai import OpenAI from anthropic import Anthropic from google import genai from google.genai import types as google_types creds = httpx.post( "https://acme.example.com/api/agents/provision", json={"agentName": "nightly-summariser", "ownerId": "org_42"}, ).json() openai = OpenAI(base_url=creds["baseUrl"] + "/v1", api_key=creds["apiKey"]) anthropic = Anthropic(base_url=creds["baseUrl"], api_key=creds["apiKey"]) gemini = genai.Client( api_key=creds["apiKey"], http_options=google_types.HttpOptions(base_url=creds["baseUrl"]), ) - 3
Handle the ceiling
A capped agent gets a 402 with
spend_limit_exceeded. That is backpressure, not a failure: stop the run and let the next window pick it up.pythontry: res = openai.chat.completions.create(model="acme/fast", messages=msgs) except Exception as err: if getattr(err, "status_code", None) == 402: mark_run_capped() # the window will roll else: raise
Rotation
There is no rotate: you mint a new key and revoke the old one, in that order, so the agent is never without one. Because attribution is per end user, the agent's history survives the swap.
seams keys mint --user org_42:nightly-summariser --bundle agent-tier
seams keys revoke key_01J.. --yesconst fresh = await seams.keys.mint({ endUserId: agentId, bundle: "agent-tier" })
await seams.keys.revoke("key_01J...")
// hand fresh.key to the agent; the old one stops working immediatelyfresh = seams.keys.mint(end_user_id=agent_id, bundle="agent-tier")
seams.keys.revoke("key_01J...")
# hand fresh.key to the agent; the old one stops working immediately