Security
This page describes the security properties of the API that you can rely on when you design your integration, and the practices that keep your keys and your data safe on your side.
API keys
Section titled “API keys”Generation and storage
Section titled “Generation and storage”- A key looks like
bel_live_<prefix>_<secret>(orbel_test_…for sandbox keys). The secret part is 32 random bytes. - Belarel stores only a SHA-256 hash of the key, never the key itself. Each call is matched by hashing the key it presents.
- The full key is shown once, when it is created — in the dashboard, or in the
tokenfield of thePOST /v1/keysresponse. It cannot be retrieved later; if you lose it, create a new key and revoke the old one. - Listings show only the key’s
prefix(for examplebel_live_k3x9a0qz), which is enough to recognize a key in logs without exposing it.
One answer for every invalid key
Section titled “One answer for every invalid key”An unknown, revoked, expired or disabled key always gets the same answer:
{ "error": { "message": "Invalid, revoked, or expired API key", "type": "authentication_error", "code": "invalid_api_key", "param": null }}The status is 401. The API never tells these cases apart, so a leaked or guessed key cannot be probed for its state. A request with no key, or with a bearer that is not a Belarel key, gets the same 401 invalid_api_key code on every endpoint, with the message Missing or malformed API key. Only the public model catalog, GET /v1/models sent with no Authorization header, answers without a key.
Lifecycle
Section titled “Lifecycle”| State | Effect | Reversible |
|---|---|---|
active |
The key can call the API within its scopes and caps. | — |
disabled |
Every call answers 401 invalid_api_key. |
Yes |
revoked |
Every call answers 401 invalid_api_key. |
No |
Expired (expires_at passed) |
Every call answers 401 invalid_api_key. |
— |
Set expires_at on keys that only need to live for a project or a test campaign.
Least privilege
Section titled “Least privilege”Scopes
Section titled “Scopes”Each key carries the scopes it needs, and nothing more:
| Scope | Grants |
|---|---|
inference |
POST /v1/chat/completions, POST /v1/responses, keyed GET /v1/models |
embeddings |
POST /v1/embeddings |
usage:read |
GET /v1/usage, GET /v1/credits, GET /v1/requests/{id} |
keys:manage |
GET/POST /v1/keys, PATCH/DELETE /v1/keys/{id} |
A call outside the key’s scopes answers 403 scope_denied. Any valid key can read itself with GET /v1/key.
Models, caps and limits per key
Section titled “Models, caps and limits per key”Beyond scopes, a key can be restricted to a list of models, and capped in USD per day and per month, in requests and tokens per minute, in calls in flight, and in output tokens per request. A key never sees or calls a model outside your organization’s catalog; a model your organization cannot see answers 404 model_not_found, exactly like a model that does not exist.
Sub-keys for isolation
Section titled “Sub-keys for isolation”A key with keys:manage can create sub-keys — one per customer, environment or service — through POST /v1/keys:
- A sub-key can never exceed its parent: scopes, models, caps, rates and expiry must all fit inside the parent’s. Otherwise the API answers
400 child_exceeds_parent. - A sub-key never gets
keys:manage, and there is only one level: sub-keys cannot create sub-keys. - A sub-key inherits its parent’s environment, mode and payment mode, and can be stricter than its parent on data handling, never looser.
- A key lists and manages only its own sub-keys; any other key id answers
404. Likewise,GET /v1/requests/{id}only finds requests made by the key itself or its sub-keys.
If one customer’s sub-key leaks, you disable or revoke that sub-key alone.
The API fails closed
Section titled “The API fails closed”Every call is admitted, priced and reserved before it reaches a model. If the API cannot decide whether a call is allowed — a check that cannot be completed, a catalog that cannot be read — it refuses with 503 and a Retry-After header rather than serving the call unchecked. Refusals for money, policy or rate are always 4xx.
When a model’s provider refuses a request, its explanation is passed on only when you can act on it — a request it cannot serve as written — and only after links and anything shaped like a key or token are removed. Other provider failures are described in Belarel’s own words.
Idempotency
Section titled “Idempotency”An Idempotency-Key is scoped to the API key that sent it, so two keys can never read each other’s stored answers. A key that has been revoked never gets a replay: the API key is checked before the idempotency store is consulted.
To recognize a repeated request, Belarel keeps a fingerprint of its body. The fingerprint is a keyed hash (HMAC-SHA256 with a secret held only by the server), for every key including zero-retention keys, so a stored fingerprint cannot be used to confirm a guessed prompt. If that secret is not available, idempotent calls are refused with 503 rather than fingerprinted without it. See Idempotency.
Provider keys (BYOK)
Section titled “Provider keys (BYOK)”Your own provider keys are validated with the provider before they are stored, encrypted (AES-256-GCM), never returned after saving, and read only by the inference service at the moment it serves a call. See Bring your own key.
Webhooks
Section titled “Webhooks”Every webhook Belarel sends is signed with HMAC-SHA256 using a secret shared only between Belarel and your organization, and the timestamp is part of the signed string, so you can reject replays. Deliveries go only to https:// endpoints on public addresses, and redirects are not followed. See Webhooks for verification code.
Your side of the work
Section titled “Your side of the work”- Keep keys on the server. Never ship a Belarel key in a browser, a mobile app or a public repository. Call the API from your backend.
- Always use HTTPS and the base URL
https://api.belarel.com/v1. - Store keys in a secret manager or environment variable (
BELAREL_API_KEY), never in code. - One key per workload. Separate keys make it possible to revoke one without stopping everything, and keep usage attribution clean.
- Rotate by creating the new key, deploying it, then revoking the old one.
- Keep tags free of personal data. Tags and the
metadatafield are recorded with every call.

