Skip to content
Exlogare

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

MethodPathWhat it returns
GET/api/v1/analysesPaginated list of RCAs (newest first).
GET/api/v1/analyses/{id}A single RCA.
GET/api/v1/stats/overviewCounts, severity split, p50/p90 time-to-RCA.
GET/api/v1/stats/timeseriesPer-day failure counts.
GET/api/v1/stats/top-projectsTop N projects by failure count.
GET/api/v1/stats/top-root-causesTop 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.

ParameterTypeNotes
sinceISO 8601 datetimeLower bound on created_at (inclusive). Naive datetimes are treated as UTC.
untilISO 8601 datetimeUpper bound on created_at (inclusive). Window must be <= 365 days.
severitylow / medium / highFilter by RCA severity.
projectstringMatch against project_path (e.g. acme/api) or project_id.
sourcestringOrigin label: gitlab_webhook, github_poll, circleci_ingest, generic_ingest, etc.
cursorstringOpaque cursor from the previous next_cursor.
limitint1..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

StatusMeaning
400Invalid query (bad cursor, since > until, window > 365 days, bad severity).
401Missing or revoked token.
403Token does not have scope=read.
404Analysis id does not belong to your tenant.
429Rate limit exceeded.