Status: AcceptedArea: BackendDate: 2026-10-16
Context
With agent sign-up (the WorkOS auth.md spec), an AI agent registers itself and gets an identity assertion, a long-lived credential it trades for short-lived access tokens. It builds an organization before any human exists, until a human claims it by signing in and approving it. Until then, something has to own that org and make the API calls, and neither a placeholderUser nor the
Organization subject fits (see Alternatives).
Decision
Before the claim, an agent is anAgent auth subject: a row in one agents table, created at
sign-up, which also holds its OAuth client, its status and who claimed it. It authenticates as
itself, owns exactly one org through an organization_id on its row (not a UserOrganization
membership), and can only reach endpoints whose Authenticator lists Agent in allowed_subjects.
After the claim, it acts on behalf of the claiming user by design, with ordinary user OAuth
tokens: the claim replaces its identity assertion with one that only yields that user’s tokens. Its
OAuth client stays marked as an agent, which is what still identifies it, for example in logs.
The claim, in one transaction: the human becomes the org’s owner, everything issued to the agent
before the claim is revoked, and the agent’s row is marked claimed (by whom and when). The row is
kept, with its organization_id: the token endpoint checks it on every exchange, so revoking the
agent stops it and identity assertions issued before the claim are rejected. The org and its data
stay as they are.
Consequences
- New rule: to open an endpoint to unclaimed agents, add
Agentto that module’sAuthenticator, handleis_agentin the service’s subject branches, and test it with@pytest.mark.auth(AuthSubjectFixture(subject="agent")). Check the existingif is_user(...) elsebranches: code that treats every non-user as anOrganizationwill misread anAgent. - Only endpoints that set up the org’s catalog may accept
Agent: organizations, products, meters, benefits, discounts, events and config. Endpoints that hold buyer data or move money never do: orders, subscriptions, refunds, customers and payouts. Neither do members or webhooks, including config’s webhook section, because a webhook set up before the claim keeps receiving data after it. In production the org can’t go live before the claim; in sandbox it can. - Checkout sessions and checkout links accept
Agent, in sandbox and production alike, so the agent can test a checkout end to end. No per-environment rule is needed: a checkout only takes payment once the org is approved, and in production that can’t happen before the claim. - Unclaimed agents can’t create organization access tokens: one would be an
Organizationcaller that skips this allowlist and keeps working after the claim. App-side testing that needs one waits for the claim. - The route allowlist is the guard: a test pins the routes that accept
Agent, so every new opt-in shows up in review. Unclaimed agent tokens also carry only the scopes of the allowed resources (products:write,meters:write, …), which blocks a wrong opt-in on a resource with its own scope, such as refunds. It doesn’t help where scopes are shared: the payout-account, member-invite and review-submission routes needorganizations:write, like editing the org itself, so only the allowlist keeps agents out of them. - No placeholder users: the users table, login, emails and user metrics only ever see people, and nothing moves between users at the claim.
- The org-access helpers and authz policies resolve an
Agentto its one org, so it behaves like a single-organization credential. Inside that org it has its own role in ADR-0005’s role layer (ROLE_PERMISSIONS): org settings and build resources, never members or finance. An agent that wants a second org signs up again; parallel sessions are separate agents. - An org owned by an agent has no human owner until the claim. Code that looks up the owner must handle that.
- OAuth tokens, grants and authorization codes gain a third sub type,
SubType.agent. - Accepted trade-off: a new kind of caller across the auth model. Every authz path and subject branch
must account for
Agent, and the agent’sorganization_idis a second ownership path next to memberships.
Alternatives considered
- Placeholder
Userwith an unreachable email: no new subject, but non-people in the users table and guards on login, emails, metrics and every endpoint that must stay off-limits. - Reuse the
Organizationsubject (like organization access tokens): no new subject, but agents aren’t distinguishable from org tokens, so agent rules can’t be expressed on endpoints. Agentsubject after the claim too: every user endpoint would need to opt in for the agent to act for its user. Delegated user tokens already cover that.

