> ## Documentation Index
> Fetch the complete documentation index at: https://docs.worldflux.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Cloud API reference

> WorldFlux Cloud is a FastAPI service. The Next.js dashboard ships a thin BFF that proxies authenticated calls. The OpenAPI document is the source of truth.

## Source of truth

The cloud control plane is a FastAPI service. Its OpenAPI document is the canonical reference; this page summarizes the direct FastAPI surface and the BFF routes the dashboard calls.

```text theme={null}
GET /openapi.json
GET /docs
```

To regenerate the dashboard's TypeScript client from that document:

```bash theme={null}
cd web
npm run generate:api
```

## Traffic model

The CLI talks to FastAPI directly with `Authorization: Bearer <api-key>`.
The dashboard sits behind Supabase auth and uses two BFF layers: named `/api/cloud/*` routes for higher-level dashboard actions, and a guarded catch-all `/api/[...path]` proxy for a narrow allowlist of Cloud read/sync routes.
`/openapi.json` remains the source of truth for generated clients.

## FastAPI route families

| Family         | Routes                                                                                                              |
| -------------- | ------------------------------------------------------------------------------------------------------------------- |
| System         | `/`, `/healthz`, `/health`, `/health/db`, `/health/r2`                                                              |
| Status         | `GET /api/status`, `GET /api/status/operational`                                                                    |
| Workspaces     | `/api/workspaces`, `/api/workspaces/{workspace_id}`, invitations, transfer ownership                                |
| Projects       | `/api/projects`, `/api/projects/{project_id}`                                                                       |
| Sync           | `POST /api/sync/runs`, `GET /api/sync/ledger-head`, `POST /api/sync/ledger-head`, artifact initiate/commit/abort    |
| Runs           | `/api/runs`, `/api/runs/{run_id}`, manifest, metrics, log, signature, compliance mapping, artifact download, delete |
| API keys       | `/api/api-keys`, rotate, revoke                                                                                     |
| Shares         | `/api/shares`, revoke, `/api/public/shares/{token}`                                                                 |
| Billing        | checkout, portal, webhook                                                                                           |
| Usage          | reserve, commit, revert                                                                                             |
| Profile / DSAR | `/api/me`, object, restrict, export, delete, cancel delete                                                          |
| Claims         | `/api/claims`, `/api/claims/{claim_id}`                                                                             |
| Museum         | `/api/museum`                                                                                                       |
| Audit logs     | `/api/audit/logs`                                                                                                   |

Machine-checked Cloud route list:

* `GET /`
* `GET /api/api-keys`
* `POST /api/api-keys`
* `DELETE /api/api-keys/{api_key_id}`
* `POST /api/api-keys/{api_key_id}/rotate`
* `GET /api/artifacts/{artifact_id}`
* `GET /api/audit/logs`
* `POST /api/billing/checkout`
* `POST /api/billing/portal`
* `POST /api/billing/webhook`
* `GET /api/claims`
* `GET /api/claims/{claim_id}`
* `GET /api/me`
* `PATCH /api/me`
* `POST /api/me/delete`
* `POST /api/me/delete/cancel`
* `GET /api/me/export`
* `POST /api/me/object`
* `POST /api/me/restrict`
* `GET /api/museum`
* `GET /api/projects`
* `POST /api/projects`
* `DELETE /api/projects/{project_id}`
* `GET /api/projects/{project_id}`
* `PATCH /api/projects/{project_id}`
* `DELETE /api/projects/{project_id}/staging-e2e-tree`
* `POST /api/projects/{project_id}/staging-retention-proof`
* `GET /api/public/shares/{token}`
* `GET /api/runs`
* `DELETE /api/runs/{run_id}`
* `GET /api/runs/{run_id}`
* `POST /api/runs/{run_id}/evidence-package`
* `GET /api/runs/{run_id}/log`
* `GET /api/runs/{run_id}/manifest`
* `GET /api/runs/{run_id}/metrics`
* `GET /api/shares`
* `POST /api/shares`
* `POST /api/shares/{share_id}/reliability-consent`
* `DELETE /api/shares/{share_id}/reliability-consent/{consent_id}`
* `GET /api/shares/{share_id}/reliability-consent/{consent_id}/export`
* `POST /api/shares/{share_id}/reliability-consent/{consent_id}/revoke`
* `POST /api/shares/{share_id}/revoke`
* `GET /api/status`
* `GET /api/status/operational`
* `POST /api/sync/artifacts/abort`
* `POST /api/sync/artifacts/commit`
* `POST /api/sync/artifacts/initiate`
* `GET /api/sync/ledger-head`
* `POST /api/sync/ledger-head`
* `POST /api/sync/runs`
* `POST /api/usage/reserve`
* `POST /api/usage/commit`
* `POST /api/usage/revert`
* `GET /api/workspaces`
* `POST /api/workspaces`
* `DELETE /api/workspaces/{workspace_id}`
* `GET /api/workspaces/{workspace_id}`
* `PATCH /api/workspaces/{workspace_id}`
* `GET /api/workspaces/{workspace_id}/invitations`
* `POST /api/workspaces/{workspace_id}/invitations`
* `PATCH /api/workspaces/{workspace_id}/transfer-ownership`
* `GET /health`
* `GET /health/db`
* `GET /health/r2`
* `GET /healthz`

## BFF routes

These are the dashboard's own routes. Most are thin proxies that add auth and forward the body to FastAPI.

| Route                              | FastAPI target          | Used by                                 |
| ---------------------------------- | ----------------------- | --------------------------------------- |
| `GET /api/cloud/me`                | `/api/me`               | Profile panel                           |
| `PATCH /api/cloud/me`              | `/api/me`               | Profile opt-ins                         |
| `POST /api/cloud/me`               | `/api/me/...`           | DSAR actions (export, delete, restrict) |
| `GET /api/cloud/workspaces`        | `/api/workspaces`       | Workspace switcher                      |
| `POST /api/cloud/workspaces`       | `/api/workspaces`       | Workspace creation                      |
| `GET /api/cloud/projects`          | `/api/projects`         | Project list                            |
| `POST /api/cloud/projects`         | `/api/projects`         | Project creation                        |
| `GET /api/cloud/runs`              | `/api/runs`             | Recent runs table                       |
| `POST /api/cloud/api-keys`         | `/api/api-keys`         | API key issuance (one-time secret)      |
| `POST /api/cloud/shares`           | `/api/shares`           | Create / revoke public share            |
| `POST /api/cloud/billing/checkout` | `/api/billing/checkout` | Stripe Checkout session                 |
| `POST /api/cloud/billing/portal`   | `/api/billing/portal`   | Stripe Customer Portal                  |
| `POST /api/cloud/sample-run`       | `/api/runs` (synthetic) | Onboarding "upload sample run"          |
| `POST /api/workspace/selection`    | local selection state   | Workspace switcher                      |

The catch-all proxy `/api/[...path]` is intentionally allowlisted. It forwards only:

* `GET /health`
* `GET /api/status`
* `GET /api/runs`, `GET /api/runs/{run_id}`, and run manifest/metrics/log/signature/compliance mapping reads
* `GET /api/artifacts/{artifact_id}`
* `GET /api/claims`, `GET /api/claims/{claim_id}`
* `GET /api/museum`
* workspace/project list and creation
* API-key creation
* share list/create/revoke
* sync run create and artifact initiate/commit/abort

Unsupported methods, paths, or query parameters are rejected before Cloud is called.

Machine-checked BFF proxy allowlist:

* `GET /health`
* `GET /api/status`
* `GET /api/runs`
* `GET /api/runs/{id}`
* `GET /api/runs/{id}/{manifest|metrics|log|signature|compliance_mapping}`
* `GET /api/artifacts/{id}`
* `GET /api/claims`
* `GET /api/claims/{id}`
* `GET /api/museum`
* `GET /api/workspaces`
* `POST /api/workspaces`
* `GET /api/projects`
* `POST /api/projects`
* `POST /api/api-keys`
* `GET /api/shares`
* `POST /api/shares`
* `POST /api/shares/{uuid}/revoke`
* `POST /api/sync/runs`
* `POST /api/sync/artifacts/{initiate|commit|abort}`

Plus auth routes used by the dashboard's session flow:

| Route                           | Purpose                         |
| ------------------------------- | ------------------------------- |
| `POST /api/auth/login`          | Supabase email/password sign-in |
| `POST /api/auth/signup`         | Supabase email/password sign-up |
| `POST /api/auth/logout`         | Supabase sign-out               |
| `POST /api/auth/reset-password` | Supabase reset email request    |
| `POST /api/auth/session`        | Current session check           |

Dashboard pages also cover forgot-password, reset-password, and verify-email flows. Public-share routes are separate from authenticated dashboard routes:

| Route                             | Purpose                                        |
| --------------------------------- | ---------------------------------------------- |
| `GET /api/public/r/[token]`       | Resolve a public share token to a run snapshot |
| `GET /api/public/vla-leaderboard` | Return public VLA leaderboard snapshots        |

Public share reads use stable public-safe states:

| State                             | HTTP status | Code                             | Cache                           |
| --------------------------------- | ----------: | -------------------------------- | ------------------------------- |
| Active share                      |         200 | n/a                              | `private, no-store`             |
| Token not found or token mismatch |         404 | `share_not_found`                | `no-store`                      |
| Revoked                           |         410 | `share_revoked`                  | `no-store`                      |
| Expired                           |         410 | `share_expired`                  | `no-store`                      |
| Missing password                  |         401 | `share_password_required`        | `no-store`                      |
| Wrong password                    |         401 | `share_wrong_password`           | `no-store`                      |
| Password locked                   |         429 | `share_password_locked`          | `no-store` + rate-limit headers |
| Read rate limited                 |         429 | `share_rate_limited`             | `no-store` + rate-limit headers |
| Evidence verification failure     |         409 | `evidence_unverified`            | `no-store`                      |
| Artifact filtered or unavailable  |         409 | `artifact_filtered`              | `no-store`                      |
| Public payload policy rejected    |         409 | `public_payload_policy_rejected` | `no-store`                      |

Web clients must render public-only unavailable states for these codes and must
not fall back to dashboard controls or internal identifiers.

Machine-checked named BFF route list:

* `DELETE /api/[...path]`
* `GET /api/[...path]`
* `PATCH /api/[...path]`
* `POST /api/[...path]`
* `PUT /api/[...path]`
* `POST /api/auth/login`
* `POST /api/auth/logout`
* `POST /api/auth/reset-password`
* `POST /api/auth/session`
* `POST /api/auth/signup`
* `DELETE /api/cloud/api-keys`
* `POST /api/cloud/api-keys`
* `POST /api/cloud/billing/checkout`
* `POST /api/cloud/billing/portal`
* `GET /api/cloud/me`
* `PATCH /api/cloud/me`
* `POST /api/cloud/me`
* `DELETE /api/cloud/projects`
* `GET /api/cloud/projects`
* `POST /api/cloud/projects`
* `POST /api/cloud/projects/[projectId]/staging-retention-proof`
* `GET /api/cloud/runs`
* `POST /api/cloud/runs`
* `POST /api/cloud/sample-run`
* `GET /api/cloud/shares`
* `POST /api/cloud/shares`
* `DELETE /api/cloud/workspaces`
* `GET /api/cloud/workspaces`
* `POST /api/cloud/workspaces`
* `POST /api/e2e/cli-sync`
* `GET /api/public/r/[token]`
* `GET /api/public/vla-leaderboard`
* `POST /api/workspace/selection`

## Auth header

All `/api/cloud/*` calls expect a Supabase session cookie when called from the dashboard. The CLI does not call these BFF routes; it sends `Authorization: Bearer <wfx_…>` directly to FastAPI.

## Error shape

The BFF normalizes failures into one shape:

```json theme={null}
{
  "error": {
    "code": "login_required",
    "message": "Please log in again."
  }
}
```

`code` is stable. Common values seen by the dashboard:

| Code                 | When                                                                           |
| -------------------- | ------------------------------------------------------------------------------ |
| `login_required`     | Session missing or expired. The dashboard redirects to `/login`.               |
| `validation_error`   | Body shape mismatch. `details` carries the offending field path.               |
| `quota_exceeded`     | Workspace plan cap hit. The Billing panel surfaces an upgrade CTA.             |
| `not_found`          | Resource id does not exist or is invisible to this caller.                     |
| `unsupported_action` | Method not allowed (e.g. `POST /api/cloud/runs` without the e2e fixture flag). |

## E2E fixtures

When `WORLDFLUX_WEB_E2E_FIXTURES=1` is set, the BFF returns deterministic in-memory data instead of proxying to FastAPI. This is what Playwright tests use.

```ts theme={null}
if (e2eFixturesEnabled()) {
  return NextResponse.json(listE2ERuns());
}
```

`/api/e2e/cli-sync` is the synthetic endpoint the test suite hits to seed runs.
