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_xxxxxxxxxxxxThe 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/healthGET /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.../statusReturns 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/testsverdict) and optionally a coverage report.izri uploadsends it. - The revert check — the revert check's two runs, at head and with the change reverted.
izri verifysends it. It feedsizri/hallucinationonly, neverizri/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.
Related
Reading this with an agent? /docs/reference/rest-api.md serves the raw markdown.
Edit this page on GitHub →All docs