API key authentication
All API requests require an API key passed via the Authorization header using a Bearer token, or via the X-API-Key header.
Getting an API key
API keys are managed at Admin → API Keys in your dashboard. Keys are shown once on creation — store them securely. API access requires a Pro or Council plan.
Tenant scoping
Every API key is scoped to a single tenant. The tenant is determined automatically from the key — you do not need to pass a tenant ID in requests. All data returned is limited to the tenant the key belongs to.
Scopes
API keys are created with specific scopes that control what they can access:
| Scope | Access |
|---|---|
cases:read | List and view cases |
cases:write | Create and update cases |
wards:read | List and view wards |
categories:read | List and view categories |
users:read | List and view users/councillors |
events:read | List and view events |
events:write | Create and update events |
news:read | List and view news articles |
news:write | Create and update news |
reports:read | View reports and analytics |
webhooks:read | List webhook endpoints |
webhooks:write | Manage webhook endpoints |
Scope availability
Pro plans have access to basic scopes (cases, wards, categories, events, news). Council plans unlock all scopes including reports, audit, webhooks, and settings.
Key security best practices
- Never expose keys in client-side code. API keys should only be used in server-to-server requests.
- Rotate keys regularly. You can create multiple keys and deactivate old ones from the Admin dashboard.
- Use the minimum scopes needed. Create separate keys for different integrations, each with only the scopes it requires.
- Store keys in environment variables, not in source code or configuration files committed to version control.
Error responses
If authentication fails, the API returns one of these status codes:
| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 403 | Valid key but insufficient scope or plan for the requested resource |
Both return a JSON body with an error field:
{
"error": "Invalid API key"
}