# Authentication

Ozma uses two credential types on the **control plane** (`https://api.ozma.app`) and one on the **gateway** (`https://gateway.ozma.app`).

## API keys vs sessions

| Credential | Format | Where it works | Scopes |
|------------|--------|----------------|--------|
| **API key** | `ozma_live_…` (24 chars after prefix) | Control plane + gateway | Stored on the key record; see below |
| **Session token** | `sess_…` | Control plane only | Full marketplace scopes |

Send either as:

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

**Gateway rejects sessions.** Proxy calls require an API key (`ozma call`, SDK `proxyCall`, MCP `call_api`). The web dashboard uses sessions for UI actions (billing portal, terms acceptance, profile edits).

The CLI and SDK store both in `~/.ozma/config.json`. When both are present, the control plane prefers the **session token**; the gateway always uses the **API key**.

## Scopes

| Scope | Purpose |
|-------|---------|
| `catalog:read` | Search, inspect, leaderboards |
| `proxy:call` | Gateway proxy calls |
| `keys:write` | Create, revoke, patch API keys |
| `usage:read` | Usage history |
| `billing:write` | Checkout, SetupIntent, portal, auto-reload |
| `provider:write` | Provider API lifecycle, documents, Connect |

**Anonymous auto-register** (`POST /v1/register`) mints a key with consumer scopes only: `catalog:read`, `proxy:call`, `keys:write`, `usage:read`. **No** `billing:write` or `provider:write`.

**Email verification elevates the same key** in place to add `billing:write` and `provider:write`. You do not need a second key for provider or billing tasks after verify.

## Human signup and OTP login

| Flow | Endpoint | Turnstile (prod) | Credits |
|------|----------|------------------|---------|
| Web signup | `POST /v1/signup` or `POST /v1/auth/signup` | Required | $1.00 (100 cents) after email verify |
| Passwordless login | `POST /v1/auth/login/request` → `POST /v1/auth/login/verify` | N/A on verify | Starter credits granted idempotently on first verify |
| Email verify link | `POST /v1/auth/verify-email` | N/A | Unlocks withheld signup credits |
| Agent auto-register | `POST /v1/register` | **Not required** | **0 credits** |

OTP login sends a 6-digit code (10-minute TTL, rate-limited). The login-request endpoint returns a generic response to prevent email enumeration.

```bash
npx @ozma_app/cli login --email you@example.com --code 123456
npx @ozma_app/cli verify --token <verification_token>
```

Revoke the current session with `POST /v1/auth/logout` or `ozma logout`.

## Turnstile on signup

Production signup routes (`/v1/signup`, `/v1/auth/signup`) require a Cloudflare Turnstile token. The CLI `ozma signup` cannot supply a widget token and will be rejected in production — use [https://ozma.app/signup](https://ozma.app/signup) or `ozma login --email` instead.

`POST /v1/register` is Turnstile-free so agents can bootstrap a key programmatically, with **zero credits** until a human verifies email.

## POST /v1/register (agents)

```bash
curl -X POST https://api.ozma.app/v1/register
```

Response includes `api_key` (shown once), `org_id`, and `credit_cents: 0`. Free catalog APIs (`price_cents = 0`) work immediately. Paid APIs need email verification, prepaid credits, or a card on file.

Response header `X-Ozma-Key` may echo the new key for convenience.

## Safe storage

- Treat `ozma_live_…` like a password. Ozma stores only a SHA-256 hash; plaintext is shown **once** at creation.
- Never commit keys to git, paste them in public issues, or embed in client-side browser code.
- Prefer environment variables (`OZMA_API_KEY`) or secret managers in CI.
- Use per-agent keys with [budget caps](/docs/guides/keys-and-budgets) instead of sharing one org-wide key.
- Revoke compromised keys immediately: dashboard, `ozma keys revoke`, or `DELETE /v1/keys/:id`.

## What requires a human

Agents can discover, call free APIs, and manage keys autonomously. These steps need a human in a browser:

- Adding a payment method (Stripe SetupIntent / Billing Portal)
- Stripe Connect KYC for paid provider payouts
- Accepting **required** provider terms (`428` until org owner/admin accepts via session)

See [Quickstart (agent)](/docs/getting-started/quickstart-agent) and [Billing](/docs/guides/billing).
