Authentication and API keys
Every API route authenticates with an API key. The key carries your account and its permissions (scopes); you never have to say which client you are, and you cannot point at another one.
Getting a key
You create your keys yourself in the app, under Settings → Settings → API keys. Only the account owner and its administrators can create or revoke a key; other members see the list. Paid tiers only: on the Discovery plan, creation is refused (plan_free_no_api).
- Name the key (for example "Looker Studio" or "warehouse sync"), tick the scopes the integration needs, and pick an expiry: never, 90 days or 1 year.
- The key is shown exactly once. Only its SHA-256 hash is stored: DURUM cannot show it to you again. Keep it in a secrets manager.
- To revoke a key, use the Revoke button on the same page. A revoked or expired key answers
401immediately.
A key only sees the account it was created on. No API parameter can point at another account, and every response is checked before it leaves: a row that did not belong to your account blocks the whole response (500 tenant_barrier).
Key format
A key always starts with durum_sk_live_, followed by 32 random characters (base64url alphabet). Example shape: durum_sk_live_Ab3dEf…. The first eight characters after the prefix act as a readable identifier (the "prefix") in exchanges with your account manager: they let you name a key without ever writing it out in full.
Using the key
Pass the key in the Authorization header with the Bearer scheme:
curl "https://app.durum.ai/api/v1/dashboard/kpis" \
-H "Authorization: Bearer durum_sk_live_YOUR_KEY"The x-api-key: durum_sk_live_YOUR_KEY header is also accepted. When both are present, Authorization is read first.
Never expose the key in a browser
The API accepts requests from any origin (open CORS): the key is the only protection. Call the API from a server, a script or a BI tool, never from code your visitors can see.
Scopes
A key only reaches the scopes it carries. Each route requires exactly one scope; without it, the response is 403.
Read scopes
| Scope | Grants access to |
|---|---|
read:dashboard | aggregated KPIs, daily series, breakdowns |
read:ads | spend per ad and per campaign, ad rules and run history |
read:attribution | multi-touch attribution, capture health, attribution report |
read:events | CRM events (leads, bookings, sales, payments, cancellations) and their summary |
read:sales | sales summary, per-rep performance, sales cycle |
read:finance | P&L and revenue (margins stripped) |
read:funnel | funnel and conversion |
read:contacts | contacts, customers, purchases |
read:calls | a contact's calls, call list, transcript, recording |
read:journeys | customer journeys, one contact's journey, touchpoint feed |
read:creatives | pending creative needs |
read:icp | ICP criteria |
read:capi | connected Meta pixels (without tokens) |
Write scopes
They are never granted by default: request them explicitly.
| Scope | Allows |
|---|---|
write:goals | replace your KPI goals |
write:settings | set your sale counting rule |
write:icp | set your ICP criteria |
write:ads | enable, disable, switch to dry-run or delete an ad rule |
write:creatives | get an upload authorization for a visual, import a creative into the reserve, acknowledge creative needs |
Global scope
The * scope grants all scopes, read and write. It is only issued on a justified request.
Rate limit
- 600 requests per minute per key, counted in fixed 60-second windows.
- Every response carries the
X-RateLimit-Limit(600) andX-RateLimit-Remaining(requests left in the current window) headers. - Beyond that, the response is
429withretry_after_seconds: 60. - Failed authentications are also throttled per IP address: past 20
401responses in one minute, the answer is429withtoo_many_failed_attemptsfor the rest of the minute.
Access log
Every authenticated request is logged by DURUM: key identifier, method, path, required scope, response code, IP address and timestamp. Each key's last-used date is updated as well. The log serves security audits; it is not readable through the API.
Authentication and access errors
| Code | JSON body | Cause |
|---|---|---|
401 | { "error": "invalid_or_missing_api_key" } | header missing, key malformed, unknown, revoked or expired |
403 | { "error": "insufficient_scope", "required": "read:calls" } | the key lacks the required scope (required says which one) |
403 | { "error": "plan_free_no_api" } | the workspace is on the Discovery plan: the REST API and the MCP server require a paid tier |
403 | { "error": "forbidden" } | the requested resource (for example a call) does not belong to your account |
429 | { "error": "rate_limited", "retry_after_seconds": 60 } | rate limit exceeded |
429 | { "error": "too_many_failed_attempts", "retry_after_seconds": 60 } | too many failed authentications from your IP address |
500 | { "error": "tenant_barrier" } | the response was blocked by the account check; contact DURUM |
The other codes (400, 404, 405, 502) are described in the API reference.