Appearance
Authentication
Programmatic access uses an API key in the x-api-key header.
bash
curl -H "x-api-key: drse_your_api_key" \
https://api.utskick.dr.se/api/v1/campaignsKeys are prefixed drse_. No OAuth flow, no bearer token, no Authorization header.
Creating a key
Keys are created in the web app — no endpoint mints them, by design. Key-management routes reject API keys with 403 Forbidden: API keys cannot access this resource, so a leaked key can never create more keys or widen its own permissions.
The API is opened per account: an integration is something somebody builds, and we would rather talk to you first. If API in the sidebar shows a lock, click it and ask for access — we answer, usually the same working day.
- Open the web app → API in the sidebar (under Avancerat).
- Create key, give it a name.
- Tick only the permissions the integration needs — see Permissions.
- Optionally set an expiry (1–365 days).
- Copy the key. It is displayed once and cannot be retrieved again.
Treat a key like a password
A key acts as your account for every permission it holds — including creating campaigns, which spends money and mails paper. Keep keys in a secret manager or environment variables; never in source control, client-side code or a browser. If one leaks, revoke it in the web app immediately.
Managing keys
All from the web app's API page: rename, change permissions, disable temporarily, revoke permanently. Changes take effect on the next request.
No credentials, no fallback
A request carrying an x-api-key header is never re-interpreted as a browser session. A bad key returns its specific failure, not a generic 401 — so you can tell "fix the key" from "back off and retry".
Failure reference
| Condition | Status | code | message |
|---|---|---|---|
| No credentials at all | 401 | unauthorized | Unauthorized |
| Unknown or malformed key | 401 | unauthorized | Invalid API key |
| Key past its expiry | 401 | unauthorized | API key expired |
| Key disabled in the web app | 401 | unauthorized | API key disabled |
| Key's usage quota exhausted | 401 | unauthorized | API key usage exceeded |
| Over the key's rate limit | 429 | rate_limited | API key rate limit exceeded |
| Over the account's rate limit | 429 | rate_limited | Account rate limit exceeded |
| Too much traffic from your network | 429 | rate_limited | Too many requests from this IP address |
| Key lacks the endpoint's permission | 403 | forbidden | Forbidden: insufficient API key permissions |
| Endpoint refuses API keys entirely | 403 | forbidden | Forbidden: API keys cannot access this resource |
json
{ "error": { "code": "unauthorized", "message": "API key expired" } }How to respond:
- 401 — a configuration problem. Retrying will not help. Check the header name, then whether the key was revoked, disabled or expired.
- 403
insufficient API key permissions— the key is valid; it lacks a permission. Grant it in the web app. Retrying will not help. - 403
API keys cannot access this resource— this endpoint is web-app-only and no permission will unlock it. - 429 — transient. Back off and retry; see Rate limits.
Rate limit
100 requests per 60 seconds, per key — the limit you will actually meet.
Above it sits a per-account ceiling of 300 requests / 60 seconds covering all your keys together, so issuing more keys no longer multiplies total throughput. Separate keys still isolate one workload's budget from another's.
Every /api/v1 response carries your position in RateLimit-Remaining, and every 429 carries Retry-After. Details and retry strategy: Rate limits.
Auth is checked before routing
On a guarded path, authentication runs before the method and sub-path are matched. An unauthenticated request to a mistyped /api/v1/... URL returns 401, not 404.
bash
curl -X DELETE https://api.utskick.dr.se/api/v1/lists
# → 401 {"error":{"code":"unauthorized","message":"Unauthorized"}}So when an unexpected 401 appears, verify the key before you start doubting the URL. Once authenticated, a genuinely unknown route returns 404 Route not found.
Checking connectivity without a key
GET /api needs no credentials:
bash
curl https://api.utskick.dr.se/api
# → {"data":{"status":"ok"}}200 here plus 401 elsewhere isolates the problem to the key.
Storing keys
bash
export DRSE_API_KEY="drse_your_api_key"
export DRSE_BASE_URL="https://your-deployment.example"ts
const KEY = process.env.DRSE_API_KEY;
if (!KEY) throw new Error("DRSE_API_KEY is not set");
export const drse = (path: string, init: RequestInit = {}) =>
fetch(`${process.env.DRSE_BASE_URL}/api/v1${path}`, {
...init,
headers: { ...init.headers, "x-api-key": KEY },
});Next
Permissions — exactly which endpoints each permission unlocks.