Examples
Per-seat agents
One customer, many agents, and a budget that does not let one of them eat the month.
When your product runs agents on a schedule, the risk is not a person typing too much. It is a loop. Give each agent its own end user and its own key, with a cap on the key, so a runaway costs one budget rather than the account.
seams users create app:nightly-summariser --name "app · nightly summariser" --bundle pro
seams keys mint --user app:nightly-summariser --bundle pro --cap 2.00 --label nightly-summariser// one end user per agent, not one per customer
const agentId = `${customer.slug}:nightly-summariser`
await seams.users.create(agentId, { name: `${customer.name} · nightly summariser`, bundle: "pro" })
const key = await seams.keys.mint({ endUserId: agentId, bundle: "pro" })
// $2.00 as micro-dollars, sent as a string
await seams.keys.cap(key.id, { capMicros: "2000000" })# one end user per agent, not one per customer
agent_id = f"{customer.slug}:nightly-summariser"
seams.users.create(agent_id, name=f"{customer.name} · nightly summariser", bundle="pro")
key = seams.keys.mint(end_user_id=agent_id, bundle="pro")
# $2.00 as micro-dollars, sent as a string
seams.keys.cap(key.id, cap_micros="2000000")Give the agent its own end-user key so its spend stays separate from the human's, in usage, margin, and on the portal.
When the cap bites
The agent gets a 402 with the code spend_limit_exceeded. Treat it as backpressure, not an error: stop the run, record it, and try again after the period rolls.
try {
await runAgent(agentKey)
} catch (err) {
if (err.code === "spend_limit_exceeded") {
await db.agentRun.update({ where: { id }, data: { status: "capped" } })
return // the next window will pick it up
}
throw err
}try:
run_agent(agent_key)
except SeamsError as err:
if err.code != "spend_limit_exceeded":
raise
db.agent_run.update(run_id, status="capped")
return # the next window will pick it upFinding the expensive one
Because each agent is its own end user, margin per end user is the answer directly, sort by provider cost and the loop is at the top.
seams margin --tableconst { rows } = await seams.margin()
rows.toSorted((a, b) => Number(BigInt(b.providerCostMicros) - BigInt(a.providerCostMicros)))margin = seams.margin()
sorted(margin.rows, key=lambda row: int(row.provider_cost_micros), reverse=True)