# REST API

| Plane | Base URL |
|-------|----------|
| Control plane | `https://api.ozma.app` |
| Gateway (proxy) | `https://gateway.ozma.app` |

OpenAPI (hand-maintained): [https://ozma.app/.well-known/openapi.json](https://ozma.app/.well-known/openapi.json)

## Envelope

Success:

```json
{ "ok": true, "data": {}, "meta": { "request_id": "req_01…" } }
```

Failure:

```json
{ "ok": false, "error": { "code": "validation_failed", "message": "…" }, "meta": { "request_id": "req_01…" } }
```

Gateway success adds billing fields: `cost_cents`, `price_cents_effective`, `credit_applied_cents`, `billable_cents`, optional `price_increase` / `discount`, and idempotency `replayed`.

## Authentication

```http
Authorization: Bearer <token>
```

| Token | Scopes |
|-------|--------|
| `ozma_live_…` | Per key record; anonymous register = consumer only; email verify adds `billing:write` + `provider:write` |
| `sess_…` | Full scopes; control plane only (not gateway) |

Scopes: `catalog:read`, `proxy:call`, `keys:write`, `usage:read`, `billing:write`, `provider:write`.

## Public

| Method | Path | Description |
|--------|------|-------------|
| GET | `/health` | Service status |
| POST | `/v1/signup` | Signup → org + key + session; Turnstile in prod; credits after verify |
| POST | `/v1/register` | Agent auto-register → key, **0 credits**; Turnstile-free |
| POST | `/v1/auth/signup` | Alias signup (session); Turnstile in prod |
| POST | `/v1/auth/login/request` | Email 6-digit login code |
| POST | `/v1/auth/login/verify` | Exchange code → session; elevates key; starter credits |
| POST | `/v1/auth/logout` | Revoke current session |
| POST | `/v1/auth/verify-email` | Verify email → grant starter credits |
| GET | `/v1/auth/me` | Session user + org (session only) |
| DELETE | `/v1/auth/sessions` | Revoke all sessions |
| GET | `/v1/search` | Catalog search (`q`, `category`, `sort`, `limit`, `offset`) |
| GET | `/v1/categories` | Categories with API counts |
| GET | `/v1/apis/:slug` | Listing detail + provider + legal/terms |
| GET | `/v1/apis/:slug/documents` | Active/scheduled legal documents |
| GET | `/v1/apis/:slug/documents/:kind/versions/:version` | Frozen document snapshot |
| GET | `/v1/apis/:slug/terms` | Effective terms + optional acceptance status when authed |
| POST | `/v1/apis/:slug/terms/accept` | **Session** — accept provider terms |
| GET | `/v1/providers` | Provider directory |
| GET | `/v1/providers/:slug` | Public provider profile |
| GET | `/v1/assets/avatars/:orgId/:file` | Avatar bytes |
| GET | `/v1/apis/:slug/stats` | Provider stats by window |
| GET | `/v1/apis/:slug/history` | Score snapshots |
| GET | `/v1/apis/:slug/ranks` | Leaderboard ranks |
| GET | `/v1/market/overview` | Market aggregates |
| GET | `/v1/apis/:slug/schema` | Proxy paths + schemas |
| GET | `/v1/apis/:slug/endpoints` | Raw endpoint rows |
| GET | `/v1/apis/:slug/openapi` | OpenAPI export |
| GET | `/v1/leaderboards/:board` | Merit leaderboards |
| POST | `/v1/stripe/webhook` | Stripe events (signed) |
| POST | `/v1/catalog/request-api` | Request missing catalog API |
| GET | `/v1/catalog/llms.txt` | Agent catalog |
| GET | `/v1/catalog/sitemap.xml` | Dynamic sitemap |
| GET/POST | `/v1/growth/unsubscribe` | Outreach unsubscribe |

## Authenticated

Requires Bearer (API key or session). Scope column shows minimum when not "any".

| Method | Path | Scope | Description |
|--------|------|-------|-------------|
| GET | `/v1/market/activity` | any (auth required) | Recent global usage ticker |
| GET | `/v1/me` | any | Org, credits, billing mode |
| GET/PATCH | `/v1/me/profile` | `provider:write` | Own org profile |
| POST | `/v1/me/avatar` | `provider:write` | Avatar upload (503 if not configured) |
| GET/POST/PATCH/DELETE | `/v1/keys[/:id]` | `keys:write` | API key CRUD + caps |
| GET | `/v1/usage` | `usage:read` | Usage history |
| GET | `/v1/me/terms-acceptances` | any | Consumer term acceptances |
| GET | `/v1/billing/packs` | any | Credit packs |
| GET | `/v1/billing/balance` | any | Balance + suspension state |
| POST | `/v1/billing/checkout` | `billing:write` | Stripe Checkout URL |
| POST | `/v1/billing/setup-intent` | `billing:write` | SetupIntent for card |
| POST | `/v1/billing/portal` | `billing:write` | Billing Portal URL |
| POST | `/v1/billing/auto-reload` | `billing:write` | Auto-reload settings |

## Provider

Requires `provider:write` (verified key or session).

| Method | Path | Description |
|--------|------|-------------|
| POST | `/v1/provider/apis` | Create/import draft API |
| GET | `/v1/provider/apis` | List own APIs |
| GET | `/v1/provider/apis/:id` | Detail + pricing |
| PATCH | `/v1/provider/apis/:id` | Metadata patch |
| POST | `/v1/provider/apis/:id/unpublish` | Live → draft |
| PATCH | `/v1/provider/apis/:id/pricing` | Endpoint prices/discounts |
| DELETE | `/v1/provider/apis/:id/pricing/pending/:endpointId` | Cancel scheduled increase |
| POST | `/v1/provider/apis/:id/credentials` | Store upstream secret |
| POST | `/v1/provider/apis/:id/verify` | Upstream smoke test (2xx) |
| POST | `/v1/provider/apis/:id/publish` | Go live — `{ "accept_terms": true }` required |
| POST/GET | `/v1/provider/documents` | Create/list legal documents |
| GET | `/v1/provider/documents/:id` | Document detail |
| POST | `/v1/provider/documents/:id/preview` | Refresh draft snapshot |
| POST | `/v1/provider/documents/:id/activate` | Activate document |
| POST/GET | `/v1/provider/domains` | Domain verification |
| POST/GET | `/v1/provider/compliance-claims` | Compliance claims |
| GET | `/v1/provider/analytics` | GMV, payouts, calls |
| POST | `/v1/provider/connect/onboard` | Stripe Connect URL |
| GET | `/v1/provider/connect/status` | Connect readiness |

## Gateway

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| ALL | `/v1/proxy/:apiSlug/*` | `ozma_live_…` + `proxy:call` | Proxied billed call |

See [Gateway reference](/docs/reference/gateway).

## Related clients

- [CLI](/docs/reference/cli)
- [SDK](/docs/reference/sdk)
- [MCP](/docs/reference/mcp)
- [Errors](/docs/reference/errors)
