> This page is for Gateway.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.merge.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.merge.dev/_mcp/server.

# Customer API keys

> Mint a Merge Gateway API key scoped to one customer, so every request it makes is attributed and enforced for that customer with no customer field.

A customer API key is a Gateway key bound to one customer, so its requests need no `customer` field. Give one to each tenant so a leaked key exposes only that tenant.

## Mint a key

`POST /v1/customers/{customer_id}/api-keys` with your organization-level key. The raw key comes back once, in `key`.

**`cURL`**

```bash title="cURL"
curl -X POST https://api-gateway.merge.dev/v1/customers/{customer_id}/api-keys \
  -H "Authorization: Bearer $MERGE_GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Acme production", "spending_limit": 100, "reset_period": "monthly" }'
```

A customer can hold any number of keys, but you can mint at most 10 per minute for one customer; the next returns `429` `customer_api_key_rate_limited` with a `Retry-After` header. An organization can hold up to 1,000 keys in total, after which minting returns `409` `org_api_key_limit_reached`. Minting for an inactive customer returns `400` `customer_inactive`. `GET` on the same path lists keys without secrets. The customer then calls Gateway as usual:

**`cURL`**

```bash title="cURL"
curl https://api-gateway.merge.dev/v1/responses \
  -H "Authorization: Bearer $CUSTOMER_GATEWAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.5",
    "input": [{"type": "message", "role": "user", "content": "Hello"}]
  }'
```

A `customer` value that contradicts the key returns `400` `customer_mismatch`.

### Mint by your own ID

To skip looking up the Customer's `id`, send your `origin_id` to `POST /v1/customers/api-keys` with the same fields. If no customer has that `origin_id` yet, Gateway creates one with your organization's defaults first, just like [`X-Merge-Customer-Origin-Id`](/merge-gateway/embedded-routing-stack/customers#address-customers-by-your-own-id) on a request. Set `customer_name` to name that new customer; it defaults to the `origin_id` and is ignored when the customer already exists. `name` still labels the key.

**`cURL`**

```bash title="cURL"
curl -X POST https://api-gateway.merge.dev/v1/customers/api-keys \
  -H "Authorization: Bearer $MERGE_GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "origin_id": "acme-corp", "customer_name": "Acme", "name": "Acme production" }'
```

The response is the same as above, with the Customer's `id` in `customer`, plus `customer_created`: `true` when this call created the customer, so a mistyped `origin_id` doesn't go unnoticed. A deleted customer's `origin_id` returns `403` `customer_deleted`; create it again with `POST /v1/customers` to reuse it.

## Rotate or revoke a key

To rotate without downtime, mint a new key, move the customer onto it, then revoke the old one with `DELETE /v1/customers/{customer_id}/api-keys/{key_id}`. Deleting a customer revokes its keys; deactivating it makes them return `403` until reactivated.

## What a customer key can reach

Other customers' paths return `404`, so tenants can't discover each other, and organization-level routes return `403` `customer_scoped_key_forbidden`.

| Allowed for its own customer                                              | Not allowed                                                                        |
| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Read the customer, budget, routing policies, allowed models, and controls | List or create customers, or read organization-wide usage                          |
| Change the customer's `name`                                              | Change its key usage mode or status                                                |
| Create, update, and delete routing policies                               | Create, change, or delete its budget                                               |
| Add, rotate, and delete its own vendor keys                               | Delete the customer, or list or mint API keys                                      |
| Read its usage                                                            | Read or change guardrails, or call the [Evals API](/merge-gateway/evals/evals-api) |

Its usage shows only `customer_byok_spend` and `request_count`, and its key list only the customer's own vendor keys, hiding what you pay and which keys you hold.

## Reference

| Field                      | Type     | Description                                                                                                       |
| -------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `id`                       | string   | Key identifier. Pass to `DELETE`.                                                                                 |
| `key`                      | string   | The raw key, on create only                                                                                       |
| `customer`                 | UUID     | The customer the key is bound to. Can't be changed.                                                               |
| `name`                     | string   | Your label                                                                                                        |
| `key_prefix`               | string   | The first 10 characters, for recognizing the key in a list                                                        |
| `is_active`                | boolean  | Whether the key can authenticate                                                                                  |
| `spending_limit`           | number   | USD cap for this key over `reset_period`. Applies on top of the customer's budget.                                |
| `reset_period`             | enum     | `daily`, `weekly`, or `monthly` (lowercase, unlike budgets). Requires `spending_limit`.                           |
| `rate_limit_rpm`           | integer  | Requests per minute, unset for no limit. Counted against all of your organization's traffic, not only this key's. |
| `created_at`, `expires_at` | datetime | Timestamps                                                                                                        |

## Next steps

#### [Budgets](/merge-gateway/embedded-routing-stack/budgets)

Cap the customer's total spend

#### [Guardrails](/merge-gateway/embedded-routing-stack/guardrails)

Let a key-pinned customer run its own guardrail policy