Zuplo
AI

How auth.md Standardises Agent Signup

Martyn DaviesMartyn Davies
May 28, 2026
6 min read

auth.md is a new open protocol that gives agents a standard way to register with an API on a user's behalf, no human filling out a signup form. Here's how it works and why it matters.

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.”

Best for:
  • 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:

JSONjson
{
  "agent_auth": {
    "skill": "https://service.com/auth.md",
    "register_uri": "https://auth.service.com/agent/auth",
    "claim_uri": "https://auth.service.com/agent/auth/claim",
    "revocation_uri": "https://auth.service.com/agent/auth/revoke",
    "identity_types_supported": ["anonymous", "identity_assertion"]
  }
}

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:

Markdownmarkdown
## Step 3 — Register

Before sending an `identity_assertion` (either variant), surface the service's
`resource_name` and `resource_logo_uri` (from Step 1a) and the scope set you'll
be acting under, and confirm with the user. This is the user's only consent gate
before their identity is asserted to the service. Skip this for `anonymous`,
there is no user identity to assert.

### identity_assertion + id-jag

Mint the assertion with:

- `aud` = the `resource` from the PRM
- `iss` = your provider's issuer URL
- `email_verified: true` OR `phone_number_verified: true`

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 verifiedUser claimed
Who vouchesAgent’s identity provider via signed JWTThe user, via OTP
Endpoint sequenceregister_uri onlyregister_uri, then claim_uri start + complete
Claim mechanismID-JAG signature verified against issuer JWKSEmail + one-time password
Pre-claim scopeFull scope on successReduced scope (spec example: api.read) until OTP completes
When to useAgent platform has a trusted identity providerNo 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?