# Registry API

Base URL: `https://creators.threetopia.com/api`. JSON endpoints return `{ "message": "…" }` on error, sometimes with a `details` array. Use the CLI unless you need a direct integration.

## Authentication

Browser authentication uses a host-only, HttpOnly, Secure, SameSite=Lax cookie. Browser mutations require the same Origin. CLI authentication uses `Authorization: Bearer TOKEN`. Never put a token in a URL or package file.

| Method and path | Purpose |
| --- | --- |
| `GET /health` | Registry version and docs |
| `POST /auth/start` | Request code: `{email}`; browser same-origin |
| `POST /auth/verify` | Verify `{challenge,code}` and set session cookie |
| `GET /me` | Current creator or null |
| `PATCH /profile` | `{handle,displayName}`; handle is permanent |
| `POST /auth/logout` | Revoke current credential |
| `POST /auth/device` | Start CLI device flow; empty JSON object |
| `POST /auth/device/poll` | `{deviceCode}`; pending returns 202 |
| `POST /auth/device/approve` | Browser only: `{userCode,approve:true}` |
| `GET /tokens` | List current creator’s CLI credentials |
| `DELETE /tokens/:id` | Revoke own CLI credential |

Device flow returns `deviceCode`, `userCode`, `verificationUri`, `expiresIn`, and `interval`. Poll at that interval; approval returns `accessToken`. The device code is confidential and is not the public user code.

## Tiles

| Method and path | Purpose |
| --- | --- |
| `GET /tiles` | Original worlds, current slots, published/reserved tiles, 144 variants |
| `POST /tiles/reserve` | `{q,r,variant}`; returns ID and canonical contract |
| `GET /tiles/:id` | Own reservation and full contract |
| `DELETE /tiles/:id` | Release own unused reservation |

Reservations are atomically unique by coordinate. World creation requires an owned reservation; upload requires its exact canonical contract.

## Packages

| Method and path | Purpose |
| --- | --- |
| `GET /packages?kind=asset&q=beacon` | Public published catalog |
| `GET /packages?mine=1` | Authenticated creator’s packages and states |
| `POST /packages` | `{name,kind,title,description,tileId?}` |
| `GET /packages/:id` | Metadata, versions; own page includes installers |
| `GET /resolve?name=@creator/name&version=1.0.0` | Public exact version, manifest and file hashes |
| `DELETE /packages/:id` | Delete own unpublished package |

Omit version or use `latest` to resolve the highest published numeric version. Use exact versions in dependency manifests.

## Upload lifecycle

1. `POST /packages/:id/versions` with the full [package manifest](/schema/package.json), including file paths, byte lengths and SHA-256 hashes.
2. `PUT /packages/:id/versions/:version/files?path=…` with each file’s raw bytes.
3. `POST /packages/:id/versions/:version/validate` with `{}`. The server measures uploaded geometry; a valid version becomes ready.
4. Review the owner-only preview.
5. `POST /packages/:id/versions/:version/publish` with `{}` to publish.

`GET /packages/:id/versions/:version/files?path=…` serves bytes. Ready and draft files require the owner’s credential. Published files are public and immutable. Uploaded HTML/JavaScript is served as an attachment with a restrictive CSP and is not executed by the site.

`DELETE /packages/:id/versions/:version` removes an unpublished draft. Published versions cannot be changed or deleted.

## Installations

After verifying and writing all files, the CLI calls `POST /packages/:id/install` with `{version,projectId}`. `projectId` is a random project UUID, not its name or path. `DELETE` at the same endpoint with `{projectId}` removes that active installation.

These records describe CLI-reported installations, not arbitrary downloads or proof that a package is actively rendered. Package owners see public creator handles and versions; no private project data.
