Queue a result for human review
const url = 'https://beta-api.plune.ai/v1/review-items';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"resultId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","note":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://beta-api.plune.ai/v1/review-items \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "resultId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "note": "example" }'A person queues a disputed result here. The Cairn ingest also queues entries, but they are PROPOSED CASES rather than results (#10, ADR-CI-03) and arrive through /v1/ingest/cairn. One item per result.
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”object
Must be a result in your project.
Why this one is worth a look.
Example generated
{ "resultId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "note": "example"}Responses
Section titled “ Responses ”Queued, pending
One entry of the review queue. Always starts pending; a reviewer moves it on.
object
Present on an entry disputing a RESULT. Exactly one of resultId, proposedCase and discoveredTest is present: an entry with two asks two questions at once, and one with none asks nothing.
Present on an entry proposing a new case. Mutually exclusive with the other two.
object
Content-derived identity; the dedup key.
object
object
object
Absent means never executed — NOT “no result yet”.
What a match resolves through. Absent is a normal proposal, not a degraded one: an agent that names no key gets an entry with no comparison, which is exactly what a genuinely new case gets.
An identifier another tool already uses for this test, so a report can find its case without anyone typing a Plune id. The order of kind is load-bearing: it is the ranking that decides which key wins when several match different cases.
object
Present on an entry reporting a test the caller RUNS and this project has no case for. Mutually exclusive with the other two.
object
The identity. Order does not matter — the server ranks them and the highest-ranked kind is what deduplication reads, so two adapters sending the same keys in a different order do not produce two entries about one test.
An identifier another tool already uses for this test, so a report can find its case without anyone typing a Plune id. The order of kind is load-bearing: it is the ranking that decides which key wins when several match different cases.
object
The reporter that found it, e.g. playwright.
Where the test lives, e.g. e2e/checkout.spec.ts:12. Required while everything descriptive is optional: an approved discovery becomes an automated case, and that shape needs a spec to point at.
What it did on this run, in the reporter’ own word.
The run that turned it up, once stored.
Present on every item rejected after R4, and absent forever on rejections stored before it — those are never migrated and must keep reading without error. An item that is pending or approved usually has none, but MAY still carry one: a rejection returned to review keeps the pair in the row as a draft, and the next move to rejected must supply it in the request anyway. Read it as “meaningful only while state is rejected”, not as “absent otherwise”.
Example
{ "schemaVersion": 1, "proposedCase": { "execution": "auto", "origin": { "mode": "explore" }, "source": { "kind": "url" }, "verdict": "passed", "externalKeys": [ { "kind": "playwright-id" } ] }, "discoveredTest": { "keys": [ { "kind": "playwright-id" } ] }, "state": "pending", "rejectionCategory": "duplicate"}Missing resultId or an invalid body
object
Example generated
{ "error": "example"}No / invalid token or session
Result not found or not yours
object
Example generated
{ "error": "example"}Already queued — the body carries the existing item id
object
Example generated
{ "error": "example"}Rate-limited: either the per-route throttle or a tenant quota. Retry-After carries the seconds until the window rolls over.
object
Example generated
{ "error": "example"}Headers
Section titled “Headers ”Seconds until the window rolls over