# auth.md

AgentModus is the efficiency layer AI agents route their LLM traffic through. This
file lets an agent **register itself** and obtain a scoped credential, following
the [auth.md](https://workos.com/auth-md) standard.

- **Authorization server:** `https://www.agentmodus.ai`
- **Protected resource (route your traffic here):** `http://localhost:8080/v1`
- **Supported flow:** user-claimed, **email-required** mode only (`service_auth`).

AgentModus is an observe-only passthrough: you keep sending your **own** provider
API key, which we forward untouched and never store. The credential you receive
here is an account identifier used as the **base_url path** — not an Authorization
bearer. A registered agent is just another fully isolated account.

> Security note: there is **no** anonymous start and **no** pre-claim credential.
> No token exists until a real human reads back a one-time code emailed to them.

> Access is **gated**: registration only proceeds for an email that is already an
> onboarded AgentModus account. An unknown email gets queued for admin review and
> returns `approval_required` (no code, no token) — retry once it's approved.

## 1. Discover

- Protected resource metadata: `https://www.agentmodus.ai/.well-known/oauth-protected-resource`
- Authorization server metadata: `https://www.agentmodus.ai/.well-known/oauth-authorization-server`

The AS metadata's `agent_auth` block points back to this file and to the
registration + claim-completion endpoints.

## 2. Pick a method

AgentModus supports exactly one method: **`service_auth`** (you supply the
user's email; a human authorizes via an emailed code). Anonymous and
identity-assertion registration are intentionally not offered.

## 3. Register

```http
POST https://www.agentmodus.ai/agent/auth/register
Content-Type: application/json
```

```json
{
  "type": "service_auth",
  "login_hint": "user@example.com",
  "client_name": "My Agent",
  "scope": "traffic.route traffic.capture"
}
```

Response (`201`). A 6-digit code is emailed to the user; it is **not** in this
response:

```json
{
  "registration_id": "reg_...",
  "registration_type": "service_auth",
  "claim_token": "clm_...",
  "claim_complete_url": "https://www.agentmodus.ai/agent/auth/claim/complete",
  "claim_token_expires": "<ISO-8601>",
  "post_claim_scopes": ["traffic.route","traffic.capture"],
  "claim": {
    "verification_uri": "https://www.agentmodus.ai/agent/claim",
    "email_sent_to": "u***r@example.com",
    "expires_in": 600,
    "interval": 5,
    "instructions": "Ask the user to read back the 6-digit code we emailed them."
  }
}
```

## 4. Claim ceremony (email-required)

1. Tell the user: a code was emailed to `email_sent_to`. Ask them to read it back.
2. The user finds the 6-digit code in their inbox (or on `verification_uri`).
3. Submit it with your `claim_token`:

```http
POST https://www.agentmodus.ai/agent/auth/claim/complete
Content-Type: application/json
```

```json
{ "claim_token": "clm_...", "user_code": "123456" }
```

Until a correct code is submitted, completion returns `400` with
`{"error":"authorization_pending"}`. Retry no faster than `interval` seconds.

## 5. Receive the credential

On success (`200`):

```json
{
  "token_type": "Bearer",
  "access_token": "am_...",
  "scope": "traffic.route traffic.capture",
  "account_id": "acct_...",
  "base_url": "http://localhost:8080/v1/am_...",
  "credential_placement": "base_url_path",
  "usage": "Set your OpenAI base_url to base_url; keep your own provider key as the Authorization bearer."
}
```

## 6. Use it

Point your OpenAI-compatible client at `base_url` and keep using your own
provider key:

```http
POST http://localhost:8080/v1/am_.../chat/completions
Authorization: Bearer <YOUR-OWN-PROVIDER-KEY>
Content-Type: application/json
```

```json
{ "model": "gpt-4o-mini", "messages": [{ "role": "user", "content": "ping" }] }
```

AgentModus forwards the request byte-for-byte to the upstream provider, returns
the response unmodified, and records the call against your isolated account.

## Scopes

- `traffic.route` — Route the agent's LLM traffic through AgentModus's OpenAI-compatible endpoint (your provider key is forwarded untouched and never stored).
- `traffic.capture` — Observe that traffic — spend, tokens, latency, quality — scoped to the agent's own isolated account.

## Errors

| HTTP | error | meaning | action |
| ---- | ----- | ------- | ------ |
| 400 | `invalid_request` | bad/missing fields | fix the request |
| 400 | `unsupported_identity_type` | not `service_auth` | use `service_auth` |
| 403 | `approval_required` | email not onboarded (gated) | request access; retry once approved |
| 400 | `authorization_pending` | code not read back yet | poll `claim_complete_url` |
| 400 | `expired_token` | claim expired | register again |
| 400 | `invalid_grant` | unknown/used `claim_token` | register again |
| 429 | `too_many_attempts` | too many wrong codes | register again |

## Revocation

Account credentials are revocable by AgentModus (support / dashboard). A revoked
account's `base_url` stops being attributed. Human dashboard login (email
magic-link) is entirely separate from this agent registration flow.
