
# Télécharger un export d'audit

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

Fournit un lien à usage unique vers le package d'un job d'export terminé. Les clés API (secrètes ou restreintes) et les utilisateurs du tableau de bord peuvent télécharger ; un téléchargement depuis le tableau de bord exige une connexion récente (étape supplémentaire, datant de 5 minutes au plus). Chaque appel écrit un enregistrement `audit_export.downloaded` dans le flux `admin` avant que le lien ne soit émis.

## Requête

| Paramètre | Dans | Obligatoire | Description |
|---|---|---|---|
| `export_id` | chemin | oui | Identifiant du job d'export, préfixé par `aex_`. |

Un job créé avec `include_ip: true` exige aussi `audit-events:read_ip` sur la clé qui télécharge.

## Réponse

La réponse dépend de l'en-tête `Accept`. Les deux formes contiennent `Cache-Control: no-store`, `Referrer-Policy: no-referrer` et `Vary: Accept`, et écrivent toutes deux le même enregistrement `audit_export.downloaded`.

- **`Accept` liste `application/json`** (explicitement, avec un `q` non nul ; `*/*` ne compte pas) : `200 OK` avec un corps JSON. Utilisez cette forme depuis un navigateur ou tout client qui ne peut pas lire une redirection inter-origine : lisez `url`, puis ouvrez-le.

  | Champ | Type | Description |
  |---|---|---|
  | `url` | string | Lien relatif sur le même hôte, `/downloads/audit-exports/{environment_id}/{export_id}/{token}`. |
  | `expires_at` | string (date-time) | Moment où le lien cesse de fonctionner (15 minutes après son émission). |

- **Tout autre `Accept`** : `302 Found` avec un corps vide. `Location` est le même lien relatif.

- Le lien est valide 15 minutes et ne fonctionne qu'une fois : le jeton est consommé à la première requête, même si le transfert est interrompu. Appelez à nouveau cette opération pour obtenir un nouveau lien.
- Le lien ne demande aucune identification ; le jeton est la seule identification. Gardez `Location` (ou `url`) secret et ne le journalisez pas.
- Le fichier est chiffré au repos avec la clé de l'environnement, donc aucune URL pré-signée de stockage n'est utilisée ; KeenAuth le déchiffre et le diffuse.

Suivre le lien renvoie `200 OK` avec `Content-Type: application/x-tar`, `Content-Length` et `Content-Disposition: attachment; filename="audit-export-aex_….tar"`. Le package tar contient :

| Fichier | Contenu |
|---|---|
| `records.ndjson.gz` | Chaque enregistrement de chaîne de chaque heure couverte, dans l'ordre de séquence, un objet JSON par ligne : les enregistrements correspondants en entier, les autres sous forme d'espaces réservés sans contenu (`"omitted": true`) afin que la chaîne de hachage puisse être reconstruite sans les révéler. |
| `manifest.json` | Filtre, compteurs, empreinte du fichier et, pour chaque heure couverte, le point de contrôle signé et la preuve d'inclusion ; JSON canonique (RFC 8785). |
| `manifest.jws` | Signature ES256 sur `manifest.json` ; vérifiez-la avec les clés publiques de `audit-jwks.json`. |

Si la connexion se termine avant que `Content-Length` octets n'arrivent, le package est incomplet ; jetez-le et demandez un nouveau lien. Un lien invalide, déjà utilisé ou expiré renvoie `404 not_found`.

## Erreurs

Chaque erreur est un corps `application/problem+json` avec un `code` stable.

| HTTP | `code` | Quand |
|---|---|---|
| 401 | `unauthenticated` | La clé API est absente, invalide ou révoquée. |
| 403 | `insufficient_permissions` | La clé ne possède pas `audit-exports:read`, le job a été créé avec `include_ip` et la clé ne possède pas `audit-events:read_ip` (`required_permissions` les liste toutes deux), ou l'appelant n'est ni une clé API ni un utilisateur du tableau de bord. |
| 404 | `not_found` | Le job n'existe pas, appartient à un autre environnement, ou l'identifiant est mal formé. Sur le lien : le jeton est invalide, déjà utilisé ou expiré. |
| 409 | `conflict` | Le job n'est pas encore terminé, ou son fichier a expiré. |
| 429 | `rate_limited` | La limite de débit a été dépassée ; respectez `Retry-After`. |
| 503 | `service_unavailable` | Une dépendance est temporairement indisponible ; réessayez plus tard. |

## Exemple

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

Avec `Accept: application/json`, le lien est renvoyé en JSON au lieu d'une redirection :

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

Les SDK utilisent la forme JSON et renvoient `url` et `expires_at`. La CLI lit elle-même le lien, écrit le package dans un fichier (jamais sur la sortie standard), refuse d'écraser un fichier existant et affiche la taille et le SHA-256 de ce qu'elle a écrit :

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

## Données personnelles

Cet endpoint contient des données personnelles : des événements d'audit. [Données personnelles dans les API KeenAuth](/docs/policies/personal-data) décrit ce qui est stocké, combien de temps c'est conservé et comment c'est supprimé. N'envoyez pas de catégories particulières de données personnelles (santé, données biométriques, religion, opinions politiques, orientation sexuelle) dans aucun champ.
