The results of a run, newest first
const url = 'https://beta-api.plune.ai/v1/runs/example/results?limit=100&defects=1&status=failed%2Cbroken&muted=true&q=coupon&subStatus=Product%20bug&combination=1';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://beta-api.plune.ai/v1/runs/example/results?limit=100&defects=1&status=failed%2Cbroken&muted=true&q=coupon&subStatus=Product%20bug&combination=1' \ --header 'Authorization: Bearer <token>'The result list of the run page (api.ts → listRunResults), a page at a time: the results the run holds, with their status, outcome, execution and error text, whatever the state of the case they point at — a result is evidence of the run and stays listed when its case is later deleted. Paged (#1380): without limit the answer is the newest 100 results, and next while more remain — the before of the call for the page after it. limit asks for another size, up to 1000; past that it is a 400. A page also stops early, with next, once its error text and failure details pass 64 Mi characters (#814). A caller that wants the whole run follows next until it is absent — the length of one answer is never the size of the run, which GET /v1/runs/{id}/summary counts. Newest first throughout. Each result carries its failure in this same answer, so the page opens the detail without another request, and every errorContext is sanitized on the way out — one stored before the rules existed included. The run report (#1232) narrows the list here, on the server: status, q and defects each narrow what the others left, and every result carries its case’s suitePath, which its Suites view groups by.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Query Parameters
Section titled “Query Parameters ”At most this many results in the answer: 100 when absent, 1000 at the most (#1380).
The next of a previous answer: continue after it.
1: only the results that carry at least one defect link (#1064). Anything else is the whole run.
Example
failed,brokenOnly the results with one of these statuses (#1232): a comma-separated list, or the key repeated. None named is every status; a word that is not a status is a 400.
Example
trueReads status the way the run’s counters do (#1247): a muted failure — the failed or broken result of a case that was muted when it was written (muted: true) — is counted as muted, not under its status. true: the muted failures, beside the statuses named, or alone when none is; false: the statuses named without them, or every result but them. Without it, status is the result’s own and finds a muted failure too. Anything but true or false is a 400.
Example
couponOnly the results whose case’s name holds this text (#1232) — any part of it, in any case; % and _ are letters here. Trimmed; blank is no filter.
Example
Product bugOnly the results marked with one of these sub-statuses (#1231), by the name a result keeps — the key repeated for several, never a comma (a name may hold one). A name the settings no longer offer still finds the results marked with it. Narrows status as the other filters do.
Example
1Only the results of this combination of the run’s combinations (#1251), by its number. Anything but a number from 0 to 24 is a 400; a number the run has no combination for finds nothing.
Responses
Section titled “ Responses ”Results of the run
object
One stored result, as every reader returns it — a run’s results, a test’s history, a corrected mark.
object
The key the reporter sent (a person’s mark: manual:<caseId>). A result a retest replaced (#1767) keeps it with #retest:<id> appended, in its history.
The number of the result’s combination in its run’s combinations (#1251, ADR 0070). Absent for a result of a run without configurations.
What happened, as it happened — a muted case’s failure is still failed here, and ?status=failed finds it.
Whether the case was muted when this result counted (#1247): stamped from the case when the result is written (a reporter, plune sync, a Cairn ingest), and following the case while the run is open. A failed or broken result of a muted case is counted as muted, not as a failure; a finished run never changes (#1247).
The sub-status a person marked it with (#1231) — the NAME as it stood then: renaming or removing it under Settings → Statuses changes what is offered next, never this. A built-in is its English name (Product bug); the dashboard shows it in its own language. null for none — every reporter’s result, which never carries one.
Returned sanitized — no colour codes, no credentials — whether it was stored before the rules existed or after.
What the platform removed before storing — written whenever something was, even without text; null when nothing ever was.
object
Values replaced by a marker, in the text and in every string of failure, each once. The headline counts only when there is no text — otherwise it repeats a line of it.
Whether the TEXT was cut to 256 KB. A shortened field of failure carries its own ….
Characters the REPORTER cut before sending, as it declared in errorContextOmitted; absent when it said nothing. Not the platform’s cut (truncated).
What the reporter said about the failed attempt, or null.
object
How many steps between the outer and the inner ones were left out. Absent when none were.
object
From the repository root in posix form, the way a case’s specRef names it. A path of the machine is refused: absolute, with a drive or a scheme, from a home (~), or climbing out with ...
object
As the runner named it — screenshot, trace, error-context.
How many artifacts past the listed ones there are — all reachable through ciUrl.
The defect links on this result (#1064), oldest first - present on a run’s results list, and only when there is one. A result carries them only if it failed or broke.
A defect (#1064): the address of a ticket somebody already filed, kept beside a case or a failed result. https only, at most 2000 characters; the label is plain text, at most 200, and is shown as text, never as markup. The author is a userId; the dashboard names them from GET /v1/members. No type: Bugs (#1065) will read the same links as external tickets.
object
Where the result’s case sits in the tree (#1232) — the suites from the root down to its own; a suite filed from a spec stands for the folders above it by its file (["e2e/cart.spec.ts", "Coupons"]). Empty for a case at the root. On a run’s results list, always.
The title of the result’s case (#1705), so a page can name its rows without reading the project’s case list. On a run’s results list; kept after the case is deleted, like the rest of the evidence.
Files uploaded to this result — screenshots, text (JSON, plain text, logs) and, for a project with a bucket, any file — oldest first (ADR 0040, ADR 0041) — present on a run’s results list, and only when there is one. Only finished files: one still waiting for complete is not listed. For storage: db the bytes are GET /v1/files/{id}.
A file of a result, everything but its bytes (ADR 0040, ADR 0041). Only a finished file is ever shown: one that was announced with uploads and not yet completed is in no list.
object
The last segment of the name it was uploaded with, cleaned of credentials as a text is.
In the database (storage: db): image/png, image/jpeg, image/webp, application/json or text/plain — a log is text/plain. In the bucket (storage: bucket): the media type it was announced with, lower case and without parameters, any type.
Bytes as kept: in the database a picture at most 2 MiB and a text at most 512 KiB, counted after its credentials are cleaned, so it can differ from what was sent; in the bucket the size that was announced and found there.
db: Plune keeps the bytes, GET /v1/files/{id} returns them. bucket: they are in the project’s own bucket, and GET /v1/files/{id} answers 409 in_bucket (the way to read them is GET /v1/files/{id}/link).
Present only when more results remain: pass it as before.
Example
{ "results": [ { "id": "6f1c2b3a-0000-4000-8000-0000000000e2", "runId": "6f1c2b3a-0000-4000-8000-0000000000d1", "testCaseId": "6f1c2b3a-0000-4000-8000-0000000000c1", "resultKey": "e2e/checkout.spec.ts::Checkout — coupon applied::chromium", "signature": "2c624232cdd221771294dfbb310aca000a0df6ac8b66b696d90ef06fdefb64a3", "status": "failed", "rawStatus": "failed", "source": "playwright", "expectedStatus": "passed", "outcome": "unexpected", "execution": { "startedAt": "2026-09-16T09:12:05.400Z", "finishedAt": "2026-09-16T09:12:09.021Z", "durationMs": 3621, "retry": 1, "worker": "chromium-1" }, "durationMs": 3621, "caseVersion": 1, "muted": false, "errorContext": "Error: expect(locator).toHaveText(\"−10 %\")\n Expected: \"−10 %\"\n Received: \"0 %\"\n at e2e/checkout.spec.ts:41:31", "errorContextMeta": { "redacted": 0, "truncated": false }, "assertions": [], "steps": null, "failure": { "headline": "Error: expect(locator).toHaveText(\"−10 %\")", "steps": [ "Pay", "Apply the coupon" ], "location": { "file": "e2e/checkout.spec.ts", "line": 41, "column": 31 }, "artifacts": [ { "name": "screenshot", "contentType": "image/png" }, { "name": "error-context", "contentType": "text/markdown" }, { "name": "trace", "contentType": "application/zip" } ], "ciUrl": "https://github.com/plune-ai/plune/actions/runs/35003177826" }, "executedBy": null, "subStatus": null, "createdAt": "2026-09-16T09:12:04.151Z", "defects": [ { "id": "6f1c2b3a-0000-4000-8000-0000000000a8", "url": "https://tracker.example.com/browse/PLN-482", "label": "Coupon is not applied on an empty cart", "authorId": "6f1c2b3a-0000-4000-8000-0000000000a2", "createdAt": "2026-09-16T10:30:00.000Z" } ], "suitePath": [ "e2e/checkout.spec.ts" ] }, { "id": "6f1c2b3a-0000-4000-8000-0000000000e1", "runId": "6f1c2b3a-0000-4000-8000-0000000000d1", "testCaseId": "6f1c2b3a-0000-4000-8000-0000000000c1", "resultKey": "e2e/checkout.spec.ts::Checkout — empty cart::chromium", "signature": "7f974c61f8f10065cd8ae6993cc069e4f85b1db1ed008b965885fb6f945a28c9", "status": "passed", "rawStatus": "passed", "source": "playwright", "expectedStatus": "passed", "outcome": "expected", "execution": { "startedAt": "2026-09-16T09:12:04.000Z", "finishedAt": "2026-09-16T09:12:05.310Z", "durationMs": 1310, "retry": 0, "worker": "chromium-1" }, "durationMs": 1310, "caseVersion": 1, "muted": false, "errorContext": null, "errorContextMeta": null, "assertions": [], "steps": null, "failure": null, "executedBy": null, "subStatus": null, "createdAt": "2026-09-16T09:12:04.151Z", "suitePath": [ "e2e/checkout.spec.ts" ] } ]}limit is not an integer from 1 to 1000, before is not a next of this list, or status names a word that is not a status
object
Example
{ "error": "`limit` must be an integer from 1 to 1000"}No / invalid token or session
Not found or not yours
object
Example generated
{ "error": "example"}