Skip to main content
Drive V-CISO workflows entirely from the API. Tables (inventories, registries), question tasks, and framework controls all share one pattern: discover, read the spec, validate with dry_run, then submit. The server validates exactly like a save in the web app, then scores and persists a new evaluation. All requests authenticate with an X-Aegister-Token header (see Authentication and API token). The organization must hold the v_ciso license. Reading needs the view role; writing needs the edit role (security_officer, owner, or super_admin).
Paths below are abbreviated as .../workflows/{wid}/.... The full base is https://app.aegister.com/api/v1/v-ciso/organizations/{oid}. All responses use the house envelope { "error": 0|1, "messages": [], "data": {…} } (plus "total" on lists).

Before you start

This guide starts from an existing organization that already holds the v_ciso license and has the framework you want to fill associated and configured. You only need its organization id ({oid}) and a token with the edit role (security_officer, owner, or super_admin).
For NIS 2, the organization profile must declare its entity type (Essential or Important). Until it does, the framework returns no controls and no tasks, and the calls below come back empty. Set it in the organization’s NIS 2 profile first.

1. Discover what to fill

List every task (table and question) in the org, each with its type, so a script can locate what to work on. Filter with ?type=table or ?type=questions.
Use the returned workflow_id + task_id for the per-task calls. The older GET .../table-tasks (List table tasks) returns only table tasks; /tasks covers both.

2. Table tasks (rows)

Read the spec (the contract)

Each column declares type, required, a declarative format (for example { "kind": "date", "pattern": "YYYY-MM-DD" }), and allowed choices (with both the value id and localized label). Dependent columns carry depends_on + choices_by_parent; cross-task columns carry source. Send either the choice label or its value id; multi-value columns accept an array or a |-joined string.

Validate without writing (dry_run)

Fix any errors (a column id that is not in the spec, wrong choice, bad date/number, a child value not allowed for its parent, an unknown cross-task value) and re-run until error_count is 0. Messages and column labels follow ?lan=en|it (English by default); a required value that is missing carries "code": "required", so a script can tell it apart without parsing the text. current lets you compare what you send with what the table holds before you submit. It holds the cells as the console shows them (localized text, choices as labels), so don’t send it back as rows: build your rows from the spec. The JSON body must carry a rows array: without it the request fails with 400 and Missing 'rows' array. dry_run counts only when it is true, 1 or yes (in the body or as a query parameter); any other value means submit.

Replace the table, or keep the rows you don’t send

By default the rows you send replace the whole table. Add "keep_rows": true (or ?keep_rows=1, or a keep_rows form field with an XLSX upload) to update it instead: a row whose first column matches a row already in the table replaces that row, new rows go at the end, and the rows you did not send stay. It is read like dry_run (true, 1 or yes).
The dry run then also returns kept_count, the number of current rows that stay.

Submit

Drop dry_run to persist. The server validates again, submits to the scoring engine, and records a new evaluation, exactly like a save in the web app.
With keep_rows, the response also carries kept_rows. A request with any validation error returns 400 with error: 1 and the data.errors list; nothing is written.
GET .../template returns an enriched XLSX (dropdowns + dependent dropdowns + value/label legend) and GET .../export returns it pre-filled with current data: handy for humans, while scripts use the JSON contract above.

3. Question tasks (answers)

Question tasks use the same /spec and /import endpoints; the payload key is answers instead of rows. Read the spec and current answers in one call to prefill, then submit.
It returns the question spec plus the latest stored answers, keyed by question id. Choice answers accept the label or value id; multi-value answers go as an array or a comma/pipe-joined string. An id that is not a question of the task is an error (Unknown id: not a question of this task or step (see /spec)).
To attach evidence or a justification, send a composite answer { "value", "evidence": [<path>], "note": [{ "id", "response" }] }. Upload the file first (see section 5) and use the returned path: it must be a file uploaded for the same organization, workflow and question, or the answer is rejected. Each note id must be one of the question’s notes in /spec; for a task question it is <question_id>_justification:
Drop dry_run to persist; the response carries the new evaluation_id, same as tables.

4. Framework controls

A workflow’s framework (the control set behind the score) has its own spec/answers/submit trio, grouped into steps. Work one step at a time with ?step=<stepId>.
Each control carries id, label, description, type (radio|select|checkbox|date|number|text|…), choices, required, an evidence flag (whether files may be attached), notes (the justification boxes, each with its id), step, subframework, category, and weight. A control id that is not in the step is an error. Submit a step’s answers with controls-import: dry_run first, then persist.
Attach evidence and a justification the same way as question tasks, with a composite value. The note id is the one listed in the control’s notes (for NIS 2 controls, for example 2.1.1_justification):
Always send the value together with the note. On ISO 27001, an answer that carries only a justification, with no value, erases the control’s level. Dropping dry_run validates then submits via /evaluate (Aegister scores and persists), returning { "submitted": <number of answers>, "step": "E", "evaluation_id": … }.

5. Evidence: the two-step upload

Upload evidence separately, not inline. Send the file to its artifact, then reference the returned path in the answer. Step 1, upload the file as base64 JSON to the artifact (a control id or question id):
Constraints: at most 10 MB (decoded, else 413); allowed formats are PDF, Word (.doc/.docx), Excel (.xls/.xlsx), images, and video (anything else returns 400). Uploads dedupe by SHA-256 (identical content returns the existing version, 200, instead of a new 201), and only the latest 10 versions per artifact are kept. Step 2, reference the returned path in the evidence array of the answer you submit (sections 3 and 4). To list existing evidence across a workflow (every artifact with its latest version and full history), call GET .../workflows/{wid}/documents (List documents in the sidebar). Per-artifact history and downloads use the other endpoints in the V-CISO → Cyber Posture → Documents group.

6. Fill a task from your documents (AI)

If the answers already live in your documents (a policy, an asset list, a register), let the AI read them and propose what to put in a table or question task. It is the same proposal as Fill from your documents in the console. Nothing is saved: you review the proposal and submit it through /import as in sections 2 and 3. Step 1, start the job with 1 to 5 documents in base64 (at most 10 MB each, 20 MB in all, else 413). You can start 20 jobs an hour; past that the call answers 429:
The AI reads PDF, Word (.docx), Excel (.xlsx, .xlsm), images (.png, .jpg, .gif, .webp) and text files (.txt, .md, .csv, .tsv, .json, .xml, .log). Any other file is listed under skipped with the reason. The call fails with 400 when no document can be read, and with 502 when the AI service cannot be reached. Step 2, poll the job until status is done:
While the AI reads you get { "status": "running" }. When the documents hold nothing for the task, the result carries a msg instead of a proposal. For a question task, kind is questions and the proposal is under answers, keyed by question id. ai explains the proposal: a summary, how many items were filled, for each answer the confidence and the document it came from, and the rows left out with the reason (for example a value missing from a linked list, which you add there first). Step 3, submit the proposal: send rows (tables) or answers (questions) to /import, dry_run first. Only the person who started the job can read it, and the documents are not kept.