Change what an open run declares, or what any run is for
const url = 'https://beta-api.plune.ai/v1/runs/example';const options = { method: 'PATCH', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"title":"plune · 2026-09-16 09:12 · ci","environment":"staging","labels":["plune","vitest","ci","nightly"],"meta":{"sha":"3e31591","branch":"master","ciUrl":"https://github.com/plune-ai/plune/actions/runs/35003177826","runner":"playwright"}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request PATCH \ --url https://beta-api.plune.ai/v1/runs/example \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "title": "plune · 2026-09-16 09:12 · ci", "environment": "staging", "labels": [ "plune", "vitest", "ci", "nightly" ], "meta": { "sha": "3e31591", "branch": "master", "ciUrl": "https://github.com/plune-ai/plune/actions/runs/35003177826", "runner": "playwright" } }'Only while the run is open. Changing what a finished run EXPECTED would change its notRun after the fact, and the summary is frozen precisely so that cannot happen. The one exception is description (#1232): it moves no number, so a body that holds nothing else is taken by a finished or terminated run too. custom is not an exception (#1764): on a closed run it is a 409, alone or with description. An owner or a member writes it; a reader is refused with 403.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Request Body required
Section titled “Request Body required ”object
What the run expects to happen. Without it, a crashed shard is indistinguishable from a green run.
object
object
object
Deprecated: not read; configurations are run fields (ADR 0070). Taken and stored as sent.
object
The caller states that expected is everything its source has — the whole suite, not one file or a --grep. When such a run finishes with an empty notRun, every active case that this source (the source of the run’s results) reported before and did not report in this run moves to detached, with the run named as the reason. Manual cases, other sources, in_review and deprecated cases are never touched; a later result on a detached case brings it back to active by itself. Set it only for the run that covers the whole suite (PLUNE_FULL_RUN=1 in the CLI).
Who is to run which case (B8-team T4): testCaseId → userId. Filled at launch by assignment: prefer-assignee from each expected case’s assigneeId at that moment — a snapshot, so a case that changes hands later does not rewrite a run under way. Cases nobody holds are absent. May also be sent as is.
object
object
The commit the run was made from — run.sha in GET /v1/runs/{id}/summary.
The branch it was made on — run.branch in GET /v1/runs/{id}/summary.
The CI run that produced the report — the reporter reads the runner’s own config.metadata.ci.buildHref. Sanitized before it is stored (a credential in a query parameter becomes a marker); set by the shard that opens the run and never changed by one that joins it. The run page links it from GET /v1/runs/{id}/summary.
The tool that reported the run — playwright, or the report format plune run import read — not the machine of the CI; run.runner in GET /v1/runs/{id}/summary.
The plan the run was launched from (D33), when it was — what GET /v1/runs?planId= filters by.
How many cases the plan’s collections selected when the run was launched (#1274); a count taken then, so it does not follow the plan afterwards.
The plan’s collections (filter query strings) as they stood when the run was launched (#1274); a copy, so the run still says what it selected by after the plan is edited or deleted.
What the run is for — plain text, at most 2 000 characters. null, an empty or a blank text removes it.
Custom field values to change (#1764): a key sets it, null clears it, a key left out keeps its value. Checked as a case’s are - a 400 that names the field and the rule. The values of a configuration group (#1251) are fixed at launch: a body that changes them is a 400; the same values sent back are taken.
object
Example
{ "title": "plune · 2026-09-16 09:12 · ci", "environment": "staging", "labels": [ "plune", "vitest", "ci", "nightly" ], "meta": { "sha": "3e31591", "branch": "master", "ciUrl": "https://github.com/plune-ai/plune/actions/runs/35003177826", "runner": "playwright" }}Responses
Section titled “ Responses ”The updated run
object
object
The value rerun is reached only by joining a terminated run again — it is never requested. scheduled (#1233): saved and not launched yet — launch, or a reporter joining by its key, starts it.
What the run expects to happen. Without it, a crashed shard is indistinguishable from a green run.
object
object
object
Deprecated: not read; configurations are run fields (ADR 0070). Taken and stored as sent.
object
The caller states that expected is everything its source has — the whole suite, not one file or a --grep. When such a run finishes with an empty notRun, every active case that this source (the source of the run’s results) reported before and did not report in this run moves to detached, with the run named as the reason. Manual cases, other sources, in_review and deprecated cases are never touched; a later result on a detached case brings it back to active by itself. Set it only for the run that covers the whole suite (PLUNE_FULL_RUN=1 in the CLI).
Who is to run which case (B8-team T4): testCaseId → userId. Filled at launch by assignment: prefer-assignee from each expected case’s assigneeId at that moment — a snapshot, so a case that changes hands later does not rewrite a run under way. Cases nobody holds are absent. May also be sent as is.
object
object
The commit the run was made from — run.sha in GET /v1/runs/{id}/summary.
The branch it was made on — run.branch in GET /v1/runs/{id}/summary.
The CI run that produced the report — the reporter reads the runner’s own config.metadata.ci.buildHref. Sanitized before it is stored (a credential in a query parameter becomes a marker); set by the shard that opens the run and never changed by one that joins it. The run page links it from GET /v1/runs/{id}/summary.
The tool that reported the run — playwright, or the report format plune run import read — not the machine of the CI; run.runner in GET /v1/runs/{id}/summary.
The plan the run was launched from (D33), when it was — what GET /v1/runs?planId= filters by.
How many cases the plan’s collections selected when the run was launched (#1274); a count taken then, so it does not follow the plan afterwards.
The plan’s collections (filter query strings) as they stood when the run was launched (#1274); a copy, so the run still says what it selected by after the plan is edited or deleted.
What to call this run in a list. The first shard to declare one wins; a shard that arrives later fills a field left empty rather than overwriting.
Where it ran — staging, prod, a preview. Filterable on GET /v1/runs, so two CI matrices read as two histories.
How the run is marked — smoke, nightly, a team name. An empty array is how every mark is removed.
What the run is for, in a person’s words (#1232) — plain text; null when nobody wrote one.
Values of the project’s custom fields (#1764), field id → value. Absent while the run has none. Not configuration, which is the execution plan and is no field.
object
The combinations of configuration values the run was launched with (#1251), in the order of the groups and of their values: Chrome · Windows, Chrome · macOS, Firefox · Windows, Firefox · macOS. Computed by the server from custom when the run is created and frozen; the values of a group cannot change afterwards. Absent for a run launched with none. The one addition: a batch from CI that names a combination the list lacks (configuration of POST /v1/runs/{id}/results) appends it at the end — a number, once given, never changes.
One combination of configuration values (#1251, ADR 0070 §2): a value of each chosen group, the groups in their order. Names are frozen at launch: a rename later changes no past run.
object
object
The group (a field with role: configuration); null with optionId for a value CI named that is not in the list.
The value (an option of the group); null for a value CI named that is not in Settings → Configurations — «not in the list», kept as it was sent.
The name of the group at launch.
The label of the value at launch.
What a run amounted to, frozen at finish. Distinct from RunSummary, which is the list-item shape.
object
One entry per status that occurred, and muted when a muted case failed or broke; an absent key means zero. A failed or broken result of a muted case is counted as muted, not as a failure; a finished run never changes (#1247).
object
counts again, by the priority each result’s case had when the run finished (#1379): { "critical": { "passed": 4, "failed": 1 } }. It is what Analytics counts a finished run under by priority, so a case’s priority changed later moves none of it. Absent from a summary frozen before it was kept.
object
object
Expected and never reported — what keeps a dead shard from looking green.
Cases a retest reset that wait for a new result (#1767): in no count, not in notRun, not in total. Only on a live summary: a run cannot finish while any wait, so a frozen one never has it.
Only for a run with configurations (#1251, ADR 0070 §4): the counts again per combination, in the order of the run’s combinations. notRun and awaitingRetest are by the pair (case, combination): a combination nobody reported has empty counts, total 0 and every expected case in notRun — none of it is counted as skipped. The run’s own notRun is then the cases with a result in no combination, its awaitingRetest the cases waiting in any one.
object
One entry per status that occurred in this combination; an absent key means zero.
object
Expected cases with no current result in this combination.
Cases a retest reset that wait for a new result in this combination; absent for none.
Only for a run with configurations (#1251): the cases that have a result, by where each stands — the worst of its combinations (failed, broken, blocked, muted, passed, skipped): «4 of 120 cases failed». A case in the Bin is not counted (as in /cases?status= and the re-run).
object
Who launched it (B8-team T3): the userId of the session, or of the owner of the token that started or synced it — the first shard to create the run; a joiner is not recorded. null for a run from before names were kept. Resolve it to a name through GET /v1/members.
Example
{ "run": { "id": "6f1c2b3a-0000-4000-8000-0000000000d1", "projectId": "u-shop::default", "kind": "automated", "externalKey": "gh-35003177826", "statusEvent": "launched", "configuration": { "full": true, "expected": [ { "testCaseId": "6f1c2b3a-0000-4000-8000-0000000000c1" }, { "externalKey": { "kind": "path-title", "value": "e2e/checkout.spec.ts > Checkout — coupon applied" } } ] }, "meta": { "sha": "3e31591", "branch": "master", "ciUrl": "https://github.com/plune-ai/plune/actions/runs/35003177826", "runner": "playwright" }, "startedAt": "2026-09-16T09:12:04.151Z", "finishedAt": null, "terminateReason": null, "summary": null, "title": "plune · 2026-09-16 09:12 · ci", "environment": "staging", "labels": [ "plune", "vitest", "ci", "nightly" ], "description": null, "createdBy": "u-shop" }}Unknown field, invalid configuration, or a meta.ciUrl that is not an http or https address
object
Example
{ "error": "validation failed — meta.ciUrl: must be an http or https address"}No / invalid token or session
A reader of the project — it may read the run, not change it
object
Example generated
{ "error": "example"}Not found or not yours
object
Example generated
{ "error": "example"}The run is closed, and the body changes more than its description (custom included)
object
Example generated
{ "error": "example"}Rate limited