In this article
July 22, 2026
July 22, 2026

Teaching your API to onboard AI agents

A walkthrough of AuthKit's Agent Registration: how an AI agent can discover your API, register itself, get claimed by a real user, and start making authenticated calls, with no browser redirect required.

Explore with AI
Open in ChatGPT
Open in Claude
Open in Perplexity

Every OAuth flow you've ever implemented assumes a human is sitting at a keyboard. A browser opens, a login screen appears, someone clicks "allow," and a token comes back. That assumption has held up for twenty years because it was almost always true.

It's not true anymore. AI agents call APIs on people's behalf constantly now, and an agent has no browser tab to open, no eyeballs to read a consent screen, and no fingers to click "allow." Point a standard OAuth authorization code flow at an agent and it just stalls out at the first redirect.

This is the problem AuthKit's new Agent Registration feature solves. Instead of forcing agents through a flow built for humans, it gives them their own: a discovery mechanism they can follow programmatically, a lightweight way to register an identity, and an optional ceremony for binding that identity to a real user, once, without a browser redirect required at every step.

The best way to understand it is to watch it happen. So that's what this post does: we'll wire up a small demo API, expose it to agents, and then hand an agent a single link and watch it register itself, claim its identity, and start making authenticated calls.

The shape of the problem

Classic OAuth (authorization code flow) has a redirect baked into its DNA: your app sends the user's browser to the provider, the user logs in and approves scopes, and the browser gets redirected back with a code. Every step depends on there being a browser and a person driving it.

The Device Authorization Flow (RFC 8628) already solved a version of this problem for smart TVs and CLIs: the device shows a code, the user enters it on a second screen, and the device polls until it's approved. Agent Registration borrows that same shape for a different kind of device. Instead of a TV showing a code, an agent registers itself, gets handed a short code, and asks a human to confirm it, sometimes by reading the code back out loud in conversation rather than typing it into a browser.

That's the "claim ceremony," and it's the piece that makes this more than just another API key scheme: an agent can start working immediately with restricted access, and only gain trusted access once a real person has vouched for it.

Setting up the demo

For this walkthrough, assume you already have an AuthKit-powered environment and a small API you want agents to be able to call. There are three things to wire up: discovery, hosting auth.md, and deciding which identity types to allow.

1. Turn on Agent Registration

In the WorkOS Dashboard, under Authentication → Agents, enable the identity types you want to support. For an API where resources belong to individual users (documents, settings, that kind of thing), enable only service_auth, so every agent has to bind to a user before it can act. If your resources are organization-scoped instead, like shared team data, anonymous registration is a reasonable option too, since agents can start exploring immediately and claim a user later.

For this demo, we'll enable both, and set restrictive untrusted permissions for anonymous agents (read-only, say) with full permissions reserved for claimed, trusted agents.

2. Wire up discovery

Agents find your registration endpoints through a standards-based chain (RFC 9728), not by guessing URLs. When your API gets a request without valid credentials, it responds with a WWW-Authenticate header pointing to a protected resource document:

  
WWW-Authenticate: Bearer resource_metadata="https://demo.example.com/.well-known/oauth-protected-resource"
  

That document points to your authorization server, and the authorization server's own metadata includes an agent_auth block with the actual registration and claim endpoints. An agent walks this chain automatically. You don't have to document it anywhere, because it's discoverable by construction.

Flowchart showing how an agent discovers your registration endpoints: the agent calls the API and gets a 401 response, which points to protected resource metadata, which points to authorization server metadata, which contains the agent_auth block with auth.md and the identity and claim endpoints.

3. Host auth.md

The AS metadata also includes a skill field pointing at a generated auth.md file, essentially a set of instructions written for an agent instead of a person, with the exact curl commands for your environment already filled in. Host it at the root of your own domain by reverse-proxying the WorkOS-hosted version, so it always reflects your current configuration:

  
{
  "rewrites": [
    { "source": "/auth.md", "destination": "https://authkit_domain/agent/auth.md" }
  ]
}
  

Swap in your actual AuthKit domain and you're done. At this point https://demo.example.com/auth.md is a live, public document that any agent can read and act on.

Handing it to an agent

Here's the part worth watching. With everything wired up, the entire onboarding instruction to a person is one line: "Can you follow this guide to set me up with this service? https://demo.example.com/auth.md"

Here's roughly what happens next, narrated step by step.

Flowchart of the agent registration lifecycle: the agent registers anonymously, explores the API with untrusted read-only access, starts a claim ceremony where the user reads a short code back to the agent, then exchanges its assertion for trusted access with a token carrying the act claim.

Discovery. The agent fetches auth.md, which tells it where the identity and claim endpoints live. No prior knowledge of your API was needed.

Registration. Since the agent doesn't yet know who it's acting for, it registers anonymously:

  
curl -X POST https://authkit_domain/agent/identity \
  -H "Content-Type: application/json" \
  -d '{"type": "anonymous"}'
  

It gets back an identity.assertion it can already exchange for a limited, untrusted credential, plus a claim.token it can use later if a user shows up who wants to take ownership of it.

Exploration. The agent exchanges that assertion for an access token and starts poking around your API with read-only, untrusted scopes. This is the part that would have been impossible in a browser-first OAuth flow: there's no human required yet, and no sensitive access granted either.

The claim. Now suppose the person actually wants this agent acting on their behalf with full permissions. The agent kicks off a claim attempt using the claim.token it already has, and gets back a verification_uri. It hands that to the person: "Open this link and sign in, then read back the code you see."

The person opens the link, signs in to AuthKit, and sees a short code. They read it back to the agent (or, if you've built a standalone claim ceremony into your own product UI, they might see the code inside your app instead of on a hosted AuthKit page, with your backend having linked their identity server-side). Either way, the agent submits that code:

  
curl -X POST https://authkit_domain/agent/identity/claim/complete \
  -H "Content-Type: application/json" \
  -d '{
    "claim_token": "<claim_token>",
    "user_code": "<code from user>"
  }'
  

Trusted access. The response contains a new identity.assertion, now bound to the user. The agent exchanges it for a fresh credential, and this one carries an act claim identifying exactly who authorized it. From here on, every call the agent makes is attributable to a real person, and it has whatever trusted permissions you configured for post-claim access.

That's the whole loop: discover, register, explore, claim, act, with a human only needed for one short "read this code back to me" moment in the middle.

Why the assertion pattern matters

It's worth pausing on why this is structured as assertion exchange rather than just handing over a token directly at each step. The identity.assertion the agent gets from registration or claim completion is a signed JWT, not a usable credential on its own. The agent still has to exchange it at the token endpoint:

  
curl -X POST https://authkit_domain/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
  --data-urlencode "assertion=<identity_assertion>"
  

That separation matters for your API's validation logic. Access tokens can be verified locally, by checking the signature and expiration, with no network round trip needed on every request. If you need to know whether a token was revoked before it expired, or you're issuing durable API keys instead of short-lived tokens, you validate those server-side against WorkOS. Either way, your API never has to trust an agent's say-so about who it's acting for. That's encoded in the token itself, in the sub, org_id, and act claims, and it's checkable.

What this unlocks

The interesting part isn't really the curl commands, it's the trust model underneath them. Anonymous registration gives you a real access tier for "an agent nobody has vouched for yet," instead of treating that as an edge case you bolt on later. The claim ceremony gives you a moment of human confirmation without needing a browser redirect for every single request. And the assertion and delegation claims give you an audit trail that says, precisely, which agent acted, and on whose authority.

If you want to try this yourself, the fastest way to see it work is the same line we used above: point any capable AI agent at your auth.md URL and ask it to set itself up. Watching it walk the whole chain on its own is more convincing than any diagram.

For the full reference on identity types, the claim ceremony, credential exchange, and dashboard configuration, see the Agent Registration docs.