# Ozma documentation corpus > Full public docs for agents. HTML: https://ozma.app/docs — Changelog: https://ozma.app/changelog # Architecture Ozma splits **control plane** (discovery, auth, billing, provider ops) from **data plane** (proxied API calls). Both run on Cloudflare Workers; state lives in Neon Postgres with Drizzle ORM. ## Planes | Component | URL | Role | |-----------|-----|------| | API worker | `https://api.ozma.app` | Catalog, keys, billing webhooks, provider CRUD, merit crons | | Gateway worker | `https://gateway.ozma.app` | Auth, spend counters, credential injection, proxy, usage enqueue | | MCP worker | `https://mcp.ozma.app` | MCP tools + OAuth 2.1 | | Web | `https://ozma.app` | Next.js + OpenNext Cloudflare Workers (SSR/ISR) marketplace, dashboard, docs | ## Gateway hot path `ALL /v1/proxy/:apiSlug/*`: 1. Validate API key (`proxy:call`) 2. Resolve live endpoint + price 3. Check required provider terms (428 if missing) 4. SpendCounter DO — budget / daily / per-call 5. Decrypt upstream credentials (AES-GCM); inject into outbound request 6. Forward to provider `base_url` 7. On success — record `usage_events`, update key spend, queue metering 8. On failure — refund reservation Stripe meter emission and Connect payouts run asynchronously via queues and scheduled crons on the API worker. ## Agent surfaces REST is the source of truth. **`@ozma_app/sdk`**, **`@ozma_app/cli`**, and **MCP** wrap the same endpoints. The web app runs **OpenNext SSR/ISR** on Cloudflare Workers; live panels (market ticker, dashboard) poll the API from the browser. Site SEO sitemap is ISR at `/sitemap.xml`; the API also exposes `GET /v1/catalog/sitemap.xml` for machines. Shared packages: `@ozma_app/core` (envelope, crypto), `@ozma/db`, `@ozma/merit`, `@ozma_app/sdk`. ## Merit & health Cron jobs roll up provider stats, refresh leaderboards, compute OzmaScores, probe live API health, and finalize scheduled price changes. Suspended APIs are excluded from public catalog merit. ## Learn more - [REST API reference](/docs/reference/api) - [Gateway reference](/docs/reference/gateway) - [Security model](/docs/concepts/security) --- # Merit engine & OzmaScore Ozma ranks APIs with **objective merit signals** derived from real proxy usage — not ads or manual curation. Scores roll up every hour from `usage_events` into `provider_stats` and `score_snapshots` via the merit engine in `packages/merit`. ## Five axes (30-day window) | Axis | Signal | |------|--------| | **Reliability** | Success rate × latency factor (Wilson lower bound) | | **Retention** | Repeat payers / unique payers (Wilson; min sample thresholds) | | **Adoption** | GMV + payer count (log-scaled; shrinkage on low volume) | | **Value** | Successful calls per dollar, adjusted for refunds | | **Momentum** | Paid calls in last 24h (hotness curve) | Small samples shrink toward neutral priors so new APIs cannot instantly outrank established ones with sparse data. ## OzmaScore (0–100) Category-normalized percentiles of each axis blend into a weighted score (reliability 30%, retention 25%, adoption 20%, value 15%, momentum 10%). Each API gets `rank_in_category` on snapshots. ## Search & leaderboards Six public leaderboards — `top_grossing`, `most_used`, `most_reliable`, `best_value`, `trending`, `new_rising` — refresh on cron. **New & Rising** uses Thompson sampling for low-data APIs with reserved exploration slots. When you search with a query, default sort `relevance` blends semantic match (~75%) with OzmaScore (~25%) among relevant candidates (merit ranking v2, default ON). ## Health & anti-gaming Daily health probes suspend APIs after three consecutive upstream failures — suspended listings drop from merit rollups. Self-dealing (same org as consumer and provider) is excluded. Payer concentration caps reduce merit when one consumer dominates an API's usage. Rankings are **earned from marketplace behavior** — transparent on every listing and API detail page. ## Learn more - [Discover APIs](/docs/guides/discover-apis) - [Provider profiles](/docs/guides/provider-profiles) --- # Security model Ozma is designed so agents can call third-party APIs **without ever seeing upstream provider secrets**. Security spans credential storage, key hashing, session hygiene, and abuse prevention. ## Upstream credentials Provider secrets are encrypted with **AES-256-GCM** before storage. Only the gateway decrypts at proxy time and injects them into outbound requests — never into API responses, MCP tool output, or usage logs. ## Ozma API keys - Format: `ozma_live_<24 chars>` (plaintext shown once at creation) - Storage: SHA-256 hash (+ optional pepper) — not reversible - Scoped: least privilege per key; anonymous register keys omit billing/provider scopes - Revocation: soft-delete; immediate effect on next request ## Sessions Session tokens (`sess_…`) are 30-day control-plane credentials created on signup or OTP login. They grant full scopes for dashboard operations but are **rejected on the gateway** — proxy calls require API keys. Passwordless login uses rate-limited 6-digit codes with constant-time verification. Logout deletes the server-side session row. ## Network hygiene - CORS allowlists production and local web origins - Stripe webhooks verified with signing secret - Secrets live in Wrangler / environment — never in git ## Abuse controls - Turnstile on human signup (not on agent register) - Per-IP and per-email signup rate limits - Gateway rate limits (~300/min per key) - Self-dealing proxy blocked (same consumer + provider org) ## Provider terms gate When providers require acceptance, the gateway fails closed with **428** before charging or calling upstream. Anonymous agent orgs must attach a verified human owner before acceptance. ## Reporting issues Report vulnerabilities via the contact addresses on [https://ozma.app/contact](https://ozma.app/contact). ## Learn more - [Authentication](/docs/getting-started/authentication) - [Keys & budgets](/docs/guides/keys-and-budgets) --- # Spend governance Spend governance is Ozma's **trust layer for autonomous agents**. Each API key can carry independent caps stored in Postgres and enforced synchronously on the gateway **before** upstream calls or billing. ## Cap fields | Field | Purpose | |-------|---------| | `budget_cents` | Lifetime spend ceiling | | `daily_cap_cents` | Maximum spend per UTC calendar day | | `per_call_max_cents` | Block individual calls above a price | | `spent_cents` / `daily_spent_cents` | Running totals (updated on success) | Unset caps mean unlimited for that dimension. The gateway SpendCounter Durable Object is authoritative for daily enforcement; counters rotate at UTC midnight. ## Enforcement behavior On each proxy call the gateway reserves spend, calls upstream, then commits or refunds: 1. Exceeds per-call max → `per_call_limit_exceeded` (402) 2. Exceeds lifetime budget → `insufficient_budget` (402) 3. Exceeds daily cap → `daily_cap_exceeded` (402) 4. Upstream failure → reservation refunded; **no charge** Metering to Stripe runs asynchronously after success. ## Surfaces Set caps via dashboard, `PATCH /v1/keys/:id`, CLI `ozma budget set` (flags: `--budget`, `--daily-cap`, `--per-call-max`), or MCP `set_budget`. `approval_mode` is accepted by API/CLI but **not enforced** yet — do not rely on it until implemented. ## Why it matters Delegating an API key to an agent without caps exposes your full org wallet. Per-key budgets let you run autonomous loops with predictable maximum loss — complementary to org-level billing limits and free-tier catalog APIs. ## Learn more - [Keys & budgets guide](/docs/guides/keys-and-budgets) - [Troubleshooting daily cap vs budget](/docs/guides/troubleshooting) --- # Agent MCP loop This example shows the recommended **discover → schema → call** loop for agents connected to Ozma MCP at `https://mcp.ozma.app/mcp`. ## 1. Connect ```bash npx @ozma_app/cli connect ``` Complete **OAuth 2.1** in the browser or configure a Bearer `ozma_live_…` key. See [Quickstart (agent)](/docs/getting-started/quickstart-agent). Install hub: [https://ozma.app/connect](https://ozma.app/connect) ## 2. Discover Tool: **`discover_tools`** or **`search_apis`** ``` query: "latest exchange rates USD EUR" sort: relevance max_results: 5 ``` Expected: `frankfurter` (Frankfurter Exchange Rates) among top results — free, finance category. Optional trust check: **`get_provider_profile`** with the provider slug from listing metadata. ## 3. Schema Tool: **`get_schema`** ``` slug: frankfurter ``` Returns proxy paths, methods, and parameter schemas. Note `GET /latest` accepts `from` and `to` query params. ## 4. Call (free API) Tool: **`call_api`** ``` slug: frankfurter endpoint: /latest method: GET input: { "from": "USD", "to": "EUR" } ``` Response envelope includes `meta.cost_cents: 0` for this free endpoint. CLI equivalent: ```bash npx @ozma_app/cli call frankfurter /latest --query from=USD --query to=EUR --json ``` ## 5. Human billing boundary (paid APIs) Agents can autonomously call **free** catalog APIs with a register key (`ozma_register` → 0 credits). For **paid** APIs or after trial credits exhaust: | Step | Who | Tool / surface | |------|-----|----------------| | Email verify | Human (once) | Web signup or `ozma login --email` — unlocks `billing:write` | | Add payment method | **Human required** | Dashboard [Billing](https://ozma.app/dashboard/billing), MCP `billing_setup` + Stripe.js, or `billing_portal` | | Set key budgets | Agent or human | MCP `set_budget` or `ozma budget set` | | Checkout prepaid pack | Human (optional) | MCP `create_checkout` or `ozma billing checkout` | `billing_setup` returns a SetupIntent `client_secret` only — card capture cannot complete inside the agent loop without a browser. ## 6. Required terms gate If `call_api` returns **428** `terms_acceptance_required`: 1. Agent: `get_api_terms` / `get_api_terms_status` (surface `document_id`, `content_hash`, `approval_url`) 2. Human session login (`ozma login --email`) or open the web `approval_url` 3. Human accepts: MCP `accept_api_terms` (session), or `ozma terms accept --yes` 4. Agent retries `call_api` ## 7. Usage audit Tool: **`get_usage`** — review spend for the org. ``` from: 2026-07-01 to: 2026-07-22 ``` ## Full onboarding doc Machine-readable agent onboarding: [https://ozma.app/onboarding.md](https://ozma.app/onboarding.md) ## Related - [MCP reference](/docs/reference/mcp) - [Authentication](/docs/getting-started/authentication) - [First API call](/docs/examples/first-call) --- # Your first API call This walkthrough uses **Frankfurter Exchange Rates** (`frankfurter`) — a free platform catalog API (`price_cents = 0`). No credit card required. ## 1. Get an API key **Option A — agent auto-register (0 credits, free APIs only):** ```bash npx @ozma_app/cli register --json # save api_key to ~/.ozma/config.json or export OZMA_API_KEY ``` **Option B — human signup ($1 trial after email verify):** Sign up at [https://ozma.app/signup](https://ozma.app/signup) and copy your key from [Dashboard → Keys](https://ozma.app/dashboard/keys). ```bash export OZMA_API_KEY=ozma_live_xxxxxxxxxxxxxxxxxxxxxxxx ``` ## 2. Inspect the listing ```bash npx @ozma_app/cli inspect frankfurter --schema --json ``` Note the proxy path: `GET /latest` with query params `from`, `to`. ## 3. Call through the gateway ```bash curl -s -H "Authorization: Bearer $OZMA_API_KEY" \ "https://gateway.ozma.app/v1/proxy/frankfurter/latest?from=USD&to=EUR" | jq ``` Or with the CLI: ```bash npx @ozma_app/cli call frankfurter /latest --query from=USD --query to=EUR --json ``` ## 4. Read the envelope ```json { "ok": true, "data": { "amount": 1.08, "base": "USD", "date": "2026-07-22", "rates": { "EUR": 0.92 } }, "meta": { "request_id": "req_01J…", "cost_cents": 0, "price_cents_effective": 0, "credit_applied_cents": 0, "billable_cents": 0 } } ``` - **`ok`** — success vs failure - **`data`** — upstream JSON body - **`meta.request_id`** — support correlation id - **`meta.cost_cents`** — what this call would cost (0 for free APIs) ## Alternative: httpbin.org For a request-echo example: ```bash curl -s -H "Authorization: Bearer $OZMA_API_KEY" \ "https://gateway.ozma.app/v1/proxy/httpbin-org/get?hello=ozma" | jq '.data' ``` ## Free vs paid | | Free (`price_cents = 0`) | Paid | |--|--------------------------|------| | Card required | No | Yes (after credits) | | Register-only key | Works | Blocked at 402 | | `meta.billable_cents` | 0 | May be > 0 postpaid | Some APIs require **provider terms acceptance** before calls succeed (HTTP **428**). Frankfurter and httpbin currently have no required terms. ## Next steps - [Discover APIs](/docs/guides/discover-apis) - [Keys & budgets](/docs/guides/keys-and-budgets) — cap agent spend - [Agent MCP loop](/docs/examples/agent-mcp-loop) --- # Publish from OpenAPI This example walks through publishing an API with the Ozma CLI. You need an **email-verified** key or session (`provider:write` scope). ```bash npx @ozma_app/cli login --email you@example.com ``` ## 1. Import OpenAPI ```bash npx @ozma_app/cli provider create ./openapi.yaml --json # or from URL: npx @ozma_app/cli provider create https://example.com/openapi.json --json ``` Save the returned `api_id` (e.g. `api_01J…`). ## 2. Patch metadata (optional) ```bash npx @ozma_app/cli provider patch api_01J… \ --name "My Weather API" \ --summary "Hourly forecasts" \ --category weather \ --json ``` ## 3. Store upstream credentials Use the **correct flags** — secret value, injection type, and header/query name: ```bash npx @ozma_app/cli provider credentials api_01J… \ --secret "sk_upstream_secret_here" \ --injection header \ --injection-name "X-Api-Key" \ --json ``` Other injection modes: `bearer`, `query` (with `--injection-name` as the query param key). Secrets are AES-GCM encrypted at rest and never returned via API. ## 4. Smoke-test upstream ```bash npx @ozma_app/cli provider verify api_01J… --json ``` Verify requires **2xx** responses from GET endpoints. Fix credentials or base URL if this fails. ## 5. Set endpoint pricing Per-endpoint cents — use **`--endpoint-id`** and **`--price-cents`**: ```bash # List endpoints from provider get npx @ozma_app/cli provider get api_01J… --json npx @ozma_app/cli provider pricing api_01J… \ --endpoint-id ep_01J… \ --price-cents 5 \ --json ``` Free endpoints: `--price-cents 0`. Bulk pricing via `--file pricing.json`. ## 6. Stripe Connect (paid APIs only) If any endpoint is paid, complete Connect before publish: ```bash npx @ozma_app/cli provider connect --json # Human completes KYC in browser — payouts_enabled + transfers must be active ``` Free-only APIs skip this step. ## 7. Publish (Ozma reseller attestation) **`--accept-terms` is required** — attests to Ozma platform/reseller terms, not your consumer-facing API terms: ```bash npx @ozma_app/cli provider publish api_01J… --accept-terms --json ``` Production paid publish re-runs upstream verify unless you have an admin skip (not available to normal providers). ## 8. Optional provider documents / terms After publish, attach legal documents for consumers: ```bash # Draft terms (display-only — shown on listing, no gateway gate) npx @ozma_app/cli provider documents create \ --kind terms \ --source hosted \ --version 1.0.0 \ --markdown ./terms.md \ --api-id api_01J… \ --acceptance-mode display_only \ --json # Activate (draft → active) npx @ozma_app/cli provider documents activate doc_01J… --json ``` For **required** acceptance (gateway **428** until accepted): ```bash npx @ozma_app/cli provider documents create \ --kind terms \ --source hosted \ --version 1.0.0 \ --markdown ./terms.md \ --api-id api_01J… \ --acceptance-mode required \ --json npx @ozma_app/cli provider documents activate doc_01J… --json ``` Consumers accept via session: `ozma terms accept --yes`. Other kinds: `privacy`, `aup`, `dpa` (see CLI help). ## 9. Confirm live ```bash npx @ozma_app/cli inspect my-api-slug --json curl "https://api.ozma.app/v1/apis/my-api-slug/openapi" ``` Listing appears at `https://ozma.app/apis/my-api-slug`. ## Related - [Publish & verify guide](/docs/guides/publish-api) - [Pricing & payouts](/docs/guides/pricing-payouts) - [Provider quickstart](/docs/getting-started/quickstart-provider) --- # FAQ ## Is Ozma a RapidAPI clone? No. Ozma is **agentic-first**: MCP + CLI + SDK, transparent merit rankings (OzmaScore), governed spend per key, and one proxy key for the whole catalog. See [Ozma vs RapidAPI](/vs/rapidapi). ## Do I need a credit card? Not for **free catalog APIs** (`price_cents = 0`) or while **trial credits** remain. Paid APIs after credits need a payment method (usage billing) or optional prepaid packs. ## Can agents manage everything? Agents cover most of the marketplace loop — discover, schema, call, keys, budgets, usage, and provider API lifecycle via MCP/CLI/SDK. **Humans are still required** for: adding the first payment method, Stripe Connect KYC (paid providers), and accepting required provider terms when the gateway returns **428**. ## What is the difference between API keys and sessions? API keys work on **both** control plane and gateway. Sessions work on **control plane only** (dashboard, billing portal, terms acceptance). Gateway proxy calls always need `ozma_live_…`. ## How much are trial credits? Human signup grants **$1.00** (100 cents) after email verification. Agent auto-register (`POST /v1/register`) gets **0 credits** but can call free APIs immediately. ## Why did my gateway call return 401 with a session token? Sessions are not accepted on `gateway.ozma.app`. Use your API key in `Authorization: Bearer ozma_live_…`. ## What is daily_cap_exceeded vs insufficient_budget? Both are HTTP 402. **`daily_cap_exceeded`** means today's daily cap on the key was hit (resets UTC midnight). **`insufficient_budget`** means the lifetime key budget would be exceeded. ## What does terms_acceptance_required (428) mean? The provider requires accepting their API terms before proxy calls. Log in with a session and run `ozma terms accept --yes` as org owner/admin. ## Can I publish an API without Stripe Connect? Yes if **all endpoints are free** (`price_cents = 0`). Paid production APIs require Connect with payouts and transfers enabled. ## What does publish --accept-terms mean? It attests to **Ozma platform/reseller terms** — separate from optional provider-authored consumer terms documents you may activate later. ## How does OzmaScore work? Five usage-derived axes (reliability, retention, adoption, value, momentum) blend into a 0–100 score. Rankings cannot be purchased. See [Merit engine](/docs/concepts/merit). ## Where are provider profiles? Public pages at `https://ozma.app/providers/{slug}` with bio, links, trust badges, and live APIs. [Provider profiles guide](/docs/guides/provider-profiles). ## How do I install MCP for Cursor? ```bash npx @ozma_app/cli connect ``` Or visit [https://ozma.app/connect](https://ozma.app/connect). ## Why does ozma signup fail in the terminal? Production signup requires Cloudflare Turnstile. Use [web signup](https://ozma.app/signup) or `ozma login --email` instead. ## What are Ozma's rate limits? Control plane ~**120**/min; gateway ~**300**/min per key prefix. See [Errors reference](/docs/reference/errors). ## Is upstream data stored? Successful proxy responses pass through to the caller; Ozma stores usage metadata (charges, latency, status) not full response bodies. Idempotency replay returns `data: null`. ## Where is machine-readable documentation? - [https://ozma.app/llms.txt](https://ozma.app/llms.txt) — catalog - [https://ozma.app/llms-full.txt](https://ozma.app/llms-full.txt) — full docs - [https://ozma.app/onboarding.md](https://ozma.app/onboarding.md) — agent playbook - Append `.md` to any `/docs/…` URL for raw markdown ## Where are legal terms? **Ozma platform:** [Terms](/terms), [Privacy](/privacy), [Legal hub](/legal). **Provider API terms:** When active, shown on `/apis/{slug}/terms`. Required terms gate gateway calls until accepted. ## How do I report a security issue? Use the contact page at [https://ozma.app/contact](https://ozma.app/contact). ## What take rate do providers pay? Standard providers: **10%** platform take (**90%** net). Founding providers: **0%** take. See [Pricing & payouts](/docs/guides/pricing-payouts). --- # 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 ``` **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 ``` 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). --- # Quickstart (agent) ## One-command install ```bash npx @ozma_app/cli connect ``` Detects Cursor, Claude Code, Codex, VS Code, Windsurf, Cline, Gemini, Zed, and Claude Desktop. Install hub: [https://ozma.app/connect](https://ozma.app/connect) ## MCP endpoint - URL: `https://mcp.ozma.app/mcp` - Auth: **OAuth 2.1** (recommended) or Bearer `ozma_live_…` - Echo `Mcp-Session-Id` header for persistent identity Production rejects unauthenticated MCP requests. ## OAuth vs Bearer | Mode | When to use | |------|-------------| | **OAuth** | Interactive agents — email OTP proves ownership; full scopes on approve | | **Bearer key** | Scripts/CI — use email-verified key with required scopes | Auto-register via MCP `ozma_register` gives **0 credits** and consumer scopes until email verify elevates the key. ## Typical agent flow 1. **`discover_tools`** / **`search_apis`** — find listings 2. **`get_schema`** — proxy paths and parameters 3. **`provision_access`** — ensure API key exists 4. **`call_api`** — proxied call through gateway 5. **`get_provider_profile`** — trust context for the seller CLI parity: ```bash npx @ozma_app/cli register npx @ozma_app/cli search "weather" --json npx @ozma_app/cli call frankfurter /latest --query from=USD --query to=EUR --json ``` ## Human billing boundary Agents autonomously handle discovery, schema inspection, free API calls, keys, and budgets. These require a **human in a browser**: - First payment method (SetupIntent / Billing Portal) - Stripe Connect KYC for paid provider payouts - Accepting **required** provider terms (`428` gate) See [Billing guide](/docs/guides/billing) and [Authentication](/docs/getting-started/authentication). ## Machine-readable onboarding Full agent playbook: [https://ozma.app/onboarding.md](https://ozma.app/onboarding.md) Catalog index: [https://ozma.app/llms.txt](https://ozma.app/llms.txt) Full docs corpus: [https://ozma.app/llms-full.txt](https://ozma.app/llms-full.txt) ## Next - [Agent MCP loop example](/docs/examples/agent-mcp-loop) - [MCP reference](/docs/reference/mcp) - [CLI reference](/docs/reference/cli) --- # Quickstart (consumer) ## 1. Create an account Sign up at [https://ozma.app/signup](https://ozma.app/signup). You receive a session token and API key. Trial credits (**$1.00 / 100 cents**) unlock after email verification. Agents without a human can `npx @ozma_app/cli register` for a key with **0 credits** — sufficient for free catalog APIs. ## 2. Store your key ```bash export OZMA_API_KEY=ozma_live_xxxxxxxxxxxxxxxxxxxxxxxx ``` Or save via `ozma login --key` / [Dashboard → Keys](https://ozma.app/dashboard/keys). ## 3. Find an API Frankfurter Exchange Rates is a free platform API (`frankfurter`): ```bash npx @ozma_app/cli search "exchange rates" --json npx @ozma_app/cli inspect frankfurter --schema --json ``` ## 4. Call through the gateway ```bash curl -s -H "Authorization: Bearer $OZMA_API_KEY" \ "https://gateway.ozma.app/v1/proxy/frankfurter/latest?from=USD&to=EUR" | jq ``` CLI: ```bash npx @ozma_app/cli call frankfurter /latest --query from=USD --query to=EUR --json ``` Expected envelope: `ok: true`, exchange rate data in `data`, `meta.cost_cents: 0`. Alternative free echo API: ```bash npx @ozma_app/cli call httpbin-org /get --query hello=ozma --json ``` ## Provider terms (428) Some APIs require accepting provider terms before proxy calls. If you receive **428** `terms_acceptance_required`: ```bash npx @ozma_app/cli terms show my-api-slug npx @ozma_app/cli login --email you@example.com npx @ozma_app/cli terms accept my-api-slug --yes ``` ## Paid APIs Free endpoints never need a card. Paid calls consume trial credits first, then usage billing if a card is on file. ## Next - [First API call example](/docs/examples/first-call) - [API keys & budgets](/docs/guides/keys-and-budgets) - [Billing](/docs/guides/billing) --- # Quickstart (provider) ## 1. Authenticate with provider scope Email-verified signup (or login) elevates your key to include `provider:write`. ```bash npx @ozma_app/cli login ``` ## 2. Import OpenAPI ```bash npx @ozma_app/cli publish ./openapi.yaml # or npx @ozma_app/cli provider create https://example.com/openapi.json ``` ## 3. Credentials → verify → pricing ```bash npx @ozma_app/cli provider credentials --secret "sk_upstream" --injection header --injection-name "X-Api-Key" npx @ozma_app/cli provider verify npx @ozma_app/cli provider pricing --endpoint-id --price-cents 5 ``` ## 4. Stripe Connect (paid APIs) ```bash npx @ozma_app/cli provider connect ``` Complete Express KYC in the browser. ## 5. Publish ```bash npx @ozma_app/cli provider publish --accept-terms ``` Optional: publish provider-authored API terms with `ozma provider documents …` (see [Publish & verify](/docs/guides/publish-api)). ## 6. Build trust with a public profile ```bash npx @ozma_app/cli profile update --bio "We ship weather data" --website https://example.com --public ``` Or use [Dashboard → Profile](https://ozma.app/dashboard/profile). ## Next - [Publish & verify](/docs/guides/publish-api) - [Provider profiles & trust](/docs/guides/provider-profiles) --- # What is Ozma Ozma is an **agentic-first API marketplace**. Consumers discover and call third-party APIs through one Ozma key. Providers publish APIs, set usage prices, and earn via Stripe Connect. Rankings use transparent merit (OzmaScore), not ads. ## How it fits together | Plane | Role | URL | |------|------|-----| | Control plane | Catalog, keys, billing, provider APIs | `https://api.ozma.app` | | Data plane | Proxied, billed API calls | `https://gateway.ozma.app` | | MCP | Tool surface for agents | `https://mcp.ozma.app/mcp` | | Web | Marketplace + dashboard + docs | `https://ozma.app` | ## Who it's for - **Consumers** — humans or apps that need APIs with spend caps and trial credits. - **Agents** — MCP / CLI / SDK clients that search, call, and manage access programmatically. - **Providers** — teams that publish OpenAPI-backed APIs and build public trust profiles. ## Next - [Consumer quickstart](/docs/getting-started/quickstart-consumer) - [Agent quickstart](/docs/getting-started/quickstart-agent) - [Provider quickstart](/docs/getting-started/quickstart-provider) --- # Billing & credits Ozma billing is **usage-first** (OpenAI-style): trial credits → optional card on file → Stripe meter → invoice. Prepaid credit packs remain available for pay-in-advance workflows. Dashboard: [https://ozma.app/dashboard/billing](https://ozma.app/dashboard/billing) ## Trial credits | Signup path | Credits | When granted | |-------------|---------|--------------| | Human signup (`/v1/signup`, `/v1/auth/signup`) | **$1.00** (100 cents) | After email verification (prod default) | | OTP login first verify | 100 cents | Idempotent grant | | Agent `POST /v1/register` | **0** | Immediate — free APIs only | Credits apply first on each call; only the remainder (`billable_cents`) meters to Stripe for **postpaid** orgs. ## Free APIs Catalog endpoints with **`price_cents = 0`** never require a card and never burn credits. Platform seed APIs (e.g. Frankfurter, httpbin.org) are permanently free. ## Usage / postpaid (primary) 1. Human adds a card via SetupIntent (`ozma billing setup`) or Billing Portal (`ozma billing portal`). 2. Org flips to `billing_mode = postpaid`. 3. After credits exhaust, successful calls enqueue Stripe meter events. 4. Stripe invoices on the subscription cycle. 5. [Spend governance](/docs/guides/keys-and-budgets) still applies on every call. ```bash npx @ozma_app/cli billing setup --json # client_secret — confirm in browser npx @ozma_app/cli billing portal --json # manage cards/invoices npx @ozma_app/cli balance --json ``` **Human card step required:** agents receive SetupIntent secrets but cannot complete 3DS/card entry without a browser (dashboard or Stripe.js). ## Prepaid packs (optional) | Pack | Price | Bonus | |------|-------|-------| | `starter` | $10 | — | | `growth` | $25 | +$2.50 | | `scale` | $100 | +$15 | | Custom | $5–$1,000 | — | ```bash npx @ozma_app/cli billing packs npx @ozma_app/cli billing checkout --pack starter ``` Checkout requires `billing:write` (verified email). Completed sessions credit the org wallet idempotently. ## Auto-reload Optional wallet refill when balance drops below a threshold (requires card on file): ```bash npx @ozma_app/cli billing auto-reload --enable --threshold 200 --amount 1000 ``` Maps to `POST /v1/billing/auto-reload`. Less central under pure usage invoicing but useful for prepaid-heavy workflows. ## Dunning & suspension Postpaid orgs with **`invoice.payment_failed`** three times are **suspended**. Gateway returns **402** `payment_required` on billable calls until `invoice.paid` clears suspension. Prepaid orgs with zero credits and no card hit **402** with dashboard top-up hints. ## What agents can vs cannot do | Action | Agent | Human | |--------|-------|-------| | Call free APIs | Yes | Yes | | Call paid APIs with credits/card on file | Yes | Yes | | Add first payment method | No | Yes | | Stripe Connect KYC (providers) | No | Yes | | Checkout / portal | Opens URL | Completes payment | ## Related - [Pricing & payouts](/docs/guides/pricing-payouts) - [Keys & budgets](/docs/guides/keys-and-budgets) - [Troubleshooting](/docs/guides/troubleshooting) --- # Connect your agent ## Recommended ```bash npx @ozma_app/cli connect ``` Flags: - `--pick` — choose which detected agents to configure - `--client cursor` — single client - `--project` — write project-scoped configs in cwd - `--key ozma_live_…` — embed a key - `--dry-run` — preview without writing ## Manual MCP (Cursor example) ```json { "mcpServers": { "ozma": { "url": "https://mcp.ozma.app/mcp", "headers": { "Authorization": "Bearer ozma_live_…" } } } } ``` ## Browser install hub Visit [ozma.app/connect](https://ozma.app/connect) for copy-paste configs per editor. --- # Discover APIs Ozma exposes catalog discovery on the web, REST, CLI, MCP, and SDK. All surfaces share the same control plane at `https://api.ozma.app`. ## Search ```http GET /v1/search?q=&category=&sort=&limit=&offset= ``` | Param | Description | |-------|-------------| | `q` | Keyword or natural-language query (hybrid semantic + keyword when embeddings exist) | | `category` | Category slug filter | | `sort` | See sort enums below | | `limit` | Default 50, max 500 | | `offset` | Pagination offset | Default sort is `ozma_score` when browsing without `q`. When `q` is set, default sort is `relevance`. ```bash npx @ozma_app/cli search "exchange rates" --category finance --sort relevance --json ``` Each result includes merit axes (`ozma_score`, `reliability`, `adoption`, `retention`, `value`, `momentum`), latency (`p95_ms`, `p50_ms`), `calls_30d`, `gmv_cents_30d`, `paid_calls_24h`, `min_price_cents`, optional `list_min_price_cents` / `has_active_discount` (time-limited sale), and tags. With merit ranking v2 active, responses may include a `why` blend explanation. On the web marketplace, discounted APIs show a **Sale** badge (list price struck through when higher). Use the **On Sale** view to filter to active discounts only. ## Sort enums | Value | Use case | |-------|----------| | `ozma_score` | Default browse — composite merit | | `relevance` | Default when `q` set; blends semantic match with OzmaScore (25% when merit v2 ON) | | `price` | Cheapest effective price first | | `new` | Recently published | | `calls` | Most calls (30d) | | `gmv` | Highest gross merchandise value (30d) | | `reliability` | Wilson reliability axis | | `value` | Successes per dollar | | `momentum` | Paid calls in last 24h (hotness) | Kill switch: set `MERIT_RANKING_V2=false` on the API worker (default ON in production). ## Categories ```http GET /v1/categories ``` Returns `{ slug, name, api_count }` for each category with live APIs. ## Leaderboards ```http GET /v1/leaderboards/:board?category= ``` | Board | Ranks by | |-------|----------| | `top_grossing` | GMV | | `most_used` | Call volume | | `most_reliable` | Reliability axis | | `best_value` | Value axis | | `trending` | Momentum | | `new_rising` | Thompson sampling for low-data APIs; exploration slots for healthy newcomers | ```bash npx @ozma_app/cli leaderboard most_reliable --category finance ``` Per-API ranks: `GET /v1/apis/:slug/ranks`. ## Market overview ```http GET /v1/market/overview ``` Aggregates catalog totals, movers, and trending APIs. Authenticated activity ticker: ```http GET /v1/market/activity?limit= ``` Requires Bearer auth (API key or session) — not a public endpoint. ## Web URL state The marketplace at [https://ozma.app/](https://ozma.app/) mirrors discovery params in the query string: | Param | Example | Effect | |-------|---------|--------| | `q` | `?q=weather` | Search query | | `category` | `?category=finance` | Category filter | | `sort` | `?sort=relevance` | Sort order | | `view` | `?view=grid` | Layout (grid/list) | Share or bookmark URLs to preserve filter state. ## API detail and schema | Endpoint | Purpose | |----------|---------| | `GET /v1/apis/:slug` | Listing, endpoints, pricing, provider, legal/terms summary | | `GET /v1/apis/:slug/schema` | Proxy paths + JSON schemas for agent tooling | | `GET /v1/apis/:slug/openapi` | Machine-readable OpenAPI | | `GET /v1/apis/:slug/stats?window=` | Provider stats (`24h`, `7d`, `30d`, `all`) | ```bash npx @ozma_app/cli inspect frankfurter --schema --json ``` ## Merit blend (relevance search) When searching with `sort=relevance`, Ozma blends normalized semantic relevance (~75%) with normalized OzmaScore (~25%) among candidates that pass the relevance floor. Rankings are earned from usage signals — not paid placement. See [Merit engine](/docs/concepts/merit). ## Provider trust Catalog rows and API detail include a **provider** block: `slug`, `name`, `avatar_url`, `founding_provider`, `verified`. Public profiles live at `https://ozma.app/providers/{slug}` with bio, links, socials, trust badges, and live API cards. Use provider context when agents choose between similar listings. Required provider terms (`acceptance_mode: required`) gate gateway calls until the consumer org accepts — see [Troubleshooting](/docs/guides/troubleshooting). ## Agent shortcuts - MCP: `discover_tools`, `search_apis`, `get_market_overview`, `get_leaderboard`, `get_provider_profile` - Machine catalog: [https://ozma.app/llms.txt](https://ozma.app/llms.txt), `GET /v1/catalog/llms.txt` - Request a missing API: `POST /v1/catalog/request-api` or `ozma request-api --query "…"` --- # API keys & budgets API keys are org-scoped credentials for programmatic access. Manage them in [Dashboard → Keys](https://ozma.app/dashboard/keys), REST, CLI, or MCP `set_budget`. ## Create keys ```bash npx @ozma_app/cli keys create --name "Production agent" --json npx @ozma_app/cli keys create --name "Capped agent" --budget 5000 --json ``` Plaintext is shown **once**. Store securely. ## Spend caps Caps live on each key and are enforced **synchronously** on the gateway before upstream calls. | Field | CLI flag | Meaning | |-------|----------|---------| | `budget_cents` | `--budget` | Lifetime spend ceiling | | `daily_cap_cents` | `--daily-cap` | Max spend per UTC calendar day | | `per_call_max_cents` | `--per-call-max` | Reject calls priced above this | | `approval_mode` | `--approval-mode` | **Not enforced** today — stored for forward compatibility only | Null cap = unlimited for that dimension. ```bash npx @ozma_app/cli budget set --budget 10000 --daily-cap 500 --per-call-max 50 npx @ozma_app/cli keys patch key_123 --budget 2000 --daily-cap 200 ``` **Use `--budget`, `--daily-cap`, `--per-call-max`** — not legacy `--budget-cents` style flags on the budget subcommand. ## Gateway error codes | Code | When | |------|------| | `insufficient_budget` | Lifetime budget would be exceeded | | `daily_cap_exceeded` | Daily cap would be exceeded | | `per_call_limit_exceeded` | Single call price too high | All return HTTP **402**. Read `error.code` — do not confuse daily cap with lifetime budget. Daily counters reset at **UTC midnight** on the gateway SpendCounter Durable Object. ## Scopes | Key type | Scopes | |----------|--------| | Anonymous register | `catalog:read`, `proxy:call`, `keys:write`, `usage:read` | | Email verified | + `billing:write`, `provider:write` | Revoke compromised keys immediately: `ozma keys revoke `. ## Usage visibility ```bash npx @ozma_app/cli usage --from 2026-07-01 --to 2026-07-22 --json ``` ## Design principles - **Per-key isolation** — delegate one key per agent with its own caps. - **Fail closed** — set caps are hard limits. - **No bill on upstream failure** — failed calls refund spend reservation. ## Related - [Authentication](/docs/getting-started/authentication) - [Spend governance concept](/docs/concepts/spend-governance) - [Troubleshooting](/docs/guides/troubleshooting) --- # Pricing, payouts & Connect Providers set **per-endpoint** prices in US cents. Consumers pay the **effective** price per successful gateway call. ## Take rate | Provider type | Ozma take | Provider net | |---------------|-----------|--------------| | Standard | **10%** | **90%** | | Founding provider | **0%** | **100%** | Founding status is assigned by Ozma — displayed as a trust badge on provider profiles. ## Setting prices ```bash npx @ozma_app/cli provider get api_123 --json # find endpoint ids npx @ozma_app/cli provider pricing api_123 \ --endpoint-id ep_456 \ --price-cents 5 \ --json ``` Bulk updates via `--file pricing.json`. Free endpoints use `--price-cents 0`. ## Price change policy (live APIs) | Change | Behavior | |--------|----------| | **Decrease** | Immediate | | **Increase** | Scheduled **+7 days**; email to recent callers; max one increase per endpoint per 30 days | | **Discount** | Temporary reduced price (1–90 days) | Cancel a pending increase: ```bash npx @ozma_app/cli provider pricing-cancel api_123 ep_456 ``` Gateway responses expose effective pricing in `meta.price_cents_effective`, with optional `meta.price_increase` or `meta.discount` objects. API detail includes `pending_increases`, `active_discounts`, and `price_history`. ## Connect payouts Paid APIs in production require Stripe Connect Express with **`payouts_enabled`** and **`transfers` active** before publish. ```bash npx @ozma_app/cli provider connect --json # Check readiness: GET https://api.ozma.app/v1/provider/connect/status ``` Daily payout job transfers settled provider share. Free-only listings skip Connect. Check earnings: ```bash npx @ozma_app/cli provider analytics --json ``` Returns **`total_gmv_cents`** (gross) and **`provider_payout_cents`** (net after take). ## Consumer-facing price transparency Listing pages and `GET /v1/apis/:slug` show current and scheduled prices so agents can budget before calling. ## Related - [Publish an API](/docs/guides/publish-api) - [Billing (consumer)](/docs/guides/billing) --- # Provider profiles & trust Every organization can maintain a **public profile** at `/providers/{slug}`. Profiles help consumers and agents evaluate who is behind an API. ## Fields - Display name & slug - Avatar (upload or `https://` URL) - Bio (≤ 160 chars) & long-form about (markdown) - Website, docs URL, support email, location - Socials: GitHub, X, LinkedIn, Discord, YouTube, Telegram - `public_profile` toggle ## Visibility - Opt-in via `public_profile: true`, **or** - Automatically listable when the org has **live APIs** ## Trust badges | Badge | Meaning | |-------|---------| | Email verified | Owner email verified | | Terms accepted | Ozma platform/reseller terms attested at publish | | Provider terms | Optional API-scoped legal document (display or required acceptance) | | Payouts verified | Stripe Connect account linked | | Founding Provider | Launch founding flag | ## Agent access ```bash npx @ozma_app/cli providers npx @ozma_app/cli providers acme npx @ozma_app/cli profile show npx @ozma_app/cli profile update --bio "…" --website https://… --public ``` REST: `GET /v1/providers`, `GET /v1/providers/:slug`, `GET|PATCH /v1/me/profile`, `PATCH /v1/provider/apis/:id` (metadata + completeness scorecard). ## Legal documents Providers may publish terms, privacy, and other documents via `POST /v1/provider/documents` → activate. See [Publish & verify](/docs/guides/publish-api). MCP tools: `list_providers`, `get_provider_profile`, `get_my_profile`, `update_my_profile`. --- # Publish & verify an API Publishing turns a draft API into a live marketplace listing proxied through `https://gateway.ozma.app`. Surfaces: [Provider wizard](https://ozma.app/provider/new), CLI `ozma provider *`, MCP `provider_*` tools. ## Prerequisites - Email-verified account (`provider:write` scope) - OpenAPI spec (file or URL) - Upstream API credentials (if required) ## Flow ### 1. Import draft ```bash npx @ozma_app/cli provider create ./openapi.yaml --json ``` ### 2. Store credentials ```bash npx @ozma_app/cli provider credentials api_123 \ --secret "YOUR_UPSTREAM_SECRET" \ --injection header \ --injection-name "X-Api-Key" ``` Injection options: `header`, `bearer`, `query` (with `--injection-name`). ### 3. Verify upstream (2xx required) ```bash npx @ozma_app/cli provider verify api_123 --json ``` Smoke-tests GET endpoints without billing. Sets `verifiedAt` on success. Publish in production re-runs verify unless skipped (restricted for paid APIs). ### 4. Set pricing ```bash npx @ozma_app/cli provider pricing api_123 \ --endpoint-id ep_456 \ --price-cents 5 ``` ### 5. Stripe Connect (paid APIs) Production paid publish requires Connect with **`payouts_enabled`** + **`transfers` active**: ```bash npx @ozma_app/cli provider connect ``` Free-only APIs (`price_cents = 0` everywhere) skip Connect. ### 6. Publish — Ozma reseller attestation ```bash npx @ozma_app/cli provider publish api_123 --accept-terms ``` `--accept-terms` maps to `{ "accept_terms": true }` — attests to **Ozma platform/reseller terms** (`provider_terms_accepted_at`). Distinct from optional provider-authored consumer terms below. ## Optional provider documents / terms After publish, providers may attach legal documents: ```bash npx @ozma_app/cli provider documents create \ --kind terms \ --source hosted \ --version 1.0.0 \ --markdown ./terms.md \ --api-id api_123 \ --acceptance-mode display_only npx @ozma_app/cli provider documents activate doc_123 ``` | `acceptance_mode` | Effect | |-------------------|--------| | `display_only` | Shown on listing and `/apis/{slug}/terms`; gateway unaffected | | `required` | Gateway **428** until consumer org owner/admin accepts via session | Activate moves `draft` → `active` (supersedes prior active doc same scope/kind). Consumer acceptance: ```bash ozma login --email you@example.com ozma terms accept my-api-slug --yes ``` ## Production gates summary | Gate | Applies to | |------|------------| | Verify 2xx | All publish (re-run on publish) | | `--accept-terms` | All publish | | Connect KYC | Paid APIs in prod | | Health probe | Live APIs (auto-suspend after 3 failures) | ## Unpublish ```bash npx @ozma_app/cli provider unpublish api_123 ``` Returns listing to draft — removed from public catalog. ## Related - [Publish from OpenAPI example](/docs/examples/publish-openapi) - [Pricing & payouts](/docs/guides/pricing-payouts) - [Provider quickstart](/docs/getting-started/quickstart-provider) --- # Troubleshooting Every Ozma response includes `meta.request_id` (format `req_…`). Include it when contacting support. ## HTTP status quick reference | Symptom | Code | Likely cause | Fix | |---------|------|--------------|-----| | 401 Unauthorized | `unauthenticated` | Missing/invalid Bearer, malformed key, expired session | Set `Authorization: Bearer ozma_live_…` on gateway; use API key not session on gateway | | 402 Payment | `payment_required` | Org suspended (failed invoices) or prepaid wallet empty | [Dashboard → Billing](https://ozma.app/dashboard/billing): pay invoice or add payment method | | 402 Payment | `insufficient_budget` | Key **lifetime** `budget_cents` exhausted | Raise budget: `ozma budget set --budget 10000` or create a new key | | 402 Payment | `daily_cap_exceeded` | Key **daily** cap hit (UTC midnight reset on SpendCounter) | Raise `--daily-cap` or wait until UTC day rolls over | | 402 Payment | `per_call_limit_exceeded` | Call price exceeds `per_call_max_cents` | Increase `--per-call-max` or pick a cheaper endpoint | | 403 Forbidden | `forbidden` | Missing scope, self-dealing, or wrong org | Verify email for `billing:write` / `provider:write`; do not proxy your own API as consumer | | 409 Conflict | `conflict` | Slug taken, duplicate resource | Pick a different slug or revoke conflicting resource | | 428 Precondition | `terms_acceptance_required` | Required provider terms not accepted | `ozma terms show `; session login + `ozma terms accept --yes` | | 429 Too many requests | `rate_limited` | Control plane (~120/min) or gateway (~300/min) | Back off; respect `Retry-After` when present | | 429 / 502 | `upstream_rate_limited` | Provider returned HTTP 429 | Retry with backoff; check upstream docs | | 502 | `upstream_error` | Provider error, timeout, or invalid JSON | Fix request; inspect `error.details.status` / body snippet | | 503 | `not_configured` | Feature disabled (e.g. avatar R2 binding) | Use `PATCH /v1/me/profile` with `avatar_url` HTTPS link instead of upload | | 503 | `internal_error` | SpendCounter outage or server fault | Retry; if persistent, report `request_id` | ## daily_cap_exceeded vs insufficient_budget Both return HTTP **402** but different codes: - **`insufficient_budget`** — lifetime `budget_cents` on the key would be exceeded. - **`daily_cap_exceeded`** — today's spend would exceed `daily_cap_cents`. The gateway SpendCounter resets at **UTC midnight** (Postgres `daily_spent_cents` is informational; DO is authoritative). ```bash npx @ozma_app/cli keys list --json # inspect caps and spent fields npx @ozma_app/cli budget set --daily-cap 5000 --budget 50000 ``` ## terms_acceptance_required (428) When a provider activates terms with `acceptance_mode: required`, gateway calls fail until the consumer org records acceptance. 1. `ozma terms show ` — read `approval_url` / `snapshot_url` 2. Log in with a human session: `ozma login --email` 3. Accept as org owner/admin: `ozma terms accept --yes` Anonymous agent orgs (register-only, no user) get `anonymous_org_hint` in the error — attach a human via email login first. ## Empty catalog / no search results - Confirm query spelling; try broader `q` or remove `category` filter. - New listings appear after successful `publish` (live status). - Suspended APIs (health probe failures) are hidden from browse. - Log a gap: `ozma request-api --query "your need"` ## Verify failures (providers) `POST /v1/provider/apis/:id/verify` or `ozma provider verify` smoke-tests GET endpoints and requires **2xx** upstream. | Symptom | Fix | |---------|-----| | 401/403 from upstream | Fix `--secret` and `--injection` / `--injection-name` | | Timeout | Check `base_url`, firewall, provider uptime | | Non-2xx | Upstream must return success for verify to pass | | Publish blocked on paid API | Complete Connect KYC first | Production paid publish runs verify unless explicitly skipped (restricted). ## Connect KYC blocked Paid APIs in production require Stripe Connect with **`payouts_enabled`** and **`transfers` active**. ```bash npx @ozma_app/cli provider connect # open Express onboarding URL npx @ozma_app/cli provider publish --accept-terms # retries after KYC ``` Free-only APIs (`price_cents = 0` on all endpoints) skip Connect. ## Avatar upload 503 `POST /v1/me/avatar` returns **`not_configured`** (503) when profile asset storage is unavailable. Set a public HTTPS image via `PATCH /v1/me/profile` with `avatar_url` instead. ## Gateway vs control plane auth | Mistake | Result | |---------|--------| | Session token on gateway | 401 — use API key | | Unverified key for `billing checkout` | 403 — verify email | | `ozma signup` in prod without Turnstile | Rejected — use web signup or OTP login | ## Idempotency replay Successful proxy calls with `Idempotency-Key` or `X-Idempotency-Key` may return `meta.replayed: true` without re-calling upstream. Response `data` may be `null` on replay. SDK and CLI do not expose idempotency flags today — send headers manually on raw HTTP. ## Further reading - [Error codes reference](/docs/reference/errors) - [Gateway reference](/docs/reference/gateway) - [Billing](/docs/guides/billing) - [Keys & budgets](/docs/guides/keys-and-budgets) --- # 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 | 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) --- # CLI Install: `npm install -g @ozma_app/cli` or run via npx. ```bash npx @ozma_app/cli --help ``` Install hub: [https://ozma.app/connect](https://ozma.app/connect) **Package:** `@ozma_app/cli` — in-repo v0.2.0+; published versions may lag. ## Config Path: **`~/.ozma/config.json`** | Field | Purpose | |-------|---------| | `apiKey` | `ozma_live_…` — required for `ozma call` | | `sessionToken` | `sess_…` — control plane; preferred when both set | | `apiUrl` | Default `https://api.ozma.app` | | `gatewayUrl` | Default `https://gateway.ozma.app` | Env overrides: `OZMA_API_URL`, `OZMA_GATEWAY_URL`. All commands support **`--json`**. API failures exit code **1** with `{ ok: false }`. ## Auth | Command | Description | |---------|-------------| | `ozma login [--key] [--email] [--code]` | Browser, paste key, or OTP login | | `ozma logout [--all]` | End session; `--all` clears API key too | | `ozma signup --email [--name]` | Human signup — **Turnstile required in prod; CLI rejected** — use web or `login --email` | | `ozma verify --token` | Email verification token exchange | | `ozma register` | Auto-register key (**0 credits**) | | `ozma whoami` | `GET /v1/me` | ## Profile & providers | Command | Description | |---------|-------------| | `ozma profile show\|update […]` | Own org profile | | `ozma providers [slug] [--q]` | Public provider directory | ## Catalog & market | Command | Description | |---------|-------------| | `ozma categories` | Category list | | `ozma search [--category] [--sort]` | Search | | `ozma inspect [--schema\|--stats\|--history\|--ranks\|--endpoints\|--openapi]` | API detail views | | `ozma leaderboard [--category]` | Merit boards | | `ozma market overview\|activity [--limit]` | Market summary / activity feed | | `ozma request-api --query [--email] [--notes]` | Request missing API | Boards: `top_grossing`, `most_used`, `most_reliable`, `best_value`, `trending`, `new_rising`. ## Consumer — keys, call, usage | Command | Description | |---------|-------------| | `ozma provision [slug]` | Register if no key | | `ozma call …` | Gateway proxy (API key only) | | `ozma terms show\|status\|accept ` | Provider terms (`accept` needs session) | | `ozma usage [--from] [--to]` | Usage history | | `ozma keys list\|create\|revoke\|patch` | Key management | | `ozma budget set [--key] [--budget] [--per-call-max] [--daily-cap] [--approval-mode]` | Spend caps | | `ozma balance` | Credit balance | Key create/patch flags: `--name`, `--scopes`, `--budget`, `--per-call-max`, `--daily-cap`, `--approval-mode`. ## Billing | Command | Maps to | |---------|---------| | `ozma billing status` | `GET /v1/billing/balance` | | `ozma billing packs` | `GET /v1/billing/packs` | | `ozma billing setup` | SetupIntent — human confirms card | | `ozma billing portal` | Billing Portal URL | | `ozma billing checkout [--pack\|--amount]` | Stripe Checkout | | `ozma billing auto-reload --enable\|--disable [--threshold] [--amount]` | Auto-reload | ## Provider | Command | Maps to | |---------|---------| | `ozma provider list\|get ` | List/detail | | `ozma provider create [--rapidapi]` | Import OpenAPI | | `ozma provider patch [--name] [--summary] …` | Metadata | | `ozma provider credentials --secret [--injection] [--injection-name]` | Upstream secret | | `ozma provider pricing [--file\|--endpoint-id --price-cents]` | Pricing | | `ozma provider pricing-cancel ` | Cancel pending increase | | `ozma provider verify ` | Smoke test | | `ozma provider publish [--skip-verify] --accept-terms` | Go live | | `ozma provider unpublish ` | Live → draft | | `ozma provider connect` | Stripe Connect onboard | | `ozma provider analytics` | Provider stats | | `ozma provider documents list\|create\|activate` | Legal documents | Alias: `ozma publish ` = `ozma provider create`. ### Documents examples ```bash ozma provider documents create \ --kind terms --source hosted --version 1.0.0 \ --markdown ./terms.md --api-id api_123 \ --acceptance-mode required ozma provider documents activate doc_123 ``` ## Connect (MCP installer) ```bash ozma connect # all detected agents ozma connect --pick # interactive multi-select ozma connect --all # every supported client ozma connect --client cursor # single client ozma connect --project --rules # repo-scoped rules ozma connect --dry-run --json ozma connect --key ozma_live_… # embed key (plaintext warning) ozma connect --no-key # keyless dev config ``` Clients: `cursor`, `claude-code`, `codex`, `vscode`, `windsurf`, `cline`, `gemini`, `zed`, `claude-desktop`. ## Turnstile signup limitation `ozma signup` cannot pass Cloudflare Turnstile in production. Use [https://ozma.app/signup](https://ozma.app/signup) or `ozma login --email`. ## Examples ```bash ozma login --email you@example.com --code 123456 ozma search "weather" --json ozma call frankfurter /latest --query from=USD --query to=EUR --json ozma keys create --name agent-1 --budget 500 ozma budget set --budget 500 --daily-cap 100 --per-call-max 50 ozma provider credentials api_123 --secret sk_x --injection bearer ozma provider pricing api_123 --endpoint-id ep_456 --price-cents 5 ozma provider publish api_123 --accept-terms ozma terms accept my-api-slug --yes ``` --- # Rate limits & errors All control plane and gateway responses use the standard envelope from `@ozma_app/core`. Every response includes `meta.request_id` — cite it in support requests. ```json { "ok": false, "error": { "code": "insufficient_budget", "message": "…", "details": {} }, "meta": { "request_id": "req_01…" } } ``` Canonical enum: `packages/core/src/envelope.ts`. ## Rate limits | Surface | Limit | Key | |---------|-------|-----| | Control plane (`api.ozma.app`) | ~**120** requests / 60s | Per API-key prefix or Cloudflare IP (KV bucket) | | Gateway (`gateway.ozma.app`) | ~**300** requests / min | Per API-key prefix (`GATEWAY_RATE_LIMIT_PER_MIN`) | Exceeded limits return **429** `rate_limited`. Login OTP and signup abuse paths have additional per-email/IP caps. ## Error code reference | Code | HTTP | Surface | Meaning | Remediation | |------|------|---------|---------|-------------| | `unauthenticated` | 401 | Both | Missing, invalid, or expired auth | Provide valid `Bearer ozma_live_…` (gateway) or key/session (API) | | `forbidden` | 403 | Both | Insufficient scope or policy block | Verify email for elevated scopes; check self-dealing; confirm org membership | | `not_found` | 404 | Both | Resource or route missing | Confirm slug/id; check API is live not draft | | `conflict` | 409 | API | Duplicate or state conflict | e.g. slug taken — choose another | | `validation_failed` | 400 / 413 | Both | Invalid input or oversize body | Fix request schema; reduce body size (413 on gateway) | | `insufficient_budget` | 402 | Gateway | Key lifetime budget exceeded | Raise `budget_cents` or use another key | | `per_call_limit_exceeded` | 402 | Gateway | Call price > `per_call_max_cents` | Increase per-call max or cheaper endpoint | | `daily_cap_exceeded` | 402 | Gateway | Daily cap exceeded (UTC day) | Raise `daily_cap_cents` or wait for UTC midnight reset | | `rate_limited` | 429 | Both | Ozma rate limit | Exponential backoff; reduce request rate | | `upstream_rate_limited` | 429 | Gateway | Provider returned HTTP 429 | Backoff; honor upstream `Retry-After` in details | | `upstream_error` | 502 | Gateway | Provider error, timeout, bad JSON | Fix upstream path/params; inspect `details.status` | | `payment_required` | 402 | Gateway / API | Suspended org or prepaid wallet empty | Pay invoice or add payment method at dashboard | | `precondition_failed` | 412 | API | Wrong document/API state for action | e.g. activate only draft docs; edit only draft/live APIs | | `terms_acceptance_required` | 428 | Gateway | Required provider terms not accepted | Session accept via `POST …/terms/accept` or `ozma terms accept` | | `not_configured` | 503 | API | Optional subsystem unavailable | e.g. avatar upload without R2 — use `avatar_url` PATCH | | `internal_error` | 500 / 503 | Both | Unexpected server or dependency failure | Retry; report `request_id`; SpendCounter outage → 503 on gateway | ## Gateway-specific notes - **`daily_cap_exceeded` vs `insufficient_budget`:** both HTTP 402; read `error.code` — daily cap is calendar UTC, lifetime budget is cumulative on the key. - **`terms_acceptance_required`:** includes `approval_url`, `snapshot_url`, `document_id` in `details`. - **Idempotency replay:** success responses may set `meta.replayed: true`; not an error. ## Related - [Troubleshooting guide](/docs/guides/troubleshooting) - [Gateway reference](/docs/reference/gateway) --- # Gateway proxy Base URL: **`https://gateway.ozma.app`** The gateway is Ozma's **data plane**. It authenticates API keys, enforces spend governance, injects upstream credentials, meters usage, and returns envelope-wrapped upstream responses. ## Proxy path ```http ALL /v1/proxy/:apiSlug/ Authorization: Bearer ozma_live_… ``` Examples: ```bash # Frankfurter (free platform API) curl -H "Authorization: Bearer $OZMA_API_KEY" \ "https://gateway.ozma.app/v1/proxy/frankfurter/latest?from=USD&to=EUR" # httpbin.org GET echo curl -H "Authorization: Bearer $OZMA_API_KEY" \ "https://gateway.ozma.app/v1/proxy/httpbin-org/get" ``` Resolve proxy paths and methods from `GET /v1/apis/:slug/schema` or `ozma inspect --schema`. ## Authentication | Credential | Accepted | |------------|----------| | API key `ozma_live_…` with `proxy:call` | Yes | | Session token `sess_…` | **No** — 401 | | Missing auth (dev only) | Keyless auto-register → 0 credits | Malformed Bearer tokens return **401** `unauthenticated`. ## Response envelope Success: ```json { "ok": true, "data": { "amount": 1.08, "base": "USD", "date": "2026-07-22" }, "meta": { "request_id": "req_01…", "cost_cents": 0, "price_cents_effective": 0, "credit_applied_cents": 0, "billable_cents": 0 } } ``` Paid calls populate `cost_cents`, `price_cents_effective`, `credit_applied_cents`, and `billable_cents`. Scheduled price increases and active discounts appear as `price_increase` / `discount` objects in `meta`. Failure uses the standard error envelope with the same `request_id`. ## Idempotency headers Send on mutating or retried calls: ```http Idempotency-Key: my-unique-key-123 ``` Also accepted: `X-Idempotency-Key`. When a prior **successful** usage event exists for the same org + key + idempotency key, the gateway returns: - `meta.replayed: true` - `meta.original_request_id` when available - Original billing fields - **`data: null`** (response bodies are not stored) Duplicate in-flight races resolve to the first success without double billing. **Note:** `@ozma_app/sdk` `proxyCall` and `ozma call` do not expose idempotency flags today. Use raw HTTP or extend your client to set the header. ## Self-dealing Proxy calls where **consumer org equals provider org** return **403** `forbidden`. Use a separate org for testing your own published API. ## Body and response limits Oversized request bodies return **413** with `validation_failed`. Upstream responses exceeding the size cap return **502** `upstream_error`. Failed upstream calls are not billed; spend reservations are refunded. ## Required provider terms (428) When a live API has active provider terms with `acceptance_mode: required`, the gateway returns **428** `terms_acceptance_required` before spend or upstream call. Details include `approval_url`, `snapshot_url`, `document_id`, and `content_hash`. Accept via control plane session: `POST /v1/apis/:slug/terms/accept` or `ozma terms accept --yes`. ## Rate limits Default **~300 requests per minute** per key prefix (`GATEWAY_RATE_LIMIT_PER_MIN`). Excess returns **429** `rate_limited`. Signup abuse limits apply separately on auto-register paths. ## Spend governance (402) Enforced synchronously via SpendCounter Durable Object before upstream call: | Code | Trigger | |------|---------| | `per_call_limit_exceeded` | Price > `per_call_max_cents` | | `insufficient_budget` | Would exceed lifetime `budget_cents` | | `daily_cap_exceeded` | Would exceed `daily_cap_cents` (UTC day) | See [Keys & budgets](/docs/guides/keys-and-budgets). ## Other gateway errors | HTTP | Code | When | |------|------|------| | 404 | `not_found` | Unknown slug or endpoint path | | 402 | `payment_required` | Suspended org or prepaid credits exhausted | | 429 | `upstream_rate_limited` | Upstream returned 429 | | 502 | `upstream_error` | Upstream non-success (except 429) | | 503 | `internal_error` | SpendCounter unavailable | ## Related - [REST API](/docs/reference/api) — catalog and control plane - [Errors](/docs/reference/errors) — full code table - [First API call example](/docs/examples/first-call) --- # MCP tools Endpoint: **`https://mcp.ozma.app/mcp`** Install: `npx @ozma_app/cli connect` — [https://ozma.app/connect](https://ozma.app/connect) ## Connection ```http POST /mcp Authorization: Bearer ozma_live_… Mcp-Session-Id: ``` Production requires **OAuth 2.1** or **Bearer API key**. Unauthenticated requests return **401**. OAuth metadata: `https://mcp.ozma.app/.well-known/oauth-protected-resource` Echo **`Mcp-Session-Id`** on subsequent requests for session key persistence (30-day TTL in KV). ## Auth modes | Mode | Best for | |------|----------| | **OAuth 2.1** | Interactive agents — email OTP proves ownership; mints full-scope key on approve | | **Bearer `ozma_live_…`** | CI/scripts with pre-provisioned verified key | Provider and billing tools need **`billing:write`** / **`provider:write`** — available after email verification or OAuth approve. Bare `ozma_register` keys return **403** until elevated. ## Consumer tools | Tool | Params | Action | |------|--------|--------| | `ozma_register` | — | `POST /v1/register` → key + **0 credits** | | `discover_tools` | `query`, `category?`, `max_results?` | Context-efficient catalog search | | `search_apis` | `query`, `category?`, `sort?`, `max_price_cents?` | Full search | | `get_categories` | — | Category list | | `get_market_overview` | — | Market aggregates | | `get_leaderboard` | `board`, `category?` | Merit board | | `request_api` | `query`, `email?`, `notes?` | Catalog gap request | | `get_api_details` | `slug` | Listing detail | | `get_schema` | `slug` | Endpoints + schemas | | `get_api_terms` | `slug` | Effective provider terms — CLI `ozma terms show` | | `get_api_terms_status` | `slug` | Acceptance status — CLI `ozma terms status` | | `accept_api_terms` | `slug`, `document_id?`, `content_hash?` | Accept terms — **human session required** (owner/admin + verified email) | | `get_balance` | — | Billing balance | | `create_checkout` | `pack?`, `amount_cents?` | Stripe Checkout (`billing:write`) | | `billing_portal` | — | Portal URL (`billing:write`) | | `billing_setup` | — | SetupIntent (`billing:write`) — **human must confirm card** | | `get_usage` | `from?`, `to?` | Usage summary | | `set_budget` | `key_id?`, `budget_cents?`, `per_call_max_cents?`, `daily_cap_cents?` | Patch key caps | | `provision_access` | `slug?` | Ensure key exists | | `call_api` | `slug`, `endpoint`, `method?`, `input?` | Gateway proxy — **428** if required terms missing | | `run_probe` | `slug`, `endpoint`, `input?` | GET test call | ## Provider tools Requires `provider:write`. | Tool | Params | Action | |------|--------|--------| | `provider_list_apis` | — | List draft/live APIs | | `provider_get_api` | `api_id` | Detail + pricing | | `provider_create_api` | `openapi_url?`, `rapidapi_url?`, `body?` | Create/import | | `provider_update_api` | `api_id`, metadata… | Patch listing | | `provider_set_credentials` | `api_id`, `secret`, `injection?` | Store upstream secret | | `provider_set_pricing` | `api_id`, `endpoints[]` | Price/discounts | | `provider_verify` | `api_id` | Upstream smoke test | | `provider_publish` | `api_id`, `skip_verify?`, `accept_terms?` | Go live — **`accept_terms: true` required** | | `provider_list_documents` | `api_id?`, `kind?` | List legal documents | | `provider_create_document` | kind, source, version, … | Create draft terms/privacy/… | | `provider_activate_document` | `document_id`, … | Activate draft document | | `provider_unpublish` | `api_id` | Live → draft | | `provider_cancel_pending_pricing` | `api_id`, `endpoint_id` | Cancel scheduled increase | | `provider_analytics` | — | GMV + payout stats | | `provider_connect_onboard` | — | Stripe Connect URL — **human KYC** | ## Profile tools | Tool | Params | Action | |------|--------|--------| | `list_providers` | `query?`, `limit?`, `offset?` | Public directory | | `get_provider_profile` | `slug` | Trust badges + APIs | | `get_my_profile` | — | Own profile (`provider:write`) | | `update_my_profile` | profile fields… | Patch profile (`provider:write`) | ## Terms & documents | Role | MCP | CLI | |------|-----|-----| | Consumer | `get_api_terms`, `get_api_terms_status`, `accept_api_terms` | `ozma terms show\|status\|accept ` | | Provider | `provider_list_documents`, `provider_create_document`, `provider_activate_document` | `ozma provider documents list\|create\|activate` | `accept_api_terms` requires a **human session** (org owner/admin + verified email). Bearer API keys alone return **403** — use the web approval URL or `ozma login --email` then `ozma terms accept --yes`. REST: `GET /v1/apis/:slug/terms`, `POST …/terms/accept`, `POST/GET /v1/provider/documents`. ## Human steps for billing / Connect | Tool | Human action | |------|--------------| | `billing_setup` | Confirm card in [dashboard](https://ozma.app/dashboard/billing) or Stripe.js | | `billing_portal` | Open returned URL in browser | | `create_checkout` | Complete Stripe Checkout in browser | | `provider_connect_onboard` | Complete Stripe Express KYC | | `accept_api_terms` | Session login + verified email (or web approval URL) | ## Recommended flow 1. Connect OAuth or verified key 2. `discover_tools` / `search_apis` → `get_schema` → `call_api` 3. On **428** `terms_acceptance_required`: `get_api_terms` → human `accept_api_terms` / CLI / web → retry 4. For paid: human adds payment method; agent sets `set_budget` 5. Provide: `provider_connect_onboard` (human) → `provider_create_api` → credentials → verify → pricing → `provider_publish` with `accept_terms: true` Agent onboarding: [https://ozma.app/onboarding.md](https://ozma.app/onboarding.md) ## Related - [CLI reference](/docs/reference/cli) - [Agent MCP loop example](/docs/examples/agent-mcp-loop) --- # TypeScript SDK ```bash npm install @ozma_app/sdk ``` **npm:** published **v0.1.0** (2026-07-07). In-repo version is **0.2.0+** with additional methods — pin to published release for stability or build from monorepo for latest. ## Quick start ```typescript import { OzmaClient } from "@ozma_app/sdk"; const client = new OzmaClient({ baseUrl: "https://api.ozma.app", apiKey: process.env.OZMA_API_KEY, sessionToken: process.env.OZMA_SESSION_TOKEN, // optional; wins over apiKey on control plane gatewayUrl: "https://gateway.ozma.app", // optional; auto-derived from baseUrl }); const { data } = await client.search({ q: "weather", sort: "relevance" }); ``` ## OzmaClientOptions | Field | Description | |-------|-------------| | `baseUrl` | Control plane (`https://api.ozma.app`) | | `apiKey` | Optional `ozma_live_*` | | `sessionToken` | Optional session — **preferred over apiKey** for control-plane auth | | `gatewayUrl` | Gateway base; defaults to `baseUrl` with `api.` → `gateway.` rewrite | | `timeoutMs` | Request timeout (default 30_000) | ## Types **SearchSort:** `ozma_score` | `price` | `relevance` | `new` | `calls` | `gmv` | `reliability` | `value` | `momentum` All control-plane methods return `Promise>` (Ozma envelope). ## Method table | Method | HTTP | Description | |--------|------|-------------| | `health()` | `GET /health` | Service status | | `register()` | `POST /v1/register` | Auto-register → key, 0 credits | | `getMe()` | `GET /v1/me` | Org + credits | | `listProviders(opts?)` | `GET /v1/providers` | Provider directory | | `getProviderProfile(slug)` | `GET /v1/providers/:slug` | Public profile | | `getMyProfile()` | `GET /v1/me/profile` | Own profile | | `updateMyProfile(patch)` | `PATCH /v1/me/profile` | Update profile | | `verifyEmail({ token })` | `POST /v1/auth/verify-email` | Email verify | | `search({ q?, category?, sort?, limit?, offset? })` | `GET /v1/search` | Catalog search | | `categories()` | `GET /v1/categories` | Categories | | `getApi(slug)` | `GET /v1/apis/:slug` | Listing detail | | `getSchema(slug)` | `GET /v1/apis/:slug/schema` | Schemas | | `getEndpoints(slug)` | `GET /v1/apis/:slug/endpoints` | Endpoint rows | | `getApiStats(slug, window?)` | `GET /v1/apis/:slug/stats` | Stats rollup | | `getApiHistory(slug, limit?)` | `GET /v1/apis/:slug/history` | Score history | | `getApiRanks(slug)` | `GET /v1/apis/:slug/ranks` | Board ranks | | `getLeaderboard(board, category?)` | `GET /v1/leaderboards/:board` | Leaderboard | | `getMarketOverview()` | `GET /v1/market/overview` | Market totals | | `getMarketActivity({ limit? })` | `GET /v1/market/activity` | Activity feed (auth) | | `createKey({ name, scopes?, budget_cents? })` | `POST /v1/keys` | Mint key | | `listKeys()` | `GET /v1/keys` | List keys | | `revokeKey(id)` | `DELETE /v1/keys/:id` | Revoke | | `patchKey(id, caps)` | `PATCH /v1/keys/:id` | Budget / daily cap / per-call max | | `getUsage({ from?, to? })` | `GET /v1/usage` | Usage | | `getBillingPacks()` | `GET /v1/billing/packs` | Packs | | `getBalance()` | `GET /v1/billing/balance` | Balance | | `createCheckout(opts)` | `POST /v1/billing/checkout` | Checkout URL | | `createSetupIntent()` | `POST /v1/billing/setup-intent` | SetupIntent | | `createPortalSession()` | `POST /v1/billing/portal` | Portal URL | | `configureAutoReload(opts)` | `POST /v1/billing/auto-reload` | Auto-reload | | `proxyCall(apiSlug, path, method, body?)` | Gateway `ALL /v1/proxy/…` | Billed proxy call | | `getApiTerms(slug)` | `GET /v1/apis/:slug/terms` | Provider terms | | `acceptApiTerms(slug, { documentId, contentHash })` | `POST …/terms/accept` | Accept terms (session) | | `providerCreateApi(body)` | `POST /v1/provider/apis` | Create draft | | `providerListApis()` | `GET /v1/provider/apis` | List APIs | | `providerVerify(apiId)` | `POST …/verify` | Smoke test | | `providerPublish(apiId, { acceptTerms, skipVerify? })` | `POST …/publish` | Publish — **`acceptTerms: true` required** | | `providerUnpublish(apiId)` | `POST …/unpublish` | Unpublish | | `providerUpdatePricing(apiId, { endpoints })` | `PATCH …/pricing` | Pricing | | `providerAnalytics()` | `GET /v1/provider/analytics` | Analytics | | `providerConnectOnboard()` | `POST …/connect/onboard` | Connect URL | | `providerCreateDocument(body)` | `POST /v1/provider/documents` | Draft document | | `providerListDocuments(opts?)` | `GET /v1/provider/documents` | List documents | | `providerActivateDocument(id, body?)` | `POST …/activate` | Activate document | ## proxyCall notes - Uses **`apiKey`** only (not `sessionToken`). - Pass explicit `gatewayUrl` when `baseUrl` is localhost. - Returns full Ozma envelope including `meta.cost_cents` and billing fields. - Does **not** expose idempotency key options — set `Idempotency-Key` header manually if needed. - **428** when required provider terms not accepted. ## Scopes Anonymous register keys: consumer scopes only. Email-verified keys add `billing:write` + `provider:write`. See [Authentication](/docs/getting-started/authentication). ## Related - [CLI](/docs/reference/cli) - [MCP](/docs/reference/mcp) - Python SDK: monorepo `packages/python/ozma/` --- # Changelog — Mon Jul 20 2026 20:00:00 GMT-0400 (Eastern Daylight Time): Go-live hardening - Provider trust hardening (terms acceptance, credential uniqueness) - Refund clawback and payout period uniqueness - Gateway idempotency and spend counter hardening - Catalog health probes and provider verify improvements --- # Changelog — Tue Jul 21 2026 20:00:00 GMT-0400 (Eastern Daylight Time): Dashboard declutter, public profiles by default, marketplace Sale filter - Sidebar order: **Dashboard** first, then Connect / Provider - Agent install tour lives on **Connect**; Dashboard Overview keeps a short first-call nudge - Profiles are **public by default** (opt-out); empty consumer shells stay out of `/providers` - Marketplace **Sale** badge + **On Sale** view for active time-limited discounts - Pricing editor discount fields and market ticker spacing cleaned up --- # Changelog — Tue Jul 21 2026 20:00:00 GMT-0400 (Eastern Daylight Time): Public docs coverage overhaul - Expanded **Getting started** with authentication (keys vs sessions, scopes, Turnstile, register) - New **Guides**: discover APIs, troubleshooting (symptom → fix for all error codes) - New **Reference**: gateway proxy (idempotency, 428 terms, rate limits, meta fields) - New **Examples**: first call (Frankfurter/httpbin), agent MCP loop, publish OpenAPI CLI flow - Rewrote reference pages for **errors** (full enum), **REST API** (grouped tables), **CLI**, **MCP**, **SDK** - Expanded guides: billing, pricing/payouts, keys/budgets (correct CLI flags), publish API - Expanded concepts: merit, spend governance, architecture, security - FAQ expanded to 20+ Q&As; quickstarts use concrete catalog slugs - All reference pages marked `lastVerified: 2026-07-22` --- # Changelog — Tue Jul 21 2026 20:00:00 GMT-0400 (Eastern Daylight Time): Go-live UX fixes and UI polish - **Marketplace resilience** — if the catalog is briefly unreachable, the homepage now shows a loading skeleton and a Retry button instead of flashing "0 APIs" or a raw error - **Mobile navigation** — Dashboard / Connect / Provider are now reachable below the desktop breakpoint via a horizontal app-nav strip - **Auth flow** — signed-in users visiting `/login` or `/signup` are redirected to the dashboard; the email verification page handles used links and signed-out browsers gracefully - **API detail** — new "Try this API" panel with copyable CLI, curl, and Python snippets - **Provider console** — clearer feedback when you have no published APIs or the list fails to load - **Profile editor** — essentials first; legal/publisher metadata and socials are collapsed by default - **UI polish** — semantic surface/border/focus/status design tokens and a restrained 120–240ms motion scale (fully honoring `prefers-reduced-motion`) --- # Changelog — Tue Jul 21 2026 20:00:00 GMT-0400 (Eastern Daylight Time): MCP consumer terms tools Remote MCP now exposes consumer terms parity with CLI/SDK: - `get_api_terms` / `get_api_terms_status` / `accept_api_terms` (accept still requires a human session) - Provider document tools documented: `provider_list_documents`, `provider_create_document`, `provider_activate_document` On gateway **428** `terms_acceptance_required`, agents can inspect terms via MCP before a human accepts. --- # Changelog — Tue Jul 21 2026 20:00:00 GMT-0400 (Eastern Daylight Time): Public profiles, dashboard revamp, and docs site - Org-level **public provider profiles** (`/providers/{slug}`) with bio, links, socials, avatar, and trust badges - New APIs: `GET /v1/providers`, `GET /v1/providers/:slug`, `GET|PATCH /v1/me/profile`, `POST /v1/me/avatar` - Catalog search/detail now include provider identity; agents get profile tools in MCP/CLI/SDK - **Dashboard** split into Overview / Keys / Billing / Profile / Account - Public **docs site** at `/docs` with changelog, raw markdown, and `/llms-full.txt` - Footer restructured; `llms.txt` removed from footer (route remains for agents) --- # Changelog — Tue Jul 21 2026 20:00:00 GMT-0400 (Eastern Daylight Time): Provider terms, legal documents, and publisher metadata - Providers can publish versioned **legal documents** (terms, privacy, AUP, DPA, license, SLA, security) scoped to an org or a specific API - **Display-only** terms appear on the API listing and terms page; **required** terms block gateway calls with **428** until the consumer org owner/admin accepts (session required) - New catalog routes: `GET /v1/apis/:slug/terms`, `POST …/terms/accept`, document snapshots under `/v1/apis/:slug/documents/…` - **Publisher metadata** fields on APIs and orgs (docs URLs, license, data regions, lifecycle, compliance claims, domain verification) - CLI: `ozma provider documents …`, `ozma terms show|status|accept`; SDK methods `getApiTerms`, `acceptApiTerms`, `providerCreateDocument`, … - Publish still requires **`accept_terms: true`** for Ozma **platform/reseller** attestation (separate from provider-authored consumer terms) - **Status:** code shipped locally; production deploy pending migration `0016` (deploy order: DB → API → web → gateway last) --- # Changelog — Tue Jul 21 2026 20:00:00 GMT-0400 (Eastern Daylight Time): Marketplace moves to SSR/ISR on Cloudflare Workers - Web app now deploys via **OpenNext** to a Cloudflare Worker (`ozma-web`) with **SSR/ISR** — newly published APIs and providers appear without a full static rebuild - Site sitemap at `/sitemap.xml` regenerates on a short ISR interval from the live catalog - Machine-readable API catalog sitemap remains at `GET /v1/catalog/sitemap.xml` on the API ---