Skip to content

Agents quickstart

It’s self-serve and free. Start free in the console: enter your work email and the code we send, name your company, then register your agent with a name, the company that runs it, and an icon. Nobody reviews it first. The client ID is issued on the spot: it’s your agent’s public name at Bage, the sub of every call it makes and the client_id in every token, so sites can tell which agent came. The console makes a key pair in your browser and gives you the private half once, as a key file:

bage-agent-key.json
{
"issuer": "https://api.trybage.com",
"client_id": "agt_…",
"private_jwk": { "kty": "OKP", "crv": "Ed25519", "x": "…", "d": "…" }
}

Keep it secret, like any API key. Bage only stores the public half. If you rotate the key, the old one stops working at once.

Terminal window
npm install @trybage/sdk
agent.ts
// npm install @trybage/sdk
import { BageAgent } from "@trybage/sdk/agent";
// The key file you downloaded from the console: { "issuer", "client_id", "private_jwk" }
const { issuer, client_id, private_jwk } = JSON.parse(process.env.BAGE_AGENT_KEY ?? "{}");
const agent = new BageAgent({ issuer, clientId: client_id, privateJwk: private_jwk });
// Asks the person, then waits until they answer (up to 5 minutes).
const session = await agent.signIn({
site: process.env.SITE ?? "https://shop.example",
loginHint: process.env.PERSON ?? "alex@example.com",
scopes: ["browse", "cart"],
message: "Reorder this week's groceries", // shown on the approval screen
onPending: () => console.log("Waiting for the person to approve…"),
});
// Every request carries the agent token and a signature made with your key.
const products = await session.fetch("/products");
console.log(products.status, JSON.stringify(products.body));

signIn reads the site’s /.well-known/agent-login to check that it accepts Bage, then waits up to 5 minutes for the person. If they chose Always before, it returns at once. Tokens last 15 minutes, and session.fetch() refreshes them by itself for as long as the sign-in lasts. To hear back instead of waiting, to keep a session across restarts, or to sign the person in next time without their email, see Long sessions. Driving a browser? See Browser agents.

Ask only for scopes the site allows, or the request is refused with invalid_scope. Each site picks its own: Bage’s standard ones and scopes it defined itself, like returns. GET https://api.trybage.com/v1/sites?origin=https://shop.example lists them, its own with a title and description; the site’s /.well-known/agent-login points there as scopes_endpoint.

Without the SDK: every call carries a client_assertion, a JWT signed with your private key. Its header has typ: "client-assertion+jwt". Its claims are iss and sub (your client ID), aud (the full endpoint URL), exp (at most 5 minutes out) and a jti used only once. Send the token to the site in the agent-token header. Sign each request with Web Bot Auth (tag="web-bot-auth"), covering @method, @authority, @path and agent-token. The “Python, no SDK” sample does all of this.

New agents start in test mode. A test agent can only ask its own team and three test people who answer by themselves:

login_hint Answer
approve@test.trybage.com Approves at once
deny@test.trybage.com Denies at once (access_denied)
wait@test.trybage.com Never answers (authorization_pending, then expired_token)

Test tokens carry "test": true, and websites refuse them unless they turn on allowTestAgents, which is meant for staging. When you’re ready, make the agent live in the console yourself. There’s no review. To stop abuse, one person can register up to 20 agents an hour.