# 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 <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`** — 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 <slug>` — read `approval_url` / `snapshot_url`
2. Log in with a human session: `ozma login --email`
3. 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 `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 <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.

## Further reading

- [Error codes reference](/docs/reference/errors)
- [Gateway reference](/docs/reference/gateway)
- [Billing](/docs/guides/billing)
- [Keys & budgets](/docs/guides/keys-and-budgets)
