Programmatic API
A read-only REST API for pulling your own project's run history, evidence, release readiness, gate history, flag blast radius and trends into your own systems — an ongoing, machine-token-authenticated counterpart to the dashboard. Team tier and above.
1. Issue a read-only token
On a project's Settings page, click Issue read-only API token. It is shown once — store it as a secret. It can never upload a report and is rejected by the ingest API; an ordinary (write-scoped) CLI token is likewise rejected here.
2. Authenticate
Authorization: Bearer <your read-only token>Every request below requires this header. A missing, invalid, or wrong-purpose token is rejected before any data is returned.
3. List run history
curl -H "Authorization: Bearer $TOKEN" \
"https://api.releasetwin.com/api/v1/projects/<projectId>/reports?limit=50&offset=0"{
"items": [
{
"id": "b6e2...",
"kind": "case",
"caseId": "CASE-1",
"oracleLocator": "tickets/CASE-1",
"passed": true,
"classification": null,
"outcome": null,
"release": "4.2",
"uploadedAt": "2026-09-09T12:00:00Z",
"source": "case",
"flakiness": "Stable"
}
],
"total": 1,
"limit": 50,
"offset": 0
}kindis"case"or"flag-proof";classificationis case-only,outcomeis flag-proof-only.limitdefaults to 50, maximum 200;offsetdefaults to 0. Items are newest first.- The
projectIdin the path must match the token's own project — any other id returns 404. sourceis"case"for an authored case or"junit"for an imported result; an imported row has no oracle reference, so itsoracleLocatorisnullrather than blank.flakinessis"Stable","Flaky"or"Recovering". It describes the case, not this one report, so every report for a flaky case carries it.
4. Get a report's evidence metadata
curl -H "Authorization: Bearer $TOKEN" \
"https://api.releasetwin.com/api/v1/projects/<projectId>/reports/<reportId>/evidence"{
"reportId": "b6e2...",
"reportKind": "case",
"caseId": "CASE-1",
"passed": true,
"screenshotIds": ["a1b2c3..."],
"uploadedAt": "2026-09-09T12:00:00Z",
"redacted": true
}Metadata only — no image bytes are ever embedded in this response. Every stored evidence document is already redacted by the uploading CLI before it reaches ReleaseTwin (see Security & credentials), so redacted is always true.
5. Get one report
curl -H "Authorization: Bearer $TOKEN" \
"https://api.releasetwin.com/api/v1/projects/<projectId>/reports/<reportId>"Returns the same object the list route returns for that report. A report id belonging to another project returns 404 — indistinguishable from one that does not exist, so it cannot be used to probe for other projects.
6. Fetch a screenshot
curl -H "Authorization: Bearer $TOKEN" \
"https://api.releasetwin.com/api/v1/projects/<projectId>/evidence-screenshots/<screenshotId>?reportId=<reportId>" \
--output screenshot.pngResponds with image/png bytes. This is the separate, independently authenticated request the evidence-metadata response above deliberately does not save you: metadata never embeds image bytes. reportId is required — it is what proves the screenshot belongs to an evidence document in this project.
7. Release readiness
curl -H "Authorization: Bearer $TOKEN" \
"https://api.releasetwin.com/api/v1/projects/<projectId>/releases/2026.9.1?window=14d"{
"release": "2026.9.1",
"headline": "Incomplete",
"greenCount": 3,
"failingCount": 1,
"staleCount": 1,
"flakyCount": 1,
"windowDays": 14,
"incompleteReason": "Flaky",
"cases": [
{ "caseId": "CHECKOUT-SMOKE", "state": "Green", "latestOutcome": "passed", "latestReportAt": "2026-09-11T10:00:00+00:00" },
{ "caseId": "CHECKOUT-FLAKE", "state": "Flaky", "latestOutcome": "failed", "latestReportAt": "2026-09-11T10:00:00+00:00" }
]
}headlineis"Proven","NotProven"or"Incomplete". A flaky case cannot make a releaseProven;incompleteReasonsays whether a flaky or a stale case is holding it back, and isnullfor every other headline.windowaccepts7d,14d,30dor90dand defaults to14d; anything else returns 400.- One computed document per release, so this route is not paged.
8. Compare two releases
curl -H "Authorization: Bearer $TOKEN" \
"https://api.releasetwin.com/api/v1/projects/<projectId>/releases/diff?from=2026.9.0&to=2026.9.1"{
"from": "2026.9.0",
"to": "2026.9.1",
"regressedCount": 1,
"recoveredCount": 1,
"unchangedCount": 1,
"flakyCount": 1,
"cases": [
{ "caseId": "CHECKOUT-SMOKE", "from": "Green", "to": "Failing", "changeKind": "regressed" },
{ "caseId": "CHECKOUT-NEW", "from": "NoSignal", "to": "Green", "changeKind": "no-signal" }
]
}Each side is "Green", "Failing" or "NoSignal" — a time-independent comparison, so there is no Stale here. changeKind is regressed, recovered, unchanged, no-signal or flaky: a flip by a flaky case is not attributable to either release. Both from and to are required.
9. Flag-proof gate history
curl -H "Authorization: Bearer $TOKEN" \
"https://api.releasetwin.com/api/v1/projects/<projectId>/flag-proof-gate/history?limit=50&offset=0"{
"items": [
{
"id": "3333...",
"release": "2026.9.1",
"flagKeys": ["checkout-v2"],
"verdict": "pass",
"flagStates": ["checkout-v2:on"],
"checkedAt": "2026-09-11T10:00:00+00:00"
}
],
"total": 1,
"limit": 50,
"offset": 0
}The recorded verdicts, newest first. This reads history — running the gate is a write, and happens in CI. Paged like the report list.
10. Flag blast radius
curl -H "Authorization: Bearer $TOKEN" \
"https://api.releasetwin.com/api/v1/projects/<projectId>/flags/checkout-v2"{
"flagKey": "checkout-v2",
"items": [
{ "caseId": "CHECKOUT-SMOKE", "state": "Green", "tracker": "jira", "ticketKey": "CHK-1" },
{ "caseId": "CHECKOUT-ORPHAN", "state": null, "tracker": null, "ticketKey": null }
],
"total": 2,
"limit": 50,
"offset": 0
}- Scoped to this token's project. The dashboard shows the blast radius across every project in the organization; a read-only token reaches one project, so this route answers for that project alone.
stateisnullwhen the case's most recent flag-proof report carries no release label, andtracker/ticketKeyare both null when its oracle locator does not resolve to a connected tracker.
11. Trends
curl -H "Authorization: Bearer $TOKEN" \
"https://api.releasetwin.com/api/v1/projects/<projectId>/trends?window=30d"{
"window": "ThirtyDays",
"granularity": "day",
"buckets": [
{ "start": "2026-09-11T10:00:00+00:00", "casePassRate": 0.75, "flagProofPassRate": 1, "runVolume": 4, "classificationBreakdown": { "ProductBug": 1 } },
{ "start": "2026-09-12T10:00:00+00:00", "casePassRate": null, "flagProofPassRate": null, "runVolume": 0, "classificationBreakdown": {} }
],
"flakiestCases": [
{ "caseId": "CHECKOUT-FLAKE", "flipCount": 4, "lastActivity": "2026-09-11T10:00:00+00:00", "flakiness": "Flaky" }
]
}- A rate is
null, never0, when its denominator is zero — plot it as a gap, because0would read as “everything failed”. windowaccepts7d,30dor90dand defaults to30d. 90-day windows bucket by ISO week, shorter ones by UTC day —granularitysays which.flipCountis the windowed raw signal, whileflakinesscomes from the case's whole history, so the two can legitimately disagree.
Plan entitlements
Every route needs the programmaticApiAccess entitlement a read-only token comes with. Sections 7 and 8 additionally need releaseRollup, section 9 flagProofMergeGate, section 10 flagBlastRadiusView, and section 11 trendAnalytics. Access to the API is not access to every feature in it, so a plan without release rollups cannot read them here either.
{ "error": "entitlement-required", "entitlement": "releaseRollup" }The refusal names the feature's entitlement, not programmaticApiAccess, which by that point has already passed — so the key in the response is the one to go and change.
Rate limits
Sized for a customer's own polling job, not a CI burst. An over-limit token receives a 429 with a Retry-After header; other tokens are unaffected.
Next
MCP server — the same routes, wired into an AI coding agent so it can read your evidence directly.
Hosted platform — issuing tokens and what the dashboard shows you directly.