Skip to navigation

Customer API keys

Give each customer its own Merge Gateway key

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
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
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 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
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 customerNot allowed
Read the customer, budget, routing policies, allowed models, and controlsList or create customers, or read organization-wide usage
Change the customer’s nameChange its key usage mode or status
Create, update, and delete routing policiesCreate, change, or delete its budget
Add, rotate, and delete its own vendor keysDelete the customer, or list or mint API keys
Read its usageRead or change guardrails, or call the 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

FieldTypeDescription
idstringKey identifier. Pass to DELETE.
keystringThe raw key, on create only
customerUUIDThe customer the key is bound to. Can’t be changed.
namestringYour label
key_prefixstringThe first 10 characters, for recognizing the key in a list
is_activebooleanWhether the key can authenticate
spending_limitnumberUSD cap for this key over reset_period. Applies on top of the customer’s budget.
reset_periodenumdaily, weekly, or monthly (lowercase, unlike budgets). Requires spending_limit.
rate_limit_rpmintegerRequests per minute, unset for no limit. Counted against all of your organization’s traffic, not only this key’s.
created_at, expires_atdatetimeTimestamps

Next steps