Attach a file to a bug
const url = 'https://beta-api.plune.ai/v1/bugs/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/files?name=example';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'image/png'}, body: 'binary'};
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/bugs/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/files?name=example' \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: image/png' \ --data binaryA file onto a bug (#1066, ADR 0057: screenshots are attachments of the bug, not pictures in its text), an owner’s or a member’s. The body IS the file, as for a result’s (POST /v1/results/{id}/files): a picture (PNG, JPEG, WebP) at most 2 MiB, or a text (application/json, text/plain) at most 512 KiB as UTF-8, cleaned of credentials before it is kept. Up to 30 files, 20 pictures and 10 texts of them in the database. The project’s files share one budget of 1 GiB: an upload that would pass it first deletes the project’s oldest files kept in the database — a person’s as much as a reporter’s — and its answer says what went (evicted). The list of the same files says beforehand what one more would cost (budget.evicts). Another file — a video, an archive — needs the project’s own bucket: …/files/uploads, then PUT, then POST /v1/files/{id}/complete. A reader of the project cannot add one; someone else’s case, run or bug is a 404, the same as an invented one. 600 requests a minute a person. Like the bug’s copies, a file attached to a bug is never sent away by the budget, so the files of bugs together take at most 50% of it (413 bug_files_share). It goes when the bug goes, or by DELETE /v1/files/{id}. Audited on the bug: bug/event with the file’s name and size.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Header Parameters
Section titled “Header Parameters ”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.
Query Parameters
Section titled “Query Parameters ”What the file is called; only the last segment of a path is kept, cleaned of credentials.
Request Body required
Section titled “Request Body required ”Any JSON document, UTF-8.
object
Example
{ "currency": "EUR", "rate": 1.08}Text, UTF-8 — a log is one.
Example
POST /v1/cart/coupon 422 31msResponses
Section titled “ Responses ”Stored. evicted is there only when the budget sent files of results or cases away for it
A person’s file just stored, and — only when it sent anything away — what the budget evicted for it.
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).
The user who added it; null once that account is gone.
What the 1 GiB budget of the project sent away to make room (#1234): the project’s oldest files kept in the database, a person’s as much as a reporter’s.
object
Example
{ "id": "3f0c2a7e-1b5d-4c8e-9a61-2d7f4e8b0c15", "name": "price-list-eur.png", "contentType": "image/png", "size": 48213, "storage": "db", "createdAt": "2026-10-13T09:00:00.000Z", "createdBy": "6f1c2b3a-0000-4000-8000-0000000000a2"}No name (or one over 200 characters once cleaned), an empty file, or a text with nothing left once cleaned
object
Example generated
{ "error": "example"}No / invalid token or session
A reader of the project
object
Example generated
{ "error": "example"}No such bug in this project
object
Example generated
{ "error": "example"}It already has 20 pictures (or 10 texts, for a text)
object
Example generated
{ "error": "example"}A picture larger than 2 MiB, or a text larger than 512 KiB — as sent, or once cleaned; or the files of bugs would take more than 50% of the storage budget (code: bug_files_share)
object
Example generated
{ "error": "example"}A type that is not listed — it needs the project’s bucket; a text in a charset other than UTF-8; or bytes that are not UTF-8 text
object
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.
object
Example generated
{ "error": "example"}Headers
Section titled “Headers ”Seconds until the window rolls over