Import requirements from a Markdown file (a dry run by default)
const url = 'https://beta-api.plune.ai/v1/projects/example/requirements/import';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"markdown":"# Billing SRS\n\n## 3. Functional requirements\n\n| ID | Requirement | Priority |\n|----|-------------|----------|\n| REQ-FN-001 | The system **shall** let a user sign in with an e-mail link. | Critical |\n| REQ-FN-002 | The system shall export an invoice as a PDF file. | High |\n","mode":"table","table":0,"keyColumn":"ID","textColumn":"Requirement","priorityColumn":"Priority","priorityMap":{"Critical":"Must-have","High":"Should-have"}}'};
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/projects/example/requirements/import \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "markdown": "# Billing SRS\n\n## 3. Functional requirements\n\n| ID | Requirement | Priority |\n|----|-------------|----------|\n| REQ-FN-001 | The system **shall** let a user sign in with an e-mail link. | Critical |\n| REQ-FN-002 | The system shall export an invoice as a PDF file. | High |\n", "mode": "table", "table": 0, "keyColumn": "ID", "textColumn": "Requirement", "priorityColumn": "Priority", "priorityMap": { "Critical": "Must-have", "High": "Should-have" } }'Reads the table (mode: table) or the section (mode: sections) the person picked after .../import/discover, with the columns they mapped, and answers what it does with each row: create, update, unchanged or skip, each with a reason and the line in the file. dryRun is true unless the body says false: a dry run writes nothing. dryRun: false applies the same plan in ONE transaction (a failure leaves nothing behind) and answers the same report with applied: true and the id of each requirement written. A row with a key matches an existing requirement by that key (any case); a row without one matches by exact title (the reason says matched by title) and a new one gets REQ-<n>. A re-import of the same file changes nothing: every row is unchanged. A field is updated only when the file gives a value and it differs; an empty cell leaves the requirement as it is. Tables and sections not picked are listed in notImported, never dropped silently. Checklist marks ([x]) are never read: coverage is counted from results. A bad row (ragged, a key that is invalid or repeated in the file, a status that is not draft, active or done, a text over the description limit) is skipped with its line; the rest goes on. Refusals: more than 2000 requirements or a file over 1048576 bytes is a 413 naming the limit; an unknown column or table is a 400 naming the headers or the count. caseTables (#1624) chooses Given / When / Then tables as manual test cases: unticked by default, so a table not listed there creates nothing and stays in notImported; the cases are written in the same transaction as the requirements, each with its own audit entry, and a project over its case ceiling is a 429 with nothing written. describe (#1856) appends a left-out section or table to the description of a requirement of the same import, under its own heading; the file is not changed. Every entry of notImported carries its lines and nearestAbove, the closest requirement above it, which a client offers as the default. An applied import is ONE audit entry with the counts, never the text. An owner or a member (a reader gets 403); a bearer token only in the project it was minted in.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”An id from GET /v1/projects
Request Body required
Section titled “Request Body required ”The file and the mapping. table mode needs table (or tables, several tables in one run) and a titleColumn or a textColumn; sections mode needs section. A field of the other mode is a 400. Columns are named by their header text (any case, emphasis ignored); an unknown name is a 400 listing the headers. keyPattern is not accepted: no pattern is run on a file.
object
At most 1048576 bytes (1 MiB).
Default true: nothing is written and the report says what would be. false applies everything in one transaction.
The type of a requirement the file does not type (default feature).
Value to value, applied exactly (any case). A priority not in the map is kept as written and listed in values.priority.
object
Table mode: the index of the table from the discover call. For several tables in one run give tables instead.
Table mode, several tables in one run (one preview, the table named on each row): each entry has table and the column fields of the single-table form (keyColumn, titleColumn or textColumn, descriptionColumn, priorityColumn, categoryColumn, typeColumn, statusColumn, acceptanceCriteriaColumn, categoryMap) and fixedCategory / fixedType. Instead of table and those fields at the top level, never together. priorityMap and defaultType stay shared. The limits (1 MiB, 2000 rows) apply to all the tables together, and a key must be unique across them.
object
object
Given / When / Then tables to create MANUAL TEST CASES from (#1624), table or sections mode. Without this nothing is created: a table whose headers include Given, When and Then is only proposed in the report (cases, selected: false) and stays in notImported.tables. Each entry: table (the index from the discover call) and optional idColumn (the scenario id, AC-001), textColumn (default: the When cell), givenColumn / whenColumn / thenColumn (default headers Given, When, Then). A table is either requirements or cases, not both. In table mode table / tables may be left out when only cases are wanted. The rows count against the 2000 of the whole file.
object
Table mode: one category for the whole table, used where a row has no category of its own (a column wins).
Table mode: one type for the whole table, used where a row has no type of its own (a column wins).
Table mode: the column with the external id (REQ-FN-001). It becomes the key, so a second import updates. Without it a row matches by exact title.
Table mode: the title. Not together with textColumn.
Table mode: the text, used as the title (first line, cut at 200 characters at a word boundary) with the whole text as the description when they differ.
Table mode: the description (Markdown kept; <br> becomes a line break, an escaped pipe a pipe).
Table mode: functional, non-functional or other, or a value categoryMap sends to one of them; any other value is left empty and reported.
Table mode: user_story, feature or epic.
Table mode: draft, active or done; any other value skips the row. Without it an imported requirement is draft and an existing one keeps its status.
Table mode: only a column the person names; an “Acceptance signal” or “Observable result” column is never picked by its header.
Table mode: value to one of the three categories.
object
Sections mode: one requirement per list item or per sub-heading (its body is the description). Default: sub-headings when the section has any, else list items.
Sections mode: a list item or sub-heading that starts with this text (any case) carries its key: REQ-12: The system shall....
Sections mode: list items under a sub-heading take its text as their priority.
Both modes (#1856): left-out content as part of a requirement’s description. Each entry: exactly one of section / table (its index from the discover call; it must be listed in notImported of this request) and into (the line of a requirement of this import, as in items). The description is the one the file gives (or none), then for each source in file order ## <heading> and its lines as written: a section its own lines up to the next heading of any level, without its heading line, a table whole (a table without a heading is Table <index + 1>). Front matter is never a source. A description over 16 384 bytes skips the whole requirement with the description would pass 16 KiB with <heading>. An unchanged requirement becomes an update of its description; the same describe again is unchanged. 400: a source that is imported (section 3 is imported, not left out), a heading that holds nothing of its own (section 0 holds nothing of its own) or not in the file, a line that is not a requirement of this import (or one skipped for its own reason), an entry with both or neither of section / table, the same source twice.
object
Examples
A table with an id column, as a dry run
{ "markdown": "# Billing SRS\n\n## 3. Functional requirements\n\n| ID | Requirement | Priority |\n|----|-------------|----------|\n| REQ-FN-001 | The system **shall** let a user sign in with an e-mail link. | Critical |\n| REQ-FN-002 | The system shall export an invoice as a PDF file. | High |\n", "mode": "table", "table": 0, "keyColumn": "ID", "textColumn": "Requirement", "priorityColumn": "Priority", "priorityMap": { "Critical": "Must-have", "High": "Should-have" }}Bullets under a heading, as a dry run
{ "markdown": "---\ntitle: Invoice reminders\n---\n\n### Requirements\n\n- The system sends a reminder three days before the due date.\n- A customer can switch the reminders off.\n\n### Risks\n\n- Mails land in spam\n", "mode": "sections", "section": "Requirements", "unit": "bullets"}The same table, written
{ "markdown": "# Billing SRS\n\n## 3. Functional requirements\n\n| ID | Requirement | Priority |\n|----|-------------|----------|\n| REQ-FN-001 | The system **shall** let a user sign in with an e-mail link. | Critical |\n| REQ-FN-002 | The system shall export an invoice as a PDF file. | High |\n", "mode": "table", "table": 0, "keyColumn": "ID", "textColumn": "Requirement", "priorityColumn": "Priority", "priorityMap": { "Critical": "Must-have", "High": "Should-have" }, "dryRun": false}A left-out section as the description of REQ-FN-002, as a dry run
{ "markdown": "# Billing SRS\n\n## 3. Functional requirements\n\n| ID | Requirement | Priority |\n|----|-------------|----------|\n| REQ-FN-001 | The system **shall** let a user sign in with an e-mail link. | Critical |\n| REQ-FN-002 | The system shall export an invoice as a PDF file. | High |\n\n## Notes\n\nThe PDF carries the logo of the shop.\n", "mode": "table", "table": 0, "keyColumn": "ID", "textColumn": "Requirement", "priorityColumn": "Priority", "priorityMap": { "Critical": "Must-have", "High": "Should-have" }, "describe": [ { "section": 2, "into": 8 } ]}Responses
Section titled “ Responses ”The report - the same for a dry run and for an applied import
object
true once written; a dry run is false.
object
One entry per row, list item or sub-heading read, in file order.
object
1-based line in the file.
What the import does (a dry run) or did (applied).
The key in the file, or the key of the requirement a title matched; null for a new requirement without one (it gets REQ-<n>).
new, matched by key, matched by title, matched by key: unchanged, or why the row is skipped (names the problem).
On an update: the fields that differ (names, never values).
Once applied: the id of the requirement created or updated.
Things worth a look that did not stop the row (an unknown category or type).
Only when tables was sent: the index of the table the row is from.
Only when tables was sent: the heading above that table.
Only on an item describe named (#1856): the left-out parts appended to its description, in file order; also on a skip over the 16 KiB limit, to show what did not fit.
object
The heading of the section or the table; null for a table without one.
Lines of the file, 1-based, both ends included. A table: its header row to its last row. A section: its heading to the line before the next heading of any level (or the end of the file), its own lines only (#2158).
object
Manual test cases proposed from Given / When / Then tables (#1624); present only when the file has such a table or the request chose one. Title: <id>: <text> (text = textColumn, else the When cell; without an id the text alone), first line, at most 200 characters. The Given cell is the precondition; one step: the When cell as the action, the Then cell as the expected result. Created as drafts, in no suite. A re-import leaves a case alone when the project already has a live one whose title starts with <id>: (or equals the title when the row has no id). create / unchanged of a row with selected: false is what ticking the table would do; only selected: true rows are written.
object
true when the request listed the table in caseTables.
The id column value, or null.
new, matched by id: left as it is, matched by title: left as it is, template placeholder, or why the row is skipped.
Once applied: the id of the case created.
Counts of the SELECTED case rows only.
object
More unselected proposals were found than the 2000 listed.
Present when every row read is a template placeholder ([Behavior]): the file is a blank template. Such rows are skipped with the reason template placeholder.
Present with emptyTemplate: this file is an empty template: ....
The distinct values read from the priority and category columns, with what the map says of each: show them for editing.
object
object
What the map sends it to; null - no entry (a priority is then kept as written).
object
What the map sends it to; null - no entry (a priority is then kept as written).
What the import did not read, reported and never silently dropped (at most 200 tables and 200 sections are listed).
object
object
Lines of the file, 1-based, both ends included. A table: its header row to its last row. A section: its heading to the line before the next heading of any level (or the end of the file), its own lines only (#2158).
object
object
object
Lines of the file, 1-based, both ends included. A table: its header row to its last row. A section: its heading to the line before the next heading of any level (or the end of the file), its own lines only (#2158).
object
object
Example
{ "dryRun": true, "applied": false, "mode": "table", "summary": { "create": 1, "update": 0, "unchanged": 0, "skip": 1 }, "items": [ { "line": 7, "action": "create", "key": "REQ-FN-001", "title": "The system **shall** let a user sign in with an e-mail link.", "reason": "new" }, { "line": 8, "action": "skip", "key": "REQ-FN-002", "title": "The system shall export an invoice as a PDF file.", "reason": "status 'maybe' is not one of draft, active, done" } ], "values": { "priority": [ { "value": "Critical", "count": 1, "mappedTo": "Must-have" }, { "value": "High", "count": 1, "mappedTo": "Should-have" } ], "category": [] }, "notImported": { "frontmatterKeys": [], "tables": [], "sections": [ { "index": 1, "heading": "3. Functional requirements", "level": 2, "bulletCount": 0, "lines": { "from": 3, "to": 8 }, "nearestAbove": null } ] }}Invalid JSON, mapping or keyPattern; an unknown column (the message lists the headers), table or section; a describe source that is not left out, or a line that is not a requirement of this import
object
Example generated
{ "error": "example"}No / invalid token or session
You are in the project as a reader
No such project among yours (or a token of another project)
object
Example generated
{ "error": "example"}A key was taken by another change while the import ran; nothing was written
object
Example generated
{ "error": "example"}The markdown is over 1 MiB, or the table or section has more than 2000 requirements
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