Authentication
Bearer keys, the two key scopes, and how to mint keys from the API.
Keys
Every call carries Authorization: Bearer <key>. Keys start with mg_live_ or mg_test_; both are accepted everywhere and the prefix is there so you can tell at a glance which environment a leaked string belongs to. Anything that does not start with one of the two is rejected before we touch the database.
Keys are created in the dashboard or through the API below. Each one can carry its own spend cap and its own requests-per-minute limit, both enforced before anything reaches a provider.
The two scopes
A key does exactly one of two jobs, and the separation is deliberate: the credential you paste into application config is never the credential that can mint more of itself.
| Scope | Description |
|---|---|
| inference | Spends money: /chat/completions, /embeddings, /images/generations, /rerank, /batches. Cannot see or create keys. |
| provisioning | Manages keys: /keys and /keys/{id}, plus OAuth client registration. Cannot make a model call. |
A provisioning key cannot create another provisioning key — every key it mints is inference. Otherwise revoking a leaked provisioning key would not be enough; you would have to go looking for whatever else it had made first.
Minting keys from the API
This is what you need if you resell Multigrid: one key per end customer, each capped, each revocable. Create a provisioning key in the dashboard first.
curl https://api.multigrid.ai/v1/keys \
-H "Authorization: Bearer $MULTIGRID_PROVISIONING_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "customer-4192",
"spend_limit_usd": 25,
"rate_limit_rpm": 60
}'
# → 201 { "id": "key_…", "key": "mg_live_…", "prefix": "mg_live_9f3a", … }
# "key" appears here and nowhere else, ever.| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Up to 100 characters. How the key shows up in your logs and ours. |
| spend_limit_usd | number | Optional | A positive ceiling for this key’s lifetime spend. Omit for no cap. |
| rate_limit_rpm | number | Optional | Requests per minute, at least 1. Omit to inherit the account default. |
| project_id | string | Optional | Files the key under one of your projects. Verified against your own account. |
GET /keys lists them, PATCH /keys/{id} changes the name, limits or enabled state, and DELETE /keys/{id} revokes one. A revoked key’s row survives, disabled: the requests it made still point at it, and deleting the row would orphan their history. An id belonging to another account reads as a 404 rather than a 403, because that is all it should tell you.
Inspecting the calling key
GET /key returns everything the gateway checks on the way in — balance, spend against the key cap, month-to-date against the account ceiling, and the rate limit. It exists so a client can tell the four ways it can be refused apart without triggering each one.
curl https://api.multigrid.ai/v1/key -H "Authorization: Bearer $MULTIGRID_API_KEY"Authentication failures
| Status | Code | Retry | Meaning |
|---|---|---|---|
| 401 | unauthenticated | No | No Authorization header, or it is not a bearer token. |
| 401 | invalid_api_key | No | The token does not look like a Multigrid key, or no key matches it. |
| 401 | key_revoked | No | The key existed and has been disabled. |
| 403 | wrong_key_scope | No | Right key, wrong job — an inference key on /keys, or a provisioning key on /chat/completions. |
| 403 | account_suspended | No | The account itself is suspended. Keys on it all fail the same way. |
Something here disagrees with what the API actually did? That is a bug in this page, and worth reporting.
Report it