Every API has an unspoken rule at the front door: a human signs up first. Fill
in an email, click a verification link, copy a key, paste it into your code
env var. This is all good when humans are driving, but it’s kind of a blocker
when it’s an agent at the wheel.
auth.md, a new open protocol shaped by the
folks at WorkOS, attempts to start the process of removing that wall. It’s a
Markdown file an app hosts at its domain walking an agent through registering
for a credential, claiming it against a real user, and using it against the API.
Think llms.txt or robots.txt, but for “how do I get an API key for this
service.”
- API teams thinking about how AI agents will adopt their product
- Anyone watching the MCP / agent ecosystem and wondering what signup looks like in that world
- Platforms whose users want their agents to act for them without manual key copying
Why agent signup needs a protocol
When an agent (Claude Code, Cursor, opencode, whatever) wants to use an API on a user’s behalf, it has to obtain credentials. Today that means one of:
- The user signs up manually and sets the key up in a way that the agent can use.
- The agent walks the user through a browser-based OAuth dance designed for human screens.
- The user shares a long-lived personal token, a blast-radius problem the moment it leaks.
None scale to “my agent uses fifteen APIs to ship this task.” The agent needs a way to introduce itself, prove who its user is, and walk away with a credential scoped to that user. That’s what auth.md standardises. Managing API keys for AI agents covers the same problem from the operator side.
How agents discover auth.md
Discovery is two hops. An unauthenticated request to the API returns a 401 with
a WWW-Authenticate: Bearer resource_metadata="..." header pointing at the
Protected Resource Metadata document
at /.well-known/oauth-protected-resource on the API host. That document names
the API, its scopes, and the URL of its authorization server. The agent then
fetches /.well-known/oauth-authorization-server from that authorization
server, and finds the agent_auth block that points at the auth.md file and the
registration endpoints:
The auth.md file itself is a procedural document: numbered steps saying “discover the metadata, pick a registration method, register, claim, use the credential, handle revoke.” Written for an agent to read top-to-bottom. A short excerpt from the spec’s own auth.md gives you the flavour:
Imperative voice, conditional branches (“Skip this for…”), field references by exact name. An agent can act on each line.
Crucially, auth.md doesn’t ask you to rip out your existing auth. If you already issue API keys, you keep issuing API keys. The protocol just gives an agent a discoverable, machine-readable way to ask for one.
Agent-verified vs user-claimed flows
The protocol defines two ways an agent can introduce its user:
| Agent verified | User claimed | |
|---|---|---|
| Who vouches | Agent’s identity provider via signed JWT | The user, via OTP |
| Endpoint sequence | register_uri only | register_uri, then claim_uri start + complete |
| Claim mechanism | ID-JAG signature verified against issuer JWKS | Email + one-time password |
| Pre-claim scope | Full scope on success | Reduced scope (spec example: api.read) until OTP completes |
| When to use | Agent platform has a trusted identity provider | No identity provider vouching for anyone |
Agent verified. The agent’s identity provider mints an
ID-JAG,
a JWT-shaped assertion saying “this user is X, audience is your service, signed
by us.” The agent posts it to register_uri. Your service fetches the
provider’s JWKS, verifies the signature, and issues a credential. If the
asserted user has no account, it’s up to you: match an existing account, prompt
to claim afterwards, or refuse.
User claimed. The agent posts an anonymous registration to register_uri
and gets a credential with a reduced “pre-claim” scope. To bind it to a real
user, the agent posts the user’s email to claim_uri, the service emails an
OTP, the human reads it back, the agent posts it to claim_uri again, and the
scope upgrades (the example adds api.write). Slower, but it works with no
provider vouching.
A service can support either or both.
What auth.md means for API providers
Credentials are bound to a user. A post-claim credential is tied to the human the agent acts for, keeping your per-user rate limits, billing, and audit logs intact. The spec doesn’t define a separate agent identity field, so distinct credentials per agent for the same user are your handler’s call, based on ID-JAG issuer or session context. Same problem as provisioning API keys at first login, just triggered by an agent.
The signup form stops being the bottleneck. If an API is good at agentic work but bad at frictionless signup, agents will route around it. A discoverable auth.md is how you stop being the boring step.
You stay in control of scopes. The agent can only request what your auth.md advertises, and you decide which flows you accept. Agent-verified from a trusted provider is a different risk profile than an anonymous claim, and the protocol lets you treat them differently. Until OTP completes, the credential is limited to whatever read-only slice you advertised, so a never-claimed registration is a read-only token sitting unused.
What auth.md isn’t: a replacement for OAuth, a new token format, or WorkOS-specific. The protocol composes existing standards (Protected Resource Metadata, ID-JAGs) and is published on GitHub under MIT.
Implementing auth.md on your gateway
The spec is days old at the time of writing, the reference implementation is a
sample app, the “adopted by” list is short. But agents needing to register with
services on a user’s behalf is a problem getting more real every month, and a
Markdown file at a well-known URL is the kind of low-ceremony primitive the
robots.txt / llms.txt / /.well-known/ lineage suggests can spread.
Implementing it on the Zuplo side doesn’t need a new policy, just a handful of custom request handlers. Discovery endpoints are two small handlers returning the metadata JSON. Registration and claim endpoints are custom request handlers wrapping your existing API key issuance. Revocation hangs off the same path backing your portal’s “delete key” button. The work is shaping your auth surface to match the spec, not building new auth.
Custom request handlers
Wrap your existing API key issuance behind the register_uri, claim_uri, and revocation_uri endpoints with TypeScript request handlers.
If you run an API you’d like agents to use without a human in the loop on every signup, this is the conversation to be in. Read the protocol, look at your own auth flow, and ask: what would my auth.md say?
