# API reference — first release

All state-changing requests are same-origin only and return JSON.

## Public or account routes

### `GET /api/health`

Checks application and MySQL availability.

### `POST /api/auth/request-code`

Login request:

```json
{
  "email": "person@example.com",
  "purpose": "login",
  "turnstileToken": "..."
}
```

QR claim request:

```json
{
  "email": "person@example.com",
  "purpose": "claim",
  "publicCode": "YS7K2M9Q",
  "activationCode": "M7K4-92PL",
  "turnstileToken": "..."
}
```

### `POST /api/auth/verify-code`

```json
{
  "email": "person@example.com",
  "purpose": "claim",
  "code": "483921"
}
```

Successful claim verification reserves the QR and returns `/onboarding?source=qr`.

### `POST /api/auth/logout`

Revokes the database session and clears cookies.

### `POST /api/stories`

Requires a signed-in user. Creates a story and, when a verified claim cookie exists, permanently connects the reserved QR in the same transaction.

### `PATCH /api/stories/[id]`

Requires Story Keeper, Family Editor or Legacy Steward permission.

### `POST /api/uploads/presign`

Creates a media metadata record and returns a short-lived R2 PUT URL.

### `POST /api/uploads/complete`

Marks the uploaded object ready and sets it as the story cover.

## Administrator routes

### `GET /api/admin/qr`

Returns the 100 most recent QR operational records. Activation codes are never returned.

### `POST /api/admin/qr`

Creates 1–50 QR records. Plain activation codes are returned only in this response.

### `GET /api/qr/[code]/svg`

Returns a scalable QR containing the permanent resolver URL.

### `GET /api/qr/[code]/png`

Returns a 1600-pixel PNG QR containing the permanent resolver URL.
