# 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/<upstream-path>
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 <slug> --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 <slug> --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)
