API
Conventions#
Every application endpoint lives under /api/v1. Requests and responses are
JSON unless stated otherwise. The self-hosted OpenAPI document is served by the
instance itself — see Discovery — and is the exact route list for
your version. This page documents the shapes and rules that hold across all of
them.
Authentication#
Three credential types reach the API.
Access tokens#
A bearer JWT with a 15-minute lifetime:
curl -H "Authorization: Bearer <ACCESS_TOKEN>" \
https://<ABRQ_HOST>/api/v1/data-connectors
Refresh tokens#
The refresh token is never returned in a response body. It is set as an
httpOnly, SameSite=strict cookie named abrq_refresh, scoped to the path
/api/v1/auth, so it is only ever sent to the auth endpoints.
Refresh is rotating with reuse detection: each refresh invalidates the token it consumed. Presenting an already-consumed refresh token is treated as theft and revokes the whole session — every token in that chain stops working and the user must sign in again.
Personal access tokens#
Long-lived tokens for scripts and integrations. They are prefixed abrq_
and are shown once, at creation.
- Scope is
readorwrite, enforced centrally rather than per route. Areadtoken receives 403 on any unsafe method (POST,PUT,PATCH,DELETE), whatever the endpoint. - A token may optionally be scoped to a project, in which case it cannot see or touch anything outside it.
curl -H "Authorization: Bearer abrq_<TOKEN>" \
https://<ABRQ_HOST>/api/v1/executions
Server-sent events#
EventSource cannot set request headers, so SSE endpoints authenticate with a
query parameter instead:
GET /api/v1/cdc-tables/<CDC_TABLE_ID>/logs/stream?token=<ACCESS_TOKEN>
Warning. A token in a query string is visible to proxies and access logs. Use a short-lived access token for SSE, never a personal access token.
Error envelope#
Every error response uses the same envelope:
{
"detail": "Data connector not found",
"request_id": "5e1c0f6a-2b41-4f8f-9f2a-7d0b1c3e4a55"
}
detail is a human-readable message; request_id correlates the response with
the structured log line for that request — quote it in any support conversation.
Some errors add extra top-level keys, described below.
Status mapping#
| Status | Meaning |
|---|---|
400 |
Malformed request the schema could not even parse |
401 |
Authentication problem — missing, expired, invalid or revoked token |
403 |
Permission denied, insufficient token scope, or a licensing refusal |
404 |
The addressed resource does not exist, or is outside your project scope |
409 |
Conflict with existing state — a duplicate name, or a resource already bound |
422 |
Request parsed, but a field failed validation |
429 |
Rate limit exceeded |
500 |
Unhandled server error; the request_id is the way to find it in the logs |
Structured 403 codes#
Two of the 403 cases carry machine-readable structure beyond detail.
RBAC refusal — permission_denied:
{
"detail": "permission_denied",
"reason": "not_a_member",
"required_capability": "connector:write",
"project_id": "3f0b0d4c-8f1e-4a0e-9d1b-1c2f3a4b5c6d",
"environment_id": null,
"request_id": "0c9f2b1a-77d3-4a41-9d2e-6b0a5c8e1f30"
}
License refusal — license_expired:
{
"detail": "license_expired",
"request_id": "b2d4f6a8-1c3e-4f50-8a9b-0d1e2f3a4b5c"
}
A license_expired response means the instance has dropped into read-only
mode: reads continue to work, writes are refused platform-wide.
Rate limits#
| Scope | Limit |
|---|---|
| All endpoints | 100 requests / minute |
| Login | 10 requests / minute |
Two headers accompany rate-limited routes:
| Header | Meaning |
|---|---|
X-RateLimit-Remaining |
Requests left in the current window |
X-RateLimit-Reset |
When the current window resets |
Health, metrics and discovery#
GET /health#
{
"status": "ok",
"version": "1.4.1",
"commit": "cdcbcc8",
"deps": {
"framework_db": "ok",
"redis": "ok"
}
}
Returns 200 when both dependencies report ok, and 503 otherwise. Both the framework database and Redis must be healthy for the instance to be considered up.
Note.
GET /healthis the only health endpoint. There is no/ready, no/readyz, and no/healthz. Point every liveness and readiness probe at/healthand treat the same endpoint as both.
GET /metrics#
Prometheus text exposition of every metric the backend exposes. See Metrics.
OpenAPI#
A self-hosted instance serves its own interactive API documentation at /docs
and the raw schema at /openapi.json. Those are the authoritative route list
for the version you are running.
Route-prefix inventory#
The API is organised into the prefixes below. These are prefixes, not
callable paths — read /openapi.json on your instance for the concrete routes
and their parameters.
| Area | Prefixes under /api/v1 |
|---|---|
| Identity and access | auth, users, me, api-tokens, sso, ldap |
| Audit | audit-log, audit-forwarder-settings |
| Connectivity | data-connectors |
| Ingestion | cdc-tables, streams, inboxes, email-feeds, file-ingestions |
| Delivery | exports |
| Alerting | notification-configs, notification-settings, alert-rules |
| Platform | license, admin |
| Metadata and governance | lineage, catalog, masking-policies, retention-policies |
| Operations | overview, executions, schedules/dashboard |
| Modelling and AI | dw-models, ai-providers |
| ETL | the etl/* family |
Streaming endpoints#
Two endpoints stream server-sent events rather than returning a body:
| Stream | Purpose |
|---|---|
| CDC log tail | Live run-log lines for one CDC table |
| Stream runs | Live run status for one stream |
Both authenticate with ?token= as described above.