Skip to content

File a bug

POST
/v1/bugs
curl --request POST \
--url https://beta-api.plune.ai/v1/bugs \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "title": "The cart loses an item after a coupon is applied", "description": "Add two items, apply the coupon `SPRING`, remove one: **both** are gone.", "caseIds": [ "6f1c2b3a-0000-4000-8000-0000000000c1" ] }'

An owner’s or a member’s (a reader gets 403). The bug is open, gets the next BUG-<n> of the project (taken under the project’s lock, so two at once get two numbers) and its author becomes a watcher. caseIds links live cases of the project in the same step; a case that is not one is a 404 and nothing is made. The Bugs section must be on in the project settings (409 bugs_disabled). fromResult makes the bug from a failed result: its case is linked too, the result’s snapshot is taken and its pictures named in fileIds are copied into the bug - see POST /v1/bugs/{id}/evidence. The ceiling is 5 000 live bugs a project, the same on every plan (429, as the other ceilings answer). Audited on the bug (bug/create, and with fromResult a bug/event with the names and sizes of the copies).

X-Plune-Project
string

Which of your projects to act in — an id from GET /v1/projects. Absent, your default project. A project you are not a member of answers 404, the same as one that does not exist. Ignored under a bearer token, which is bound to the project it was minted in.

Media type application/json
object
title
required

Trimmed.

string
>= 1 characters <= 200 characters
description

Markdown, at most 16384 bytes.

string
nullable
caseIds

Live cases of the same project to link at once; one that is not is a 404 and no bug is made.

Array<string>
<= 100 items
fromResult

A bug from a failed result (#1066): its case is linked, its snapshot taken and the pictures named copied - the first entry of evidence. The title and the text are the caller’s, as for any bug.

object
resultId
required

A failed or broken result of the project (422 result_not_failed for another status; 404 for one of another project, or in a run in the Bin).

string
fileIds

Pictures of that result to copy into the bug - at most 10 a call and 30 a bug; what is over, or does not fit, is named in notCopied and never refuses the call.

Array<string>
<= 30 items
Example
{
"title": "The cart loses an item after a coupon is applied",
"description": "Add two items, apply the coupon `SPRING`, remove one: **both** are gone.",
"caseIds": [
"6f1c2b3a-0000-4000-8000-0000000000c1"
]
}

Filed

Media type application/json
object
id
required
string format: uuid
key
required

The bug’s name for people (BUG-12): the project’s own number, handed out in order, never reused and never changed.

string
/^BUG-[0-9]+$/
number
required
integer
>= 1
source
required

Whose truth the row is. plune is a bug of the project’s own; jira (a mirror of a Jira ticket, #1069) is not written by this version.

string
Allowed values: plune jira
title
required
string
>= 1 characters <= 200 characters
state
required
string
Allowed values: open closed
createdBy
required

The author’s user id, to be named from GET /v1/members; null when the account is gone.

string
nullable
caseCount
required

The live cases linked to the bug.

integer
commentCount
required
integer
createdAt
required
string format: date-time
updatedAt
required
string format: date-time
streak
required

«Passed X of N» (#1067, ADR 0057 §4). A linked case counts its green runs in a row while the bug is open: passed adds one, failed or broken resets, blocked, skipped and a muted result neither add nor reset; a run is one verdict, as the run itself counts the case - its latest result in each combination (a retry that passed is a pass), then the worst of the combinations; green counts once every combination of the run has a result, red at once (a failure in any combination); only a run newer than the one last counted and made after the case was linked counts, and with an environment in the rule only a run on it.

object
passed
required

The smallest streak among the live linked cases; 0 with none.

integer
n
required

N of the project rule (bugs.close.n of the settings).

integer
>= 1 <= 5
canBeClosed
required

The bug is open, has a live case, and every live case has N in a row - and nobody put it off for this series (POST /v1/bugs/{id}/wait, #2163). Under the rule after_n the bug then closes itself, unless flaky.

boolean
flaky
required

A live linked case is flaky: the streak is weak proof, and the rule after_n only tells, it does not close.

boolean
autoClosed
required

Closed by its streak (the rule after_n), not by a person (#1067). A person’s close or reopen sets it back to false.

boolean
reopenedByRegression
required

Open again because a linked case failed after its streak closed it - the list’s badge of a regression (#2163). A person’s move clears it.

boolean
projectId
required
string
description
required

Markdown, at most 16384 bytes; shown by the safe component. null - none.

string
nullable
stateReason
required

Why the last move of state was made: manual, the words the person gave, or Plune’s own (#1067): automatically after N green runs in a row, regression after auto-close. null - never moved.

string
nullable
closedAt
required
string format: date-time
nullable
closedBy
required
string
nullable
cases
required

The live cases linked to the bug, by title.

Array<object>
object
id
required
string format: uuid
title
required
string
greenStreak
required

Green runs of the case in a row, counted while the bug is open; from 0 again after a reopen (#1067).

integer
isFlaky
required
boolean
watchers
required

User ids of the people who are told about the bug: the author, and whoever pressed «watch».

Array<string>
watching
required

Whether the caller is one of them. The one field of the card that depends on who asks.

boolean
evidence
required

The failed results the bug was made from or added to, oldest first (#1066, ADR 0057 §6).

Array<object>
object
id
required
string format: uuid
resultId
required

The result, while it is there and its run is out of the Bin - a link that opens; null otherwise. The snapshot keeps the id.

string
nullable
runId
required

Its run, on the same terms.

string
nullable
resultState
required

present; deleted_by_cleanup - the plan’s cleanup of old history removed it (#1049); deleted - any other road (the Bin, a deleted case). Kept when the result goes: it cannot be read back after.

string
Allowed values: present deleted_by_cleanup deleted
snapshot
required

The result as it was when the evidence was taken, cleaned of credentials as every reader cleans it: result (status, raw status, source, sub-status, outcome, expected status, muted, case version, duration, params, when), errorContext (at most 16384 bytes; a longer one is cut and ends with …[truncated N bytes], and errorContextTruncated is true), failure (headline, steps, place, CI link - not the artifacts), steps (a person’s marks), case (id, title, keys, flaky and muted then), run (id, title, environment, started), takenBy, takenAt.

object
notCopied
required

The pictures named that were not copied, and why. The evidence is written all the same.

Array<object>
object
fileId
required
string
name
required

null when the id named no picture of the result.

string
nullable
reason
required

limit - over 10 a call or 30 a bug; budget - the bug’s copies in Plune’s storage would take more than half of the file budget; storage - the project’s bucket did not make the copy; missing - not a picture of that result (gone, a text, or never one).

string
Allowed values: limit budget storage missing
createdBy
required
string
nullable
createdAt
required
string format: date-time
files
required

The bug’s copies of the pictures of its evidence, oldest first. Read by GET /v1/files/{id} (one in the project’s bucket by GET /v1/files/{id}/link), by anyone who reads the bug; never deleted alone (DELETE /v1/files/{id} is a 409 evidence_file), they go with the bug.

Array<object>
object
id
required
string format: uuid
name
required
string
contentType
required
string
size
required
integer
storage
required

db: the bytes are GET /v1/files/{id}; bucket: a new object in the project’s bucket, under a key of the bug’s.

string
Allowed values: db bucket
createdAt
required
string format: date-time
streakMove
required

What the streak last did, while it is the last move of the state; null after a person’s move, or when the streak never moved it (#2163).

object
kind
required
string
Allowed values: auto_closed regression
at
required
string format: date-time
run
required

The run of the move; null once it is in the Bin or gone.

object
id
required
string
title
required
string
nullable
environment
required

The environment of that run, as it was then.

string
nullable
case
required

The case that failed - a regression’s; null on a close, or once the case is in the Bin or gone.

object
id
required
string format: uuid
title
required
string
Example
{
"id": "6f1c2b3a-0000-4000-8000-0000000000f6",
"key": "BUG-12",
"number": 12,
"source": "plune",
"title": "The cart loses an item after a coupon is applied",
"state": "open",
"createdBy": "6f1c2b3a-0000-4000-8000-0000000000a2",
"caseCount": 1,
"commentCount": 2,
"createdAt": "2026-09-16T09:12:04.151Z",
"updatedAt": "2026-09-16T09:12:04.151Z",
"streak": {
"passed": 2,
"n": 3,
"canBeClosed": false,
"flaky": false
},
"autoClosed": false,
"reopenedByRegression": false,
"projectId": "u-shop::default",
"description": "Add two items, apply the coupon `SPRING`, remove one: **both** are gone.",
"stateReason": null,
"closedAt": null,
"closedBy": null,
"cases": [
{
"id": "6f1c2b3a-0000-4000-8000-0000000000c1",
"title": "Cart keeps its items",
"greenStreak": 2,
"isFlaky": false
}
],
"watchers": [
"6f1c2b3a-0000-4000-8000-0000000000a2"
],
"watching": true,
"evidence": [
{
"id": "7b3e1d2c-0000-4000-8000-00000000e066",
"resultId": "6f1c2b3a-0000-4000-8000-0000000000e2",
"runId": "6f1c2b3a-0000-4000-8000-0000000000d1",
"resultState": "present",
"snapshot": {
"result": {
"id": "6f1c2b3a-0000-4000-8000-0000000000e2",
"status": "failed",
"rawStatus": "failed",
"source": "playwright",
"subStatus": null,
"outcome": "unexpected",
"expectedStatus": "passed",
"muted": false,
"caseVersion": 3,
"durationMs": 4120,
"params": null,
"createdAt": "2026-09-16T09:12:04.151Z"
},
"errorContext": "Error: expect(received).toHaveCount(expected)\nExpected: 2\nReceived: 0",
"errorContextTruncated": false,
"failure": {
"headline": "Expected 2 items in the cart, got 0",
"steps": [
"open the cart",
"apply SPRING",
"remove one"
],
"ciUrl": "https://ci.example.com/runs/77"
},
"steps": null,
"case": {
"id": "6f1c2b3a-0000-4000-8000-0000000000c1",
"title": "Cart keeps its items",
"keys": [],
"isFlaky": false,
"isMuted": false
},
"run": {
"id": "6f1c2b3a-0000-4000-8000-0000000000d1",
"title": "Nightly",
"environment": "staging",
"startedAt": "2026-09-16T09:12:04.151Z"
},
"takenBy": "6f1c2b3a-0000-4000-8000-0000000000a2",
"takenAt": "2026-09-16T09:12:04.151Z"
},
"notCopied": [
{
"fileId": "7b3e1d2c-0000-4000-8000-00000000e067",
"name": "after-coupon.png",
"reason": "budget"
}
],
"createdBy": "6f1c2b3a-0000-4000-8000-0000000000a2",
"createdAt": "2026-09-16T09:12:04.151Z"
}
],
"files": [
{
"id": "7b3e1d2c-0000-4000-8000-00000000e068",
"name": "cart.png",
"contentType": "image/png",
"size": 48213,
"storage": "db",
"createdAt": "2026-09-16T09:12:04.151Z"
}
],
"streakMove": null
}

Invalid body

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

No / invalid token or session

You are in the project as a reader

A case in caseIds is not a live case of the project, or fromResult names no result of it

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

The Bugs section is off in this project (code: bugs_disabled)

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

The result of fromResult did not fail (code: result_not_failed)

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

Rate-limited: the per-route throttle, or the per-minute request limit of the plan an API token counts by (#1442; the body names the plan, #2092). Retry-After carries the seconds until the window rolls over.

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

Seconds until the window rolls over