<!-- llms.txt: https://workos.com/llms.txt -->

# Agent Registration

> Agent Registration must be enabled for your environment. Contact your WorkOS account team if it is not yet available.

## Introduction

Agent Registration enables AI agents and LLM-based clients to obtain identity credentials from your AuthKit-powered service. Unlike traditional OAuth flows designed for users in a browser, Agent Registration is purpose-built for programmatic clients that need machine-readable instructions, minimal interaction, and secure credential management.

Agents discover the registration surface through your service's [`.well-known/oauth-authorization-server`](#discovery) metadata, register with one of several identity types, optionally bind to a user via a claim ceremony, and exchange the resulting identity assertion for an access token or API key.

## How it works

### Identity types

Agent Registration supports three identity types, selected by the agent based on its context:

| Type | When to use | Claim ceremony |
|------|-------------|----------------|
| `anonymous` | The agent has no user identity and wants to trial the service | Optional |
| `service_auth` | The agent knows the user's email and needs trusted access | Required |
| `refresh` | The agent's current assertion is expiring and needs rotation | Not applicable |

### Choosing identity types

Your choice of identity type depends on your application's data model:

- **If entities are owned by users** (e.g. a user's documents, personal settings), enable only `service_auth`. This ensures every agent is bound to a specific user through the claim ceremony before it can act on their behalf.
- **If the resource owner is an organization** (e.g. shared team resources, org-wide data), `anonymous` registrations work well — agents can begin interacting with organization-scoped resources immediately and optionally bind to a user later.

When enabling `anonymous`, assign it a restricted set of untrusted permissions suitable for exploration only. Sensitive operations should require trusted (post-claim) permissions so that anonymous agents cannot perform them. See [Trust levels and scopes](#trust-levels-and-scopes) for details.

### Registration flow

```
Agent                                          AuthKit                       User
  │                                               │                           │
  │─── POST /agent/identity ─────────────────────►│                           │
  │    (type: service_auth)                       │                           │
  │◄── claim.token + attempt ─────────────────────│                           │
  │    (includes verification_uri)                │                           │
  │                                               │                           │
  │─── "Open this link and read back the code" ──────────────────────────────►│
  │                                               │◄── signs in, views code ──│
  │◄── user reads code back ──────────────────────────────────────────────────│
  │                                               │                           │
  │─── POST /agent/identity/claim/complete ──────►│                           │
  │    (user_code)                                │                           │
  │◄── identity.assertion ────────────────────────│                           │
  │                                               │                           │
  │─── POST /oauth2/token ───────────────────────►│                           │
  │    (assertion exchange)                       │                           │
  │◄── access_token / api_key ────────────────────│                           │
  │                                               │                           │
```

> **Note:** The registration response includes the initial `verification_uri`. The separate `POST /agent/identity/claim` endpoint is only needed if the initial attempt expires and the agent needs to start a new one.

## Discovery

Agents discover your registration endpoints through a standards-based chain defined by [RFC 9728](https://workos.com/blog/introducing-rfc-9728-say-hello-to-standardized-oauth-2-0-resource-metadata):

1. Your API returns a `WWW-Authenticate` header with a `resource_metadata` parameter pointing to your Protected Resource Metadata document.
2. The agent fetches `/.well-known/oauth-protected-resource` and reads the `authorization_servers` array.
3. The agent fetches the Authorization Server metadata, which includes an `agent_auth` block with registration endpoints.

```bash
# Step 1 — Your API challenges with resource_metadata
WWW-Authenticate: Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"

# Step 2 — Protected Resource Document points to the AS
curl https://example.com/.well-known/oauth-protected-resource
{
  "resource": "https://example.com",
  "authorization_servers": ["https://authkit_domain"]
}

# Step 3 — AS metadata includes agent_auth
curl https://authkit_domain/.well-known/oauth-authorization-server | jq .agent_auth
{
  "skill": "https://authkit_domain/agent/auth.md",
  "identity_endpoint": "https://authkit_domain/agent/identity",
  "claim_endpoint": "https://authkit_domain/agent/identity/claim",
  "identity_types_supported": ["anonymous", "service_auth"]
}
```

Set up the link between your protected resource and your authorization server in the WorkOS Dashboard under **Authentication → Agents → Configuration → OAuth well-known discovery**.

The `skill` field in the AS metadata points to a generated `auth.md` document — a step-by-step guide with curl commands tailored to your environment's configuration. Agents can follow it directly without needing to parse this documentation.

### Hosting auth.md

For agents to discover your service, host the `auth.md` file at the root of your domain (e.g. `https://example.com/auth.md`). The recommended approach is to reverse-proxy the WorkOS-hosted version so it always reflects your current configuration.

Find the source URL in the WorkOS Dashboard under **Authentication → Agents → Configuration → Host auth.md**, then configure your server:

```js title="next.config.js"
async rewrites() {
  return [{
    source: '/auth.md',
    destination: 'https://authkit_domain/agent/auth.md',
  }];
}
```

```json title="vercel.json"
{
  "rewrites": [
    { "source": "/auth.md", "destination": "https://authkit_domain/agent/auth.md" }
  ]
}
```

```nginx title="nginx.conf"
location = /auth.md {
    proxy_pass https://authkit_domain/agent/auth.md;
    proxy_set_header Host authkit_domain;
    proxy_ssl_server_name on;
}
```

```js title="Cloudflare Worker"
export default {
  async fetch(request) {
    const url = new URL(request.url);
    if (url.pathname === "/auth.md") {
      return fetch("https://authkit_domain/agent/auth.md");
    }
    return fetch(request);
  }
}
```

For Cloudflare Enterprise, skip the worker and use a [URL rewrite rule](https://developers.cloudflare.com/rules/origin-rules/tutorials/change-uri-path-and-host-header/) instead.

Replace `authkit_domain` with your environment's AuthKit domain. The Dashboard provides the exact source URL and configuration snippets for your environment.

> **Tip:** The hosted `auth.md` URL is public — agents can access it directly without a reverse proxy. You can test the full registration flow without setting up hosting first.

## Registering

### Anonymous registration

Anonymous registration creates an identity with no user binding. The agent receives an identity assertion immediately and can optionally bind to a user later via the claim ceremony.

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

The response includes:

- `identity.assertion` — a signed JWT the agent uses for credential exchange
- `claim.token` — used to start a claim ceremony later (optional)
- `scopes.pre_claim` — permissions available before claiming (untrusted)
- `scopes.post_claim` — permissions available after claiming (trusted)

### Service auth registration

When the agent knows the user's email, `service_auth` registration initiates a mandatory claim ceremony:

```bash
curl -X POST https://authkit_domain/agent/identity \
  -H "Content-Type: application/json" \
  -d '{"type": "service_auth", "login_hint": "user@example.com"}'
```

The response includes a `claim.token` and `claim.attempt.verification_uri` — the agent presents this URI to the user to start the claim ceremony.

### Refresh

When an assertion is nearing expiry, the agent rotates it using the refresh token from a previous registration or claim:

```bash
curl -X POST https://authkit_domain/agent/identity \
  -H "Content-Type: application/json" \
  -d '{"type": "refresh", "refresh_token": "<refresh_token>"}'
```

## The claim ceremony

The claim ceremony binds an agent registration to a user, similar to the [OAuth 2.0 Device Authorization Flow](https://datatracker.ietf.org/doc/html/rfc8628). It's required for `service_auth` and optional for `anonymous` registrations.

### (1) Start an attempt

The agent mints a claim attempt using the claim token from registration:

```bash
curl -X POST https://authkit_domain/agent/identity/claim \
  -H "Content-Type: application/json" \
  -d '{
    "type": "service_auth",
    "claim_token": "<claim_token>",
    "login_hint": "user@example.com"
  }'
```

The response includes `attempt.verification_uri` — a URL for the user.

### (2) User confirms

The agent presents the `verification_uri` to the user. When they open it:

1. They sign in to AuthKit (if not already authenticated)
2. The page reveals a short `user_code`
3. The user reads this code back to the agent

### (3) Complete the claim

The agent submits the code along with its claim token:

```bash
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>"
  }'
```

On success, the response contains `identity.assertion` and `identity.refresh_token` — the agent's trusted credentials.

## Credential exchange

After obtaining an identity assertion (from registration or claim completion), the agent exchanges it at the token endpoint for an access token or API key:

```bash
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>"
```

The credential type (access token or API key) and its lifetime are configured per environment in the WorkOS Dashboard.

### Access token claims

Agent access tokens include the following claims:

| Claim | Description |
|-------|-------------|
| `iss` | The issuer URL for the environment |
| `aud` | The audience — the environment's `client_id`, or the `resource` URI if one was requested |
| `sub` | The agent registration ID (e.g. `agent_reg_01ABC...`) |
| `org_id` | The organization the agent is acting within |
| `scope` | Space-separated list of granted permissions |
| `act` | Delegation claim identifying the user who authorized the agent (e.g. `{"sub": "user_01DEF..."}`) |
| `jti` | Unique token identifier |
| `iat` | Issued-at timestamp |
| `exp` | Expiration timestamp |

The `act` claim follows the [RFC 8693 delegation pattern](https://datatracker.ietf.org/doc/html/rfc8693#section-4.1) and is present only after a successful claim ceremony binds a user to the registration.

## Validating credentials

Once an agent has exchanged its assertion for a credential, your application needs to validate that credential on incoming requests.

**Access tokens** — For short-lived access tokens, you can validate the JWT locally by verifying the signature and checking expiration. This avoids a network round-trip on every request. If you need to check whether a token has been revoked before expiry, call the [validate credential endpoint](https://workos.com/docs/reference/agents/validate-credential).

**API keys** — API keys must be validated server-side. Use the [validate credential endpoint](https://workos.com/docs/reference/agents/validate-credential) to confirm the key is still active and retrieve its associated registration.

### Inspecting a registration

To see full details about the agent behind a credential — its identity, organization, claim status, and timestamps — use the [get registration endpoint](https://workos.com/docs/reference/agents/get-registration) with the registration ID from the access token's `sub` claim.

## Trust levels and scopes

Agent Registration uses two trust levels to control API access:

- **Untrusted** (pre-claim) — the default for anonymous registrations before a claim ceremony. Grants limited scopes suitable for exploration.
- **Trusted** (post-claim) — granted after a successful claim ceremony. Grants full scopes.

Configure which scopes map to each trust level in the WorkOS Dashboard under your environment's agent auth settings.

## Dashboard setup

Configure Agent Registration in the [WorkOS Dashboard](https://dashboard.workos.com/environment/authentication/agents) under **Authentication → Agents**:

### Registrations

The Registrations tab lists all agent registrations created against your environment, including their current status (`unverified`, `verified`, `expired`, `revoked`), the associated user, and the organization.

### Methods

Enable the identity types agents can use to register:

- **Service auth** — agents register on behalf of a user, confirming identity through a claim ceremony
- **Anonymous** — agents register instantly with no credentials; users claim ownership when ready

Each method can be independently enabled or disabled.

### Configuration

#### Credential settings

Set the credential type and lifetime for all agent registrations:

- **Credential type** — choose whether the token exchange mints an **access token** (short-lived, re-mintable) or an **API key** (durable, persisted by the agent)
- **Credential expiration** — the lifetime of issued credentials

#### Trust level permissions

Define which permissions are available at each trust level:

- **Trusted permissions** — granted after a successful claim ceremony. These are the full set of permissions for verified agents.
- **Untrusted permissions** — granted to anonymous registrations before a claim. Use these for limited, exploratory access.

Only permissions that are [enabled for organization API keys](https://workos.com/docs/authkit/api-keys/configuring-api-keys/configuring-available-permissions) appear here. To add a permission to the list, enable it under *Authorization > Configuration > Organization API key permissions* in the Dashboard.

#### Discovery

The Discovery section shows:

- **Host auth.md** — the source URL for your environment's generated `auth.md` skill document, with a configuration dialog showing reverse-proxy setup for Nginx, Next.js, Vercel, and Cloudflare (see [Hosting auth.md](#hosting-auth-md) above)
- **OAuth well-known discovery** — the authorization server metadata URL that agents use to locate your registration endpoints

## Testing

Once your environment is configured, test the full flow by prompting any AI agent:

```
Can you follow this guide to set me up with this service? https://example.com/auth.md
```

The agent will follow the instructions in `auth.md` to register and exchange its identity assertion for credentials it can use to call your API. The agent will also guide the user through the claim flow if necessary.
