> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aegister.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Guida: l'API V-CISO spec-driven

> Compila tabelle, rispondi a domande e completa i controlli del framework in modo programmatico con la stessa validazione spec-driven usata dalla web app: discover, spec, dry_run, submit.

Gestisci i workflow V-CISO interamente dall'API. Tabelle (inventari, registri),
attività a domande e controlli del framework condividono tutti lo stesso schema:
**individua, leggi la specifica, valida con `dry_run`, quindi invia**. Il server valida
esattamente come un salvataggio nella web app, poi calcola il punteggio e persiste una nuova
valutazione.

Tutte le richieste si autenticano con un header `X-Aegister-Token` (vedi
[Autenticazione](/api-reference/authentication) e [API token](/api-reference/token)).
L'organizzazione deve possedere la licenza `v_ciso`. La lettura richiede il ruolo **view**;
la scrittura richiede il ruolo **edit** (`security_officer`, `owner` o `super_admin`).

<Note>
  I percorsi qui sotto sono abbreviati come `.../workflows/{wid}/...`. La base completa è
  `https://app.aegister.com/api/v1/v-ciso/organizations/{oid}`. Tutte le risposte usano
  l'envelope standard `{ "error": 0|1, "messages": [], "data": {…} }` (più `"total"` negli elenchi).
</Note>

## Prima di iniziare

Questa guida parte da un'**organizzazione esistente che possiede già la licenza `v_ciso`** e ha
il framework che vuoi compilare **associato e configurato**. Ti servono solo il suo id
organizzazione (`{oid}`) e un token con il ruolo **edit** (`security_officer`, `owner` o `super_admin`).

<Warning>
  Per **NIS 2**, il profilo dell'organizzazione deve dichiarare il suo **tipo di soggetto**
  (Essenziale o Importante). Finché non lo fa, il framework restituisce **nessun controllo e nessuna
  attività**, e le chiamate qui sotto tornano vuote. Impostalo prima nel profilo NIS 2
  dell'organizzazione.
</Warning>

## 1. Individua cosa compilare

Elenca ogni attività (tabella **e** a domande) dell'organizzazione, ciascuna con il suo `type`,
così che uno script possa localizzare su cosa lavorare. Filtra con `?type=table` o `?type=questions`.

```bash theme={null}
curl -H "X-Aegister-Token: $TOKEN" \
  "https://app.aegister.com/api/v1/v-ciso/organizations/123/tasks"
```

```json theme={null}
{ "error": 0, "messages": [], "total": 2,
  "data": [
    { "workflow_id": "oJJ…", "task_id": "task-nis-inv-hardware",
      "title": { "en": "Hardware inventory", "it": "Inventario hardware" },
      "type": "table", "required": true, "status": "Not started" },
    { "workflow_id": "oJJ…", "task_id": "task-nisAnnual",
      "title": { "en": "Annual review", "it": "Revisione annuale" },
      "type": "questions", "required": true, "status": "In progress" } ] }
```

Usa il `workflow_id` + `task_id` restituiti per le chiamate per-attività. Il più datato
`GET .../table-tasks` (*List table tasks*) restituisce solo le attività tabella; `/tasks` le copre entrambe.

## 2. Attività tabella (righe)

### Leggi la specifica (il contratto)

```bash theme={null}
curl -H "X-Aegister-Token: $TOKEN" \
  ".../workflows/oJJ…/tasks/task-nis-inv-hardware/spec?lan=en"
```

Ogni colonna dichiara `type`, `required`, un **`format`** dichiarativo (per esempio
`{ "kind": "date", "pattern": "YYYY-MM-DD" }`) e i valori ammessi in **`choices`** (con sia
l'id `value` sia il `label` localizzato). Le colonne dipendenti riportano `depends_on` +
`choices_by_parent`; le colonne cross-attività riportano `source`. Invia il **label** della scelta
oppure il suo **value id**; le colonne a valori multipli accettano un array o una stringa unita da `|`.

### Valida senza scrivere (dry\_run)

```bash theme={null}
curl -H "X-Aegister-Token: $TOKEN" -H "Content-Type: application/json" \
  ".../workflows/oJJ…/tasks/task-nis-inv-hardware/import?lan=en" \
  -d '{ "dry_run": true, "rows": [
        { "asset_id": "HW-001", "device_input": "Physical servers",
          "criticality": "High", "status": "Updated" } ] }'
```

```json theme={null}
{ "error": 0, "messages": [], "data": {
    "valid_count": 1, "error_count": 0,
    "columns": [ /* the normalized column set */ ],
    "rows": [ /* normalized, ready to submit */ ],
    "errors": [ /* {row, column, message} per problem */ ] } }
```

Correggi eventuali `errors` (scelta errata, data/numero non validi, un valore figlio non
ammesso per il suo genitore, un valore cross-attività sconosciuto) e ripeti finché `error_count` è 0.

### Invia

Rimuovi `dry_run` per persistere. Il server valida di nuovo, invia al motore di calcolo del
punteggio e registra una nuova valutazione, esattamente come un salvataggio nella web app.

```json theme={null}
{ "error": 0, "messages": [], "data": { "imported_rows": 1, "evaluation_id": 3780 } }
```

Una richiesta con un qualsiasi errore di validazione restituisce `400` con `error: 1` e la lista
`data.errors`; non viene scritto nulla.

<Tip>
  `GET .../template` restituisce un XLSX arricchito (menu a tendina + menu a tendina dipendenti +
  legenda value/label) e `GET .../export` lo restituisce precompilato con i dati correnti: comodo per le
  persone, mentre gli script usano il contratto JSON descritto sopra.
</Tip>

## 3. Attività a domande (risposte)

Le attività a domande usano gli **stessi** endpoint `/spec` e `/import`; la chiave del payload è
`answers` invece di `rows`. Leggi la specifica **e le risposte correnti** in un'unica chiamata per il
prefill, quindi invia.

```bash theme={null}
curl -H "X-Aegister-Token: $TOKEN" \
  ".../workflows/oJJ…/tasks/task-nisAnnual/answers?lan=en"
```

Restituisce la specifica delle domande insieme alle ultime risposte memorizzate, indicizzate per
id della domanda. Le risposte a scelta accettano il **label** o il **value id**; le risposte a valori
multipli vanno come array o come stringa unita da virgola/pipe.

```bash theme={null}
curl -H "X-Aegister-Token: $TOKEN" -H "Content-Type: application/json" \
  ".../workflows/oJJ…/tasks/task-nisAnnual/import?lan=en" \
  -d '{ "dry_run": true, "answers": {
        "task-nisAnnual-q1": "Yes",
        "task-nisAnnual-q2": ["Email", "Phone"] } }'
```

Per allegare un'**evidenza** o una **giustificazione**, invia una risposta composita
`{ "value", "evidence": [<path>], "note": [{ "id", "response" }] }`. Carica prima il file
(vedi [sezione 5](#5-evidenze-il-caricamento-in-due-passi)) e usa il `path` restituito:

```json theme={null}
{ "answers": {
    "task-nisAnnual-q1": {
      "value": "Yes",
      "evidence": ["media/docs/frameworks/65/oJJ…/task-nisAnnual-q1/tok_x.pdf"],
      "note": [{ "id": "note-1", "response": "Notified ACN on 2026-01-10." }] } } }
```

Rimuovi `dry_run` per persistere; la risposta riporta il nuovo `evaluation_id`, come per le tabelle.

## 4. Controlli del framework

Il framework di un workflow (l'insieme di controlli dietro il punteggio) ha il proprio
trio spec/answers/submit, raggruppato in **step**. Lavora uno step alla volta con `?step=<stepId>`.

```bash theme={null}
# Contract: control id, type, choices, weight, step
curl -H "X-Aegister-Token: $TOKEN" \
  ".../workflows/oJJ…/framework/spec?lan=en&step=E"

# Spec + current answers keyed by control id
curl -H "X-Aegister-Token: $TOKEN" \
  ".../workflows/oJJ…/framework/answers?lan=en&step=E"
```

Ogni controllo riporta `id`, `label`, `description`, `type`
(`radio|select|checkbox|date|number|text|…`), `choices`, `required`, un flag `evidence`
(se è possibile allegare file), `step`, `subframework`, `category` e `weight`.

Invia le risposte di uno step con `controls-import`: prima `dry_run`, poi persisti.

```bash theme={null}
curl -H "X-Aegister-Token: $TOKEN" -H "Content-Type: application/json" \
  ".../workflows/oJJ…/framework/controls-import?step=E&lan=en" \
  -d '{ "dry_run": true, "answers": { "nis2-req-E - 2.1.1": "Implemented" } }'
```

```json theme={null}
{ "error": 0, "messages": [], "data": {
    "valid_count": 1, "error_count": 0,
    "answers": { /* normalized */ }, "errors": [] } }
```

Allega l'evidenza nello stesso modo delle attività a domande, con un valore composito:

```json theme={null}
{ "answers": {
    "nis2-req-E - 2.1.1": {
      "value": "Implemented",
      "evidence": ["media/docs/frameworks/65/oJJ…/nis2-req-E - 2.1.1/tok_x.pdf"] } } }
```

Rimuovendo `dry_run` valida e poi invia tramite `/evaluate` (Aegister calcola il punteggio e persiste),
restituendo `{ "submitted": true, "evaluation_id": … }`.

## 5. Evidenze: il caricamento in due passi

Carica le evidenze separatamente, non inline. Invia il file al suo artefatto, quindi riferisci il
`path` restituito nella risposta.

**Passo 1, carica** il file come JSON base64 verso l'artefatto (un id di controllo o di domanda):

```bash theme={null}
curl -H "X-Aegister-Token: $TOKEN" -H "Content-Type: application/json" \
  ".../workflows/oJJ…/documents/nis2-req-E%20-%202.1.1/versions" \
  -d '{ "filename": "policy.pdf",
        "content": "JVBERi0xLjQK…",
        "content_type": "application/pdf",
        "source": "evidence_upload" }'
```

```json theme={null}
{ "error": 0, "messages": [], "data": {
    "id": 91, "artifact_id": "nis2-req-E - 2.1.1", "original_name": "policy.pdf",
    "size": 51234, "content_type": "application/pdf",
    "path": "media/docs/frameworks/65/oJJ…/nis2-req-E - 2.1.1/tok_x.pdf" } }
```

Vincoli: **al massimo 10 MB** (decodificato, altrimenti `413`); i formati ammessi sono **PDF, Word
(.doc/.docx), Excel (.xls/.xlsx), immagini e video** (qualsiasi altro restituisce `400`). I caricamenti
vengono deduplicati per SHA-256 (un contenuto identico restituisce la versione esistente, `200`, invece di
una nuova `201`), e per ogni artefatto vengono conservate solo le ultime **10 versioni**.

**Passo 2, riferisci** il `path` restituito nell'array `evidence` della risposta che invii
(sezioni 3 e 4).

Per elencare le evidenze esistenti in un workflow (ogni artefatto con la sua ultima versione e
l'intera cronologia), chiama `GET .../workflows/{wid}/documents` (*List Documents* nella barra laterale).
La cronologia e i download per-artefatto usano gli altri endpoint *Documents* sotto
*V-CISO*.
