# CRAFT — how to get started

You need a CRAFT account before you can do anything here. You cannot create one alone — your user receives a 6-digit code by email and must give it to you. Work through the steps in order.

CRAFT is a platform for building and running "constructs" — live, multiplayer, ownable applications. Everything here is reachable by an agent over HTTP; you never need a browser.

## Get connected

### 1. Register yourself as a client

POST https://craft.everygoodwork.dev/oauth/register with JSON {"client_name":"<your name, which your user will see in the email>","redirect_uris":["https://localhost/callback"]}. Keep the client_id it returns. You never use the redirect_uri — it is required by the spec, not by this flow.

### 2. Generate a PKCE pair

Run the script in `run_this`. It prints CODE_VERIFIER and CODE_CHALLENGE. Keep both; you need the verifier in step 6.

### 3. Pick an auth door: email OTP or device flow

Email OTP (steps 4-6 below) needs only your user's email address — no browser, ever. The device flow is the alternative when your user would rather approve in a browser they already trust: POST https://craft.everygoodwork.dev/oauth/device_authorization with JSON {"client_id":"…","scope":"read write publish"} instead of otp/start, then tell your user say_to_user[3] and send them to the returned verification_uri (or verification_uri_complete). §5.4: have them VERIFY the code shown on https://craft.everygoodwork.dev/activate matches what you told them before approving — that check is what stops a relayed link from being approved blind. You never see or handle the code yourself. Then poll https://craft.everygoodwork.dev/oauth/token with grant_type=urn:ietf:params:oauth:grant-type:device_code, device_code=…, client_id=… every `interval` seconds (from the device_authorization response): `authorization_pending` means keep polling unchanged, `slow_down` means add 5 seconds to your interval and keep polling. On success skip straight to the last step below — the token works the same either way.

### 4. Ask your user for their email address

Use say_to_user[0]. Do not invent an address. Skip this and the next two steps if you chose the device flow above.

### 5. Ask CRAFT to email them

POST https://craft.everygoodwork.dev/oauth/otp/start with JSON {"client_id":"…","email":"…","scope":"read write publish","code_challenge":"…","code_challenge_method":"S256"}. Keep the otp_session it returns. Add "manage" to scope ONLY if your user asked you to administer their account — the email calls it out separately and they must approve it deliberately.

### 6. Ask your user for the 6-digit code

Use say_to_user[1]. The email names you and lists exactly what you asked for, so your user is approving with full sight of it. Then POST https://craft.everygoodwork.dev/oauth/otp/verify with JSON {"otp_session":"…","code":"123456"}. It returns a single-use authorization code.

### 7. Exchange the code for a token

POST https://craft.everygoodwork.dev/oauth/token as form-encoded: grant_type=authorization_code, code=…, code_verifier=… (from step 2), client_id=…, redirect_uri=https://localhost/callback. You get an access_token and refresh_token. (Device-flow callers already did this inside step 3's poll — nothing more to exchange.)

### 8. Start working

Call https://craft.everygoodwork.dev/mcp with `Authorization: Bearer <access_token>`. Call tools/list to see everything you can do, then call describe_platform once to learn how CRAFT works. The authoring loop is create_construct -> author_construct_facet -> publish_construct; begin_media_upload mints a ticket for photo uploads once you want one.

## Generate your PKCE pair

Run this exactly as given, and keep both values it prints:

```sh
#!/bin/sh
# CRAFT PKCE bootstrap. Run this, keep both values.
# CODE_CHALLENGE goes to /oauth/otp/start; CODE_VERIFIER goes to /oauth/token.
# Needs only `openssl`. No key generation, no signing — the onboarding path
# takes a plain Bearer token, so nothing here has to be reasoned about.
set -eu

CODE_VERIFIER=$(openssl rand -base64 60 | tr -d '=+/\n' | cut -c1-64)
CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" \
  | openssl dgst -binary -sha256 \
  | openssl base64 -A \
  | tr '+/' '-_' \
  | tr -d '=')

echo "CODE_VERIFIER=$CODE_VERIFIER"
echo "CODE_CHALLENGE=$CODE_CHALLENGE"
```

## What to say to your user

Use these lines as written — they are the only moments a human is required.

> What email address should I use for your CRAFT account?

> I've emailed a 6-digit code to you. It lists exactly what I'm asking permission to do — please read it, and if you approve, give me the code.

> Thanks — I'm connected to your CRAFT account now.

> Please go to the link I'll give you and enter the code I'll give you — check that the code shown there matches before you approve.

## Endpoints

- Register a client: `POST https://craft.everygoodwork.dev/oauth/register`
- Start email sign-in: `POST https://craft.everygoodwork.dev/oauth/otp/start`
- Finish email sign-in: `POST https://craft.everygoodwork.dev/oauth/otp/verify`
- Start device sign-in (browser alternative): `POST https://craft.everygoodwork.dev/oauth/device_authorization`
- Where your user approves a device: `GET https://craft.everygoodwork.dev/activate`
- Exchange for a token: `POST https://craft.everygoodwork.dev/oauth/token`
- Do work (MCP): `POST https://craft.everygoodwork.dev/mcp`
- OAuth metadata: `GET https://craft.everygoodwork.dev/.well-known/oauth-authorization-server`

## Worth knowing

- Every error response from this API carries an `agent_instructions` object telling you what broke and the exact next call to make. Read it instead of guessing.
- You cannot read your user's email. Step 6 always requires them.
- A browser is never required for OTP. The device flow (step 3) IS browser-based by design — that's the point of offering it, not a fallback to avoid.
- Access tokens last 1 hour; refresh with grant_type=refresh_token.
- The /mcp surface takes a plain Bearer token. The richer /user/agent code-mode gateway additionally requires a DPoP sender-constrained token (RFC 9449) — get connected first, then upgrade if you need it.

## Machine-readable

The same content as JSON: `GET https://craft.everygoodwork.dev/.well-known/agent-onboarding`
