
# 감사 내보내기 다운로드

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

완료된 내보내기 작업 패키지에 대한 일회용 링크를 제공합니다. API 키(시크릿 또는 제한된 키)와 대시보드 사용자가 다운로드할 수 있습니다. 대시보드에서 다운로드하려면 최근 로그인(step-up, 5분 이내)이 필요합니다. 모든 호출은 링크가 발급되기 전에 `admin` 스트림에 `audit_export.downloaded` 레코드를 기록합니다.

## 요청

| 매개변수 | 위치 | 필수 | 설명 |
|---|---|---|---|
| `export_id` | 경로 | 예 | 내보내기 작업 ID이며 `aex_` 접두사가 붙습니다. |

`include_ip: true`로 생성된 작업은 다운로드하는 키에 `audit-events:read_ip`도 필요합니다.

## 응답

응답은 `Accept` 헤더에 따라 달라집니다. 두 형식 모두 `Cache-Control: no-store`, `Referrer-Policy: no-referrer`, `Vary: Accept`를 포함하며, 둘 다 같은 `audit_export.downloaded` 레코드를 기록합니다.

- **`Accept`에 `application/json`가 포함된 경우**(명시적으로, 0이 아닌 `q`와 함께; `*/*`는 해당되지 않음): JSON 본문과 함께 `200 OK`을 반환합니다. 브라우저나 교차 출처 리디렉션을 읽을 수 없는 클라이언트에서는 이 형식을 사용하십시오. `url`를 읽은 다음 여십시오.

  | 필드 | 타입 | 설명 |
  |---|---|---|
  | `url` | string | 같은 호스트의 상대 링크이며 `/downloads/audit-exports/{environment_id}/{export_id}/{token}`. |
  | `expires_at` | string (date-time) | 링크가 더 이상 작동하지 않는 시점(발급 후 15분). |

- **그 밖의 `Accept`**: 빈 본문과 함께 `302 Found`을 반환합니다. `Location`에는 동일한 상대 링크가 담깁니다.

- 링크는 15분 동안 유효하며 한 번만 사용할 수 있습니다. 전송이 중단되더라도 토큰은 첫 요청에서 소진됩니다. 새 링크가 필요하면 이 작업을 다시 호출하십시오.
- 링크에는 자격 증명이 필요하지 않으며, 토큰이 유일한 자격 증명입니다. `Location`(또는 `url`)을 비밀로 유지하고 로그에 남기지 마십시오.
- 파일은 환경 키로 저장 시 암호화되므로 스토리지 사전 서명 URL은 사용되지 않습니다. KeenAuth가 복호화하여 스트리밍합니다.

링크를 따라가면 `200 OK`이 `Content-Type: application/x-tar`, `Content-Length`, `Content-Disposition: attachment; filename="audit-export-aex_….tar"`과 함께 반환됩니다. tar 패키지에는 다음이 들어 있습니다:

| 파일 | 내용 |
|---|---|
| `records.ndjson.gz` | 대상 시간대마다 모든 체인 레코드를 순서대로, 한 줄에 JSON 객체 하나씩 담습니다. 필터와 일치하는 레코드는 전부 담고, 나머지는 내용이 없는 자리표시자(`"omitted": true`)로 담아 내용을 드러내지 않으면서도 해시 체인을 재구성할 수 있도록 합니다. |
| `manifest.json` | 필터, 개수, 파일 해시, 그리고 대상 시간대마다 서명된 체크포인트와 포함 증명; 정규 JSON(RFC 8785). |
| `manifest.jws` | `manifest.json`에 대한 ES256 서명; `audit-jwks.json`의 공개 키로 검증하십시오. |

`Content-Length` 바이트가 모두 도착하기 전에 연결이 끝나면 패키지가 불완전하므로 버리고 새 링크를 요청하십시오. 잘못되었거나, 이미 사용했거나, 만료된 링크는 `404 not_found`를 반환합니다.

## 오류

모든 오류는 안정적인 `code`를 포함한 `application/problem+json` 본문으로 반환됩니다.

| HTTP | `code` | 발생 조건 |
|---|---|---|
| 401 | `unauthenticated` | API 키가 없거나, 잘못되었거나, 취소되었습니다. |
| 403 | `insufficient_permissions` | 키에 `audit-exports:read` 권한이 없거나, 작업이 `include_ip`로 생성되었는데 키에 `audit-events:read_ip` 권한이 없거나(`required_permissions`에 두 권한이 모두 나열됨), 호출자가 API 키도 대시보드 사용자도 아닙니다. |
| 404 | `not_found` | 작업이 없거나, 다른 환경에 속하거나, ID 형식이 잘못되었습니다. 링크의 경우: 토큰이 잘못되었거나, 이미 사용했거나, 만료되었습니다. |
| 409 | `conflict` | 작업이 아직 완료되지 않았거나, 파일이 만료되었습니다. |
| 429 | `rate_limited` | 속도 제한을 초과했습니다. `Retry-After` 값을 따르십시오. |
| 503 | `service_unavailable` | 종속 서비스를 일시적으로 사용할 수 없습니다. 나중에 다시 시도하십시오. |

## 예시

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

`Accept: application/json`를 사용하면 리디렉션 대신 링크가 JSON으로 반환됩니다:

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

SDK는 JSON 형식을 사용하며 `url`와 `expires_at`를 반환합니다. CLI는 링크를 직접 읽고, 패키지를 파일에 씁니다(표준 출력에는 절대 쓰지 않음). 기존 파일은 덮어쓰지 않으며, 기록한 내용의 크기와 SHA-256을 출력합니다:

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

## 개인정보

이 엔드포인트는 개인정보, 즉 감사 이벤트를 포함합니다. [KeenAuth API의 개인정보](/docs/policies/personal-data)에서는 저장되는 항목, 보관 기간, 삭제 방법을 설명합니다. 어떤 필드에도 특수 범주의 개인정보(건강, 생체 정보, 종교, 정치적 견해, 성적 지향)를 보내지 마십시오.
