Troubleshooting
Symptom → cause → fix for common Ozma API, gateway, billing, and provider errors.
View as Markdown →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: 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 <slug>; session login + ozma terms accept <slug> --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— lifetimebudget_centson the key would be exceeded.daily_cap_exceeded— today's spend would exceeddaily_cap_cents. The gateway SpendCounter resets at UTC midnight (Postgresdaily_spent_centsis informational; DO is authoritative).
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.
ozma terms show <slug>— readapproval_url/snapshot_url- Log in with a human session:
ozma login --email - Accept as org owner/admin:
ozma terms accept <slug> --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
qor removecategoryfilter. - 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.
npx @ozma_app/cli provider connect # open Express onboarding URL
npx @ozma_app/cli provider publish <id> --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.