
# Scarica un export di audit

`GET /v1/audit-exports/{export_id}/download` · autorizzazione `audit-exports:read`

Fornisce un link monouso al pacchetto di un job di export completato. Le chiavi API (segrete o con restrizioni) e gli utenti della dashboard possono scaricare; per uno scaricamento dalla dashboard serve un accesso recente (step-up, non più vecchio di 5 minuti). Ogni chiamata scrive un record `audit_export.downloaded` nello stream `admin` prima che il link venga emesso.

## Richiesta

| Parametro | In | Obbligatorio | Descrizione |
|---|---|---|---|
| `export_id` | path | sì | ID del job di export, con prefisso `aex_`. |

Un job creato con `include_ip: true` richiede anche `audit-events:read_ip` sulla chiave che scarica.

## Risposta

La risposta dipende dall'header `Accept`. Entrambe le forme contengono `Cache-Control: no-store`, `Referrer-Policy: no-referrer` e `Vary: Accept`, e scrivono entrambe lo stesso record `audit_export.downloaded`.

- **`Accept` elenca `application/json`** (esplicitamente, con un `q` diverso da zero; `*/*` non conta): `200 OK` con un body JSON. Usa questa forma da un browser o da qualsiasi client che non può leggere un redirect cross-origin: leggi `url`, poi aprilo.

  | Campo | Tipo | Descrizione |
  |---|---|---|
  | `url` | string | Link relativo sullo stesso host, `/downloads/audit-exports/{environment_id}/{export_id}/{token}`. |
  | `expires_at` | string (date-time) | Quando il link smette di funzionare (15 minuti dopo l'emissione). |

- **Qualsiasi altro `Accept`**: `302 Found` con body vuoto. `Location` è lo stesso link relativo.

- Il link è valido per 15 minuti e funziona una sola volta: il token viene consumato alla prima richiesta, anche se il trasferimento viene interrotto. Chiama di nuovo questa operazione per ottenere un nuovo link.
- Il link non richiede credenziali; il token è l'unica credenziale. Mantieni `Location` (o `url`) segreto e non registrarlo nei log.
- Il file è cifrato a riposo con la chiave dell'ambiente, quindi non viene usato alcun URL presigned dello storage; KeenAuth lo decifra e lo trasmette.

Seguendo il link si ottiene `200 OK` con `Content-Type: application/x-tar`, `Content-Length` e `Content-Disposition: attachment; filename="audit-export-aex_….tar"`. Il pacchetto tar contiene:

| File | Contenuto |
|---|---|
| `records.ndjson.gz` | Ogni record della catena di ogni ora coperta, in ordine di sequenza, un oggetto JSON per riga: i record corrispondenti in forma completa, gli altri come segnaposto senza contenuto (`"omitted": true`) così la hash chain può essere ricostruita senza rivelarli. |
| `manifest.json` | Filtro, conteggi, hash del file e, per ogni ora coperta, il checkpoint firmato e la prova di inclusione; JSON canonico (RFC 8785). |
| `manifest.jws` | Firma ES256 su `manifest.json`; verificala con le chiavi pubbliche in `audit-jwks.json`. |

Se la connessione termina prima che arrivino tutti i `Content-Length` byte, il pacchetto è incompleto; scartalo e richiedi un nuovo link. Un link non valido, già usato o scaduto restituisce `404 not_found`.

## Errori

Ogni errore è un body `application/problem+json` con un `code` stabile.

| HTTP | `code` | Quando |
|---|---|---|
| 401 | `unauthenticated` | La chiave API manca, non è valida o è stata revocata. |
| 403 | `insufficient_permissions` | La chiave non ha `audit-exports:read`, il job è stato creato con `include_ip` e la chiave non ha `audit-events:read_ip` (`required_permissions` elenca entrambi), oppure il chiamante non è né una chiave API né un utente della dashboard. |
| 404 | `not_found` | Il job non esiste, appartiene a un altro ambiente, o l'ID è malformato. Sul link: il token non è valido, è già stato usato o è scaduto. |
| 409 | `conflict` | Il job non è ancora completato, oppure il suo file è scaduto. |
| 429 | `rate_limited` | È stato superato il rate limit; rispetta `Retry-After`. |
| 503 | `service_unavailable` | Una dipendenza è temporaneamente non disponibile; riprova più tardi. |

## Esempio

```bash
curl -L -o audit-export.tar \
  https://api.eu.keenauth.com/v1/audit-exports/aex_01j9xk3m7q8r2s4t6v8w0y2z4e/download \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY"
```

Con `Accept: application/json` il link viene restituito come JSON invece che come redirect:

```bash
curl https://api.eu.keenauth.com/v1/audit-exports/aex_01j9xk3m7q8r2s4t6v8w0y2z4e/download \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Accept: application/json"
```

Gli SDK usano la forma JSON e restituiscono `url` e `expires_at`. La CLI legge il link da sola, scrive il pacchetto in un file (mai sullo standard output), rifiuta di sovrascrivere un file esistente e stampa la dimensione e l'SHA-256 di ciò che ha scritto:

```bash
keenauth audit-exports download aex_01j9xk3m7q8r2s4t6v8w0y2z4e --file audit-export.tar
```

## Dati personali

Questo endpoint contiene dati personali: eventi di audit. [Dati personali nelle API di KeenAuth](/docs/policies/personal-data) descrive cosa viene memorizzato, per quanto tempo viene conservato e come viene cancellato. Non inviare categorie particolari di dati personali (salute, dati biometrici, religione, opinioni politiche, orientamento sessuale) in nessun campo.
