Browse docs
Docs /reference/rest-api
Reference

REST API

Izri's HTTP surface is deliberately small. REST covers the things that must be plain HTTP — health checks, CI triggers, inbound webhooks, file serving — and everything else goes through tRPC.

For programmatic use, prefer the CLI or the MCP server. They wrap this surface and stay in step with it.

Authentication

Authorization: Bearer izri_xxxxxxxxxxxx

The API checks the izri_ prefix first and only then validates the token. Without the prefix the request falls through to cookie-session handling and returns an opaque 401. See API tokens.

Browser sessions use a cookie set by the auth flow; token auth takes precedence when both are present.

Endpoints

GET /health

Liveness. No authentication.

curl https://api.izri.ai/health

GET /api/test-runs/:runId/status

Current status of a test run. Requires tests:read.

curl -H "Authorization: Bearer $IZRI_API_TOKEN" \
     https://api.izri.ai/api/test-runs/01HK.../status

Returns one of PENDING, RUNNING, PASSED, FAILED, ERROR, CANCELLED. See verdicts and exit codes.

For polling, prefer izri run --watch — it already implements sane intervals and a timeout.

POST /api/ci/trigger/:projectId

Trigger a run from a CI system that isn't GitHub Actions.

curl -X POST \
     -H "Authorization: Bearer $IZRI_API_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"branch":"feat/x","commitSha":"abc123"}' \
     https://api.izri.ai/api/ci/trigger/01HK...

Requires tests:run. Returns the run identifier immediately; the run is asynchronous.

POST /api/ci/evidence/:projectId

Upload a commit's test evidence. Two kinds of upload share the route:

  • Tests evidence — the JUnit results of your CI run (the izri/tests verdict) and optionally a coverage report. izri upload sends it.
  • The revert check — the revert check's two runs, at head and with the change reverted. izri verify sends it. It feeds izri/hallucination only, never izri/tests, and is judged once tests evidence for the same commit exists.

Requires tests:run: what you upload becomes the verdict, so a read-only token is refused with 403.

From GitHub Actions, send the job's id-token in X-Izri-OIDC. The job needs permissions: id-token: write; izri upload and the Action then request and send the token themselves. Calling the route yourself, request it with the origin of the Izri URL you upload to as the audience (https://api.izri.ai here; the API's own origin or the app origin that proxies it both work, your own when self-hosting): curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://api.izri.ai" | jq -r .value. A token minted for any other audience, GitHub's default included, is refused with 401. Its signed claims bind the upload to the repository, run and workflow file that produced it, GitHub's record of that run binds the commit, and that is what makes the evidence trusted. Without the header the upload is recorded but never trusted.

curl -X POST \
     -H "Authorization: Bearer $IZRI_API_TOKEN" \
     -H "X-Izri-OIDC: $ACTIONS_ID_TOKEN" \
     -H "Content-Type: application/json" \
     -d @evidence.json \
     https://api.izri.ai/api/ci/evidence/01HK...
{
  "baseSha": "…", "headSha": "…",
  "head": { "junit": "<testsuites>…</testsuites>" },
  "coverage": { "format": "lcov", "report": "TN:\nSF:src/tax.ts\n…" },
  "run": { "id": "123456", "workflowPath": ".github/workflows/ci.yml" }
}

head or skipped: { "reason": "…" } is required. baseSha is informational: the base Izri compares against is the pull request's own, as GitHub reported it. coverage.format is lcov or cobertura. run names the GitHub Actions run that produced the upload; with an id-token Izri confirms the claimed run with GitHub and run may be omitted, and without one the upload is recorded as untrusted whatever run says. The body is capped at 5 MB.

A revert-check upload carries head and reverted: { "junit" } with testsSelected (the number of test files the change selected), or skippedReason: "budget" with testsSelected and no results when that selection was over budget. It takes no coverage or skipped, and reverted needs head; those combinations are refused with 400.

Status Meaning
200 Recorded. Tests evidence returns testRunId, testsStatus, totalTests and trusted. A revert check returns verificationRunId, findings (categories), trusted and judged; judged: false comes with notJudgedReason (for example no izri/tests evidence for this commit) and is judged when that evidence arrives.
400 Invalid body, or unparsable JUnit or coverage. oidc_mismatch: the id-token was issued to another repository, or the project's repository is not a GitHub owner/repo a token can be matched to.
401 oidc_invalid: the X-Izri-OIDC token is not a GitHub Actions id-token for this Izri (bad signature, expired, wrong audience). Nothing is recorded.
409 no_delta_for_head — Izri has no delta for that commit yet. The webhook creates one on push; retry shortly.
413 Over the size cap.
503 oidc_unavailable: GitHub's signing keys could not be fetched to check the id-token. Nothing is recorded; retry.

Whether the evidence is trusted is decided from the id-token's claims and the GitHub compare of the PR, never from the body of the upload.

GET /files/*

Serves stored artifacts — visual snapshots, diffs, reports. Authenticated: these are your organization's artifacts, not public objects.

Inbound webhook receivers

These are endpoints Izri receives on. You don't call them; you configure a provider to.

Endpoint Sender
POST /api/webhooks/github-app GitHub App events
POST /api/webhooks/github/:projectId Per-project GitHub webhook
POST /api/webhooks/stripe Stripe billing events

For webhooks Izri sends you, see outbound webhooks.

Authentication routes

/api/auth/** is handled by the auth layer — OAuth callbacks, session management. Not intended for direct calls.

The GitHub OAuth callback must exactly match the configured app URL. A port mismatch produces a silent redirect loop rather than an error, and it is the most common self-hosting misconfiguration.

tRPC

Everything else is tRPC, mounted at /trpc/*, with the same bearer-token authentication.

The tRPC surface is typed end-to-end and changes with the product. Rather than documenting procedure signatures that would drift, use the clients that are versioned against it — the CLI and the MCP server.

Errors

Standard HTTP status codes.

Status Meaning
401 Missing, malformed, or invalid token. Check the izri_ prefix.
403 Authenticated, but the token lacks the required scope.
404 Not found — or the resource belongs to another organization.
402 The operation requires a higher plan.
429 Rate limited.

404 doubling as a cross-organization permission error is intentional: it avoids confirming that a resource exists to someone who can't access it. If you're sure an ID is valid and still get 404, verify the token belongs to the right organization.

Reading this with an agent? /docs/reference/rest-api.md serves the raw markdown.

Edit this page on GitHub →All docs