Make a requirement
const url = 'https://beta-api.plune.ai/v1/requirements';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"type":"user_story","status":"active","title":"A buyer pays by card","description":"As a buyer I pay for the order by card, so that I do not wait for an invoice.","priority":"Must-have","acceptanceCriteria":"- the card is charged once","category":"functional","key":"REQ-214"}'};
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/requirements \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "type": "user_story", "status": "active", "title": "A buyer pays by card", "description": "As a buyer I pay for the order by card, so that I do not wait for an invoice.", "priority": "Must-have", "acceptanceCriteria": "- the card is charged once", "category": "functional", "key": "REQ-214" }'An owner’s or a member’s (a reader gets 403). A new requirement has no cases; link them with PUT /v1/requirements/{id}/cases/{caseId}. Without a key it gets the next REQ-<n> of the project; with one, that key is kept (a clash in the project is 409; the same key in another project is fine). Built in: nothing here depends on an external tracker.
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”object
A free short label (Must-have, High), not an enum. Optional; null or absent - not set.
Markdown text. Optional; null or absent - not set.
Which kind of requirement it is. Optional; null or absent - not set.
Optional: 1 to 40 letters, digits, -, _, .; not only digits and not an id. Absent: the server assigns the next REQ-<n> of the project. An import’s external id (REQ-FN-001) goes here; a key the project already has is a 409.
Example
{ "type": "user_story", "status": "active", "title": "A buyer pays by card", "description": "As a buyer I pay for the order by card, so that I do not wait for an invoice.", "priority": "Must-have", "acceptanceCriteria": "- the card is charged once", "category": "functional", "key": "REQ-214"}Responses
Section titled “ Responses ”Created
object
The requirement’s short name (REQ-214), unique in the project, case-insensitive. Generated (REQ-<n>, per project, a number is never reused) or supplied. Never only digits, never an id.
Where the requirement stands. Archiving is separate (archivedAt) and keeps the status.
Plain text, at most 16384 bytes.
A free short label (Must-have, High), not an enum. null - not set.
Markdown text. null - not set.
Which kind of requirement it is. null - not set.
How the linked cases stand, counted from their RESULTS, never from a flag: each live case once, under the status of its newest result in a run that is not in the Bin (newest by when the run was created) — the status the run counters count it under, so a muted case’s failure is muted. A case with no result is notRun; a case in the Bin is not counted. total is the sum of the rest; «N of M cases passed» is passed of total.
object
M — the live cases linked to the requirement.
N — the cases whose newest result is passed.
Muted cases whose newest result failed or broke.
Cases with no result in a run that is not in the Bin.
Of total, the cases of kind automated — the numerator of «9 of 14 cases are automated»; the denominator is total.
Only in the list asked with ?cases=true; always in the one requirement’s page.
object
The status the coverage counted the case under; null — no result yet.
The run that result is in; null — no result yet.
Set while the requirement is archived: out of the default list and out of every coverage filter; its links stay.
Example
{ "id": "6f1c2b3a-0000-4000-8000-0000000000f5", "key": "REQ-214", "projectId": "u-shop::default", "type": "user_story", "status": "active", "title": "A buyer pays by card", "description": "As a buyer I pay for the order by card, so that I do not wait for an invoice.", "priority": "Must-have", "acceptanceCriteria": "- the card is charged once\n- a declined card shows the bank reason", "category": "functional", "coverage": { "total": 0, "passed": 0, "failed": 0, "broken": 0, "blocked": 0, "skipped": 0, "muted": 0, "notRun": 0, "automated": 0 }, "archivedAt": null, "createdAt": "2026-09-16T09:12:04.151Z", "updatedAt": "2026-09-16T09:12:04.151Z"}Invalid body
object
Example generated
{ "error": "example"}No / invalid token or session
You are in the project as a reader
The key is already used by a requirement of this project
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