# 63Digital auth.md

You are an agent. 63digital.com supports agentic registration per [auth.md](https://github.com/workos/auth.md) (v0.6), for two things done on a person's behalf with their verified email: **sending 63Digital a project enquiry** (replies go to that address) and **buying the team a coffee** through the [Agentic Commerce Protocol](https://agenticcommerce.dev) (the receipt goes to that address). Everything else on the site — the MCP server at `https://63digital.com/mcp`, pages as Markdown, `llms.txt` — is public and needs no registration.

Only one registration method is offered: **`service_auth`** (the person's email). Anonymous registration and ID-JAG assertions are not accepted.

## Step 1 — Discover

- Protected Resource Metadata: `GET https://63digital.com/.well-known/oauth-protected-resource`
- Authorization Server metadata (issuer `https://63digital.com/agent-auth`): `GET https://63digital.com/agent-auth/.well-known/oauth-authorization-server` (also at the RFC 8414 location, `https://63digital.com/.well-known/oauth-authorization-server/agent-auth`)

Scopes: `enquiries.send` and `checkout`. Every access token carries both.

## Step 2 — Get consent

Before registering, tell the person you'll ask 63Digital to verify their email so you can send an enquiry for them, and confirm they're happy to go ahead. Draft the enquiry with them; don't send anything they haven't seen.

## Step 3 — Register

```http
POST https://63digital.com/agent-auth/agent/identity
Content-Type: application/json

{ "type": "service_auth", "login_hint": "person@example.com" }
```

Response (200) carries a `claim_token` (keep it in memory; it's returned once) and a `claim` block with `user_code`, `verification_uri`, `expires_in` (600) and `interval` (5).

## Step 4 — Claim ceremony

Give the person the link and the code in one message:

> Open this link, confirm your email (we'll send you a sign-in link), then enter this 6-digit code: **123456**
> https://63digital.com/agent-auth/claim?claim_attempt_token=…

The person opens `verification_uri`, asks for a sign-in link, opens it from their inbox, and types the code. Meanwhile poll:

```http
POST https://63digital.com/agent-auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=clm_…
```

- `authorization_pending` — keep polling every `interval` seconds.
- `slow_down` — you're polling too fast; back off.
- `expired_token` — the code's 10 minutes ran out: `POST https://63digital.com/agent-auth/agent/identity/claim` with `{ "claim_token": "clm_…", "email": "person@example.com" }` for a fresh `claim_attempt`, and hand the new code over.
- Success — `access_token` (Bearer, 1 hour, scope `enquiries.send`) plus an `identity_assertion` (24 hours).

## Step 5 — Refresh

When the access token expires, exchange the identity assertion for a new one:

```http
POST https://63digital.com/agent-auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=ia_…
```

The assertion is opaque (not a JWT) — pass it back as-is. `invalid_grant` means register again.

## Step 6 — Send the enquiry

Either the REST endpoint:

```http
POST https://63digital.com/api/enquiries
Authorization: Bearer at_…
Content-Type: application/json

{ "name": "Ada Lovelace", "message": "…", "phone": "optional", "company": "optional" }
```

`202 { "status": "sent", "reply_to": "person@example.com" }` on success. Or, over MCP, connect to `https://63digital.com/mcp` with `Authorization: Bearer at_…` and call `submit_enquiry`.

Limits: 5 enquiries per email per day; messages up to 5,000 characters.

## Step 7 — Buy a coffee (ACP)

With the same access token, check out through the Agentic Commerce Protocol (spec `2026-04-17`; discovery at `https://63digital.com/.well-known/acp.json`). Every call needs `Authorization: Bearer at_…` and `API-Version: 2026-04-17`; every POST needs an `Idempotency-Key`.

1. `POST https://63digital.com/api/acp/checkout_sessions` with `{ "line_items": [{ "id": "coffee", "quantity": 1 }], "currency": "gbp", "capabilities": {} }` — £3.00 each, up to 10.
2. Mint a Stripe Shared Payment Token granted to the profile in the session's `capabilities.payment.handlers[0].config.merchant_id`, capped at the session total.
3. `POST https://63digital.com/api/acp/checkout_sessions/{id}/complete` with `{ "payment_data": { "handler_id": "card_tokenized", "instrument": { "type": "card", "credential": { "type": "spt", "token": "spt_…" } } } }`.

A completed session carries an `order` with a `permalink_url`; Stripe emails the receipt to the verified address. Agents that only want to pay, without registering, can use MPP instead: `POST https://63digital.com/coffee/buy` answers `402` with a `WWW-Authenticate: Payment` challenge (method `stripe`).

## Revocation

`POST https://63digital.com/agent-auth/oauth2/revoke` with `token=…` (form-encoded) revokes an access token or identity assertion. Always 200.

## Errors

| Code | Where | What to do |
| --- | --- | --- |
| `anonymous_not_enabled`, `issuer_not_enabled` | `/agent/identity` | Use `service_auth`. |
| `invalid_login_hint` | `/agent/identity` | `login_hint` must be an email address. |
| `rate_limited` (429) | `/agent/identity` | Back off; try later. |
| `invalid_claim_token`, `claim_expired` | `/agent/identity/claim` | Register again. |
| `authorization_pending`, `slow_down`, `expired_token` | `/oauth2/token` | See Step 4. |
| `invalid_grant` | `/oauth2/token` | Register again. |
| `invalid_token` (401) | `/api/enquiries` | Get or refresh an access token. |

Without a token, the person can always email hello@63digital.com or use https://63digital.com/contact/.
