Read API (v1)
Versioned, token-authenticated REST API for reading RCA history and stats — for dashboards, scripts, and bots.
/api/v1 is the stable contract for reading your data out of Exlogare. Use it from Grafana, scheduled scripts, Slack/Discord bots, or anywhere you’d otherwise scrape the dashboard.
The browser-facing /api/analyses endpoint is not versioned — it can change shape when the dashboard evolves. /api/v1 is the integration surface and we will only deprecate it with a clear migration window.
Authentication
Create a token with scope=read at Settings → API tokens (admin role). On Free you get up to 3 active tokens; paid plans are unlimited.
curl -H "Authorization: Bearer $EXLOGARE_TOKEN" \
"https://api.exlogare.net/api/v1/analyses?since=2026-04-01"
For terminal and cron workflows, the Exlogare CLI uses the same /api/v1 surface:
exl analyses list --since 2026-04-01T00:00:00Z
exl analyses show <analysis-id>
A token created with scope=ingest only will get 403 here — that’s by design, so a leaked ingest token can never be repurposed to exfiltrate analysis data.
Endpoints
| Method | Path | What it returns |
|---|---|---|
GET | /api/v1/analyses | Paginated list of RCAs (newest first). |
GET | /api/v1/analyses/{id} | A single RCA. |
GET | /api/v1/stats/overview | Counts, severity split, p50/p90 time-to-RCA. |
GET | /api/v1/stats/timeseries | Per-day failure counts. |
GET | /api/v1/stats/top-projects | Top N projects by failure count. |
GET | /api/v1/stats/top-root-causes | Top N root cause clusters. |
Each endpoint accepts a Bearer exl_… token. No CSRF header is required (CSRF is a browser concern; tokens already prove origin).
GET /api/v1/analyses
Cursor-paginated, newest-first. Use the cursor, not page numbers — new analyses keep arriving and offsets shift.
| Parameter | Type | Notes |
|---|---|---|
since | ISO 8601 datetime | Lower bound on created_at (inclusive). Naive datetimes are treated as UTC. |
until | ISO 8601 datetime | Upper bound on created_at (inclusive). Window must be <= 365 days. |
severity | low / medium / high | Filter by RCA severity. |
project | string | Match against project_path (e.g. acme/api) or project_id. |
source | string | Origin label: gitlab_webhook, github_poll, circleci_ingest, generic_ingest, etc. |
cursor | string | Opaque cursor from the previous next_cursor. |
limit | int | 1..500, default 100. |
Response:
{
"items": [
{
"id": "39af2c01-...",
"provider": "gitlab",
"source": "gitlab_webhook",
"ci_run_id": "654",
"ci_job_id": "1552",
"project_id": "12345",
"project_path": "apex/apexframework/release-server",
"project_web_url": "https://gitlab.com/apex/apexframework/release-server",
"pipeline_url": "https://gitlab.com/apex/apexframework/release-server/-/pipelines/654",
"job_url": "https://gitlab.com/apex/apexframework/release-server/-/jobs/1552",
"mr_iid": null,
"root_cause": "Connection refused: postgres:5432",
"explanation": "...",
"fix_suggestion": "...",
"severity": "high",
"confidence": 0.87,
"created_at": "2026-04-21T10:15:00Z"
}
],
"next_cursor": "MjAyNi0wNC0yMVQxMDoxNTowMHwzOWFmMmMwMS0...",
"limit": 100
}
Walk pages until next_cursor is null:
cursor=""
while :; do
page=$(curl -fsS -H "Authorization: Bearer $EXLOGARE_TOKEN" \
"https://api.exlogare.net/api/v1/analyses?limit=200${cursor:+&cursor=$cursor}")
echo "$page" | jq -r '.items[] | [.created_at, .severity, .project_path, .root_cause] | @tsv'
cursor=$(echo "$page" | jq -r '.next_cursor // empty')
[ -z "$cursor" ] && break
done
GET /api/v1/analyses/{id}
Returns a single RCA in the same shape as an items entry. Returns 404 if the id doesn’t belong to your tenant.
Stats
All /api/v1/stats/* endpoints accept days (1..365, default 30). top-projects and top-root-causes also take limit (1..50, default 10).
curl -H "Authorization: Bearer $EXLOGARE_TOKEN" \
"https://api.exlogare.net/api/v1/stats/overview?days=7"
{
"failures_detected": 42,
"analyses_completed": 41,
"rca_count": 41,
"severity_counts": {"low": 11, "medium": 18, "high": 12},
"avg_time_to_rca_seconds": 27.4,
"p50_time_to_rca_seconds": 12.0,
"p90_time_to_rca_seconds": 78.1,
"window_days": 7
}
Recipes
Morning digest to Slack
Drop a morning-digest.py script in cron / a scheduled GitHub Action. Reads RCAs from yesterday and posts a summary to your Slack incoming-webhook URL. Sample script: /integrations/scripts/morning-digest.py.
If you want a shell-only version without Python, use the CLI:
exl analyses list --since 24h --severity high --json \
| jq -r '.items[] | "[\(.severity)] \(.project_path): \(.root_cause)"'
Grafana dashboard
We ship a ready-to-import Grafana dashboard backed by the Infinity datasource: /integrations/grafana/exlogare-rca.json. Import in Grafana → set the bearer token → done. See the import notes in the Grafana integration guide.
Pull-based Slack/Discord bot
Tenant-side bots that prefer pulling over our webhook fan-out can poll /api/v1/analyses?since=$LAST_RUN&severity=high every 5 minutes. Use the next_cursor to walk pages so you never miss an event during a backlog.
Errors
| Status | Meaning |
|---|---|
400 | Invalid query (bad cursor, since > until, window > 365 days, bad severity). |
401 | Missing or revoked token. |
403 | Token does not have scope=read. |
404 | Analysis id does not belong to your tenant. |
429 | Rate limit exceeded. |