# Chatdrift — auth.md

How an autonomous agent authenticates against Chatdrift (https://www.chatdrift.com). This document follows the [auth.md](https://workos.com/auth-md) convention: discover this brand's authorization server, pick a registration method, register, claim a credential if needed, use it, and know how to recover from errors and revoke it.

## Discover

Start from either of two entry points:

1. **Protected resource metadata** — fetch:

   `https://www.chatdrift.com/api/.well-known/oauth-protected-resource`

   or read the `WWW-Authenticate` header on any `401` response from an authenticated endpoint (it carries a `resource_metadata="..."` parameter pointing at the same document).
2. **Authorization server metadata** — the resource metadata's `authorization_servers` entry points at:

   `https://www.chatdrift.com/.well-known/oauth-authorization-server`

   This is RFC 8414 authorization server metadata plus an `agent_auth` extension block describing this document itself:

   ```json
   {
     "agent_auth": {
       "skill": "https://www.chatdrift.com/auth.md",
       "claim_endpoint": "https://www.chatdrift.com/api/oauth/register",
       "register_uri": "https://www.chatdrift.com/api/oauth/register",
       "identity_types_supported": ["anonymous"],
       "anonymous": { "credential_types_supported": ["oauth_client"] }
     }
   }
   ```

   `claim_endpoint` is the real [auth.md](https://workos.com/auth-md) field name — for the `anonymous` identity type below, the "claim" step is dynamic client registration followed by the authorization-code redirect, so it points at the same URL as `registration_endpoint`. `register_uri` is a non-standard alias kept alongside it for backward compatibility; new clients should read `claim_endpoint`. The `anonymous` object is a per-identity-type capability block naming the credential that type issues (a dynamically registered OAuth client) — it augments, and never replaces, `identity_types_supported` above.

   If anything in this file conflicts with the live metadata, the metadata is authoritative.

   The full auth.md spec also defines an `identity_assertion` identity type, where the agent presents an assertion token (an ID-JAG — `urn:ietf:params:oauth:token-type:id-jag`) issued by its own provider instead of registering a fresh client. Chatdrift does not require identity_assertion today — every agent uses the `anonymous` identity type below — but a client that already holds an ID-JAG assertion may present it at the token endpoint once support lands; check `identity_types_supported` on the live authorization server metadata for the current answer.

## Pick a method

Chatdrift supports three independent credential types — pick whichever fits how your agent operates:

| Method | Best for | Credential |
| --- | --- | --- |
| OAuth 2.1 + PKCE + dynamic client registration | MCP clients (Claude, ChatGPT, custom agents) acting on behalf of a signed-in user | short-lived bearer access token, refreshable |
| Team API key | Server-to-server integrations owned by one team | long-lived `nex_` bearer token |
| External-agent key | Third-party agents calling the external-agent MCP endpoint | long-lived `nex_ext_` bearer token |

1. **OAuth 2.1 + PKCE + dynamic client registration** — register a client at `https://www.chatdrift.com/api/oauth/register`, send the user to authorize at `https://www.chatdrift.com/api/oauth/authorize`, and exchange the resulting code at `https://www.chatdrift.com/api/oauth/token`. This is the `identity_types_supported: ["anonymous"]` flow named in the `agent_auth` block above — the agent registers its own OAuth client with no upfront identity assertion, then a human completes the authorization-code redirect to grant it scoped access.
2. **Team API key** — self-serve in the dashboard under Settings → API Keys. Prefixed `nex_`. Send as `Authorization: Bearer nex_...`.
3. **External-agent key** — for calls to `https://www.chatdrift.com/external-agent/mcp`. Prefixed `nex_ext_`. Send as `Authorization: Bearer nex_ext_...`.

## Register

Dynamic client registration (RFC 7591) at `https://www.chatdrift.com/api/oauth/register`:

```http
POST https://www.chatdrift.com/api/oauth/register
Content-Type: application/json

{
  "redirect_uris": ["https://your-agent.example/callback"],
  "client_name": "Your Agent",
  "grant_types": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_method": "none"
}
```

Response:

```json
{
  "client_id": "...",
  "client_secret": "...",
  "client_id_issued_at": 1700000000,
  "redirect_uris": ["https://your-agent.example/callback"]
}
```

Team API keys and external-agent keys don't go through this endpoint — create them from the dashboard (Settings → API Keys) instead; there is no self-registration API for them.

## Claim

For the OAuth flow, the registered client's authorization code is claimed via the standard authorization-code + PKCE exchange: send the user through `authorization_endpoint` with a `code_challenge`, then exchange the returned `code` and matching `code_verifier` at `token_endpoint` for tokens. There is no separate "claim ceremony" endpoint for the anonymous OAuth-client identity type — approval happens entirely through that redirect. API keys and external-agent keys require no claim step: they are already scoped to a team the moment they're created.

## Use the credential

Present whichever credential you obtained as a bearer token on every request:

```http
Authorization: Bearer <token>
```

- **Streamable HTTP**: https://www.chatdrift.com/api/chatgpt/mcp/mcp
- **SSE**: https://www.chatdrift.com/api/mcp
- **External agent (A2A-style)**: https://www.chatdrift.com/external-agent/mcp
- **ChatGPT connector**: https://www.chatdrift.com/api/chatgpt/mcp/mcp

OAuth access tokens are short-lived; when one expires, use the paired `refresh_token` against `https://www.chatdrift.com/api/oauth/token` (`grant_type=refresh_token`) to obtain a new one without re-prompting the user. API keys and external-agent keys don't expire — rotate them from the dashboard instead.

## Errors

Every authenticated endpoint that rejects a credential returns a `401` or `403` with a machine-readable `WWW-Authenticate` challenge header (RFC 6750) alongside a JSON body:

| `error` | HTTP status | Meaning | What to do |
| --- | --- | --- | --- |
| `invalid_request` | 401 | The `Authorization` header is missing or malformed | Resend with `Authorization: Bearer <token>` |
| `invalid_token` | 401 | The token is expired, malformed, or unknown | Refresh (OAuth) or re-issue (API key) the credential |
| `insufficient_scope` | 403 | The token is valid but lacks a required scope | Re-authorize with the missing scope, or use a credential that has it |

The `WWW-Authenticate` header on each of these carries `resource_metadata="..."` pointing back at `https://www.chatdrift.com/api/.well-known/oauth-protected-resource`, so an agent that lands on an error can always re-run Discover from scratch.

## Revocation

- **OAuth tokens**: `POST https://www.chatdrift.com/api/oauth/revoke` (RFC 7009) with the token to invalidate it immediately.
- **API keys / external-agent keys**: delete the key from the dashboard (Settings → API Keys) — there is no revocation API for these; deletion is immediate and takes effect on the next request.

These are two independent layers: revoking an OAuth token never touches a team's API keys, and vice versa.

## Deprecation policy

Every credential/endpoint above is versioned in its URL (`/api/v1`, `/api/public/v1`). No version documented here is currently deprecated. When a version is deprecated, its responses will carry a `Deprecation` header (RFC 8594) naming the date deprecation took effect, and — once a retirement date is set — a `Sunset` header naming it; the deprecated version will keep working for at least 90 days after `Deprecation` first appears. See https://www.chatdrift.com/openapi.json's `info.description` for the same policy.

## Sandbox

The free Hobbyist team is the approved sandbox for testing against Chatdrift: it is isolated from other teams' data, shares nothing with production teams, and only supports draft agent versions — safe to register OAuth clients and API keys against without any risk to real customer data.

## Further reading

- Authorization server metadata: https://www.chatdrift.com/.well-known/oauth-authorization-server
- Protected resource metadata: https://www.chatdrift.com/api/.well-known/oauth-protected-resource
- Dynamic client registration: https://www.chatdrift.com/api/oauth/register
- Scopes: openid, profile, mcp:read, mcp:write, conversations:read, conversations:write, contacts:read, contacts:write, analytics:read, tickets:read, tickets:write, agents:read, agents:write, guardrails:read, guardrails:write, data-sources:read, data-sources:write
