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

text
  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, capped

The 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. 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,
      })
    }
  2. 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_.

    python
    import 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. 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.

    python
    try:
        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.. --yes