Skip to content

One queue entry, with its comparisons

GET
/v1/review-items/{id}
curl --request GET \
--url https://beta-api.plune.ai/v1/review-items/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0 \
--header 'Authorization: Bearer <token>'

The same object the listing returns, assembled by the same code — so a client holding an id never sees a different shape from one reading the page. An item of another project and an id that was never issued get the same refusal, word for word.

id
required
string format: uuid

The entry

Media type application/json

A review item plus enough of what it points at to be judged without opening it.

object
schemaVersion
required
integer
Allowed values: 1
id
required
string format: uuid
resultId

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.

string format: uuid
proposedCase

Present on an entry proposing a new case. Mutually exclusive with the other two.

object
stableId
required

Content-derived identity; the dedup key.

string
title
required
string
<= 300 characters
execution
required
string
Allowed values: auto manual
steps
required
Array<string>
>= 1 items <= 20 items
expected
required
string
<= 2000 characters
origin
required
object
runId
required
string
mode
required
string
Allowed values: explore design api
target
required
string
technique
string
<= 32 characters
priority
string
source
object
kind
required
string
Allowed values: url openapi-operation requirement
value
required
string
>= 1 characters <= 100 characters
rationale
string
>= 1 characters <= 400 characters
runCost
object
tokens
required
integer
cases
required
integer
verdict

Absent means never executed — NOT “no result yet”.

string
Allowed values: passed failed
externalKeys

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.

Array<object>
<= 32 items

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
kind
required
string
Allowed values: playwright-id path-title allure-history cairn-stable eval-id qase testrail
value
required
string
<= 1024 characters
discoveredTest

Present on an entry reporting a test the caller RUNS and this project has no case for. Mutually exclusive with the other two.

object
keys
required

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.

Array<object>
>= 1 items <= 8 items

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
kind
required
string
Allowed values: playwright-id path-title allure-history cairn-stable eval-id qase testrail
value
required
string
<= 1024 characters
title
required
string
<= 300 characters
source
required

The reporter that found it, e.g. playwright.

string
<= 32 characters
specRef
required

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.

string
<= 400 characters
rawStatus

What it did on this run, in the reporter’ own word.

string
<= 64 characters
platformRunId

The run that turned it up, once stored.

string
state
required
string
Allowed values: pending approved rejected
rejectionCategory

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”.

string
Allowed values: duplicate incorrect out-of-scope low-value not-testable other
note
string
createdAt
required
string format: date-time
updatedAt
required
string format: date-time
testCaseId
required
string format: uuid
testCaseTitle
required
string
runId
required
string format: uuid
runAt
required
string format: date-time
passed
required
integer
failed
required
integer
verdictsPassed

Judge questions; absent when none judged.

integer
verdictsFailed
integer
match

Only on an entry carrying proposedCase whose keys matched a case here.

object
testCaseId
required
string format: uuid
title
required
string
matchedBy
required

Which key established the match. Named because when several keys point at different cases exactly one is shown, chosen by the kind ranking, and an unexplained choice reads as arbitrary.

object
kind
required
string
Allowed values: playwright-id path-title allure-history cairn-stable eval-id qase testrail
value
required
string
<= 1024 characters
differences
required

Empty means the proposal matches the existing case field for field.

Array<object>

One difference between a proposal and the case it was matched to. The list is FLAT and ORDERED rather than keyed by field, because order is part of what it means — the server decides it so that two reviewers reading the same entry see the same card.

object
field
required

Only what a reviewer reads while deciding. Run identifiers are excluded on purpose: they differ on every regeneration and would report a change every single time.

string
Allowed values: title steps expected execution technique priority
before
required

The value on the existing case, absent when it records none. Untyped on purpose — a string for title, a list for steps; the two sides of steps do not even share a shape in storage, which is why they compare as one block rather than step by step.

after
required

The value the proposal carries. Same typing note as before.

previousRun

Only on an entry carrying resultId for a test that has run here before.

object
previousResultId
required
string format: uuid
previousRunId
required
string format: uuid
previousRanAt
required
string format: date-time
previousStatus
required
string
Allowed values: passed failed broken blocked skipped
previousOutcome
required
string
Allowed values: expected unexpected flaky skipped
statusChanged
required

What happened to the test.

boolean
outcomeChanged
required

Whether it behaved as expected. Independent of statusChanged: a test can stay just as unsuccessful and still move from broken to failed, and that is exactly what changes what a reviewer does next. Both axes are reported always, changed or not, so that “the state moved and the behaviour did not” is never something to infer from a missing field.

boolean
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",
"match": {
"matchedBy": {
"kind": "playwright-id"
},
"differences": [
{
"field": "title"
}
]
},
"previousRun": {
"previousStatus": "passed",
"previousOutcome": "expected"
}
}

No / invalid token or session

Not found or not yours — the two answer identically

Media type application/json
object
error
required
string
Example generated
{
"error": "example"
}