
# Create an audit export

`POST /v1/audit-exports` · permission `audit-exports:write`

Queues a job that writes the matching audit records into a signed package you can verify without trusting KeenAuth. The job is returned with `202 Accepted`; follow it with [Get an audit export](get.md) and fetch the file with [Download an audit export](download.md).

Only API keys (secret or restricted) can create exports; the job records the key as `requested_by`. The environment comes from the key.

## Request

`Idempotency-Key` is required (16 to 64 characters, `A-Z a-z 0-9 _ -`). The same key with the same body returns the first job with `Idempotent-Replayed: true`; the same key with a different body fails with `422 idempotency_key_reuse`. The SDKs and the CLI set the header for you.

The filters are the same as in [List audit events](../audit-events/list.md); an omitted field does not filter. Differences:

| Field | Type | Description |
|---|---|---|
| `stream` | string | `admin` (default) or `application`. |
| `from` | RFC 3339 | Inclusive lower bound. Defaults to the start of the retention window. There is no 92-day limit; a package holds at most 50 million lines (placeholders included). |
| `to` | RFC 3339 | Exclusive upper bound. Defaults to now; cannot be in the future. |
| `action`, `category`, `actor_type`, `actor_id`, `target_type`, `target_id`, `org_id`, `outcome`, `request_id`, `correlation_id`, `agent_only` | | As in the list filters. The list's `client` filter is not available here; a `client` field fails with `400 unknown_field`. |
| `include_ip` | boolean | Adds the full IP address and user agent to records younger than 13 months. Also requires `audit-events:read_ip`. Default `false`. |

```json
{
  "stream": "admin",
  "from": "2026-07-01T00:00:00Z",
  "to": "2026-10-01T00:00:00Z",
  "action": "user."
}
```

At most 3 jobs run at the same time per environment (1 in development and staging environments).

## Response

`202 Accepted` and the job with `status` `queued`. `filter` shows the applied filter with the defaults of `from` and `to` filled in. `recorded_before` is the snapshot bound: only records stored before it enter the package.

```json
{
  "id": "aex_01j9xk3m7q8r2s4t6v8w0y2z4e",
  "status": "queued",
  "filter": {
    "stream": "admin",
    "from": "2026-07-01T00:00:00Z",
    "to": "2026-10-01T00:00:00Z",
    "action": "user.",
    "category": null,
    "actor_type": null,
    "actor_id": null,
    "target_type": null,
    "target_id": null,
    "org_id": null,
    "outcome": null,
    "request_id": null,
    "correlation_id": null,
    "agent_only": false
  },
  "include_ip": false,
  "record_count": null,
  "file": null,
  "manifest_sha256": null,
  "failure_reason": null,
  "created_at": "2026-10-19T09:00:00Z",
  "started_at": null,
  "completed_at": null,
  "expires_at": null,
  "recorded_before": "2026-10-19T09:00:00Z",
  "requested_by": {"type": "api_key", "id": "key_01j9xk3m7q8r2s4t6v8w0y2z4d"}
}
```

## Errors

Every error is an `application/problem+json` body with a stable `code`.

| HTTP | `code` | When |
|---|---|---|
| 400 | `malformed_request` | `Idempotency-Key` is missing or malformed, or the body is not valid JSON. |
| 400 | `unknown_field` | The body has a field that is not listed above. |
| 400 | `invalid_filter` | The audit service rejected the filter, for example `to` in the future. |
| 400 | `range_outside_retention` | `from` or `to` is outside the audit retention window of your plan. |
| 422 | `validation_failed` | A field is invalid (wrong type, bad timestamp, `to` not after `from`, unknown enum value, malformed id, empty string). |
| 422 | `idempotency_key_reuse` | The `Idempotency-Key` was used with a different body. |
| 401 | `unauthenticated` | The API key is missing, invalid or revoked. |
| 403 | `insufficient_permissions` | The key lacks `audit-exports:write`, uses `include_ip` without `audit-events:read_ip` (`required_permissions` lists both), or the caller is not an API key. |
| 429 | `rate_limited` | Too many jobs are running in this environment, or the rate limit was exceeded; honor `Retry-After`. |
| 503 | `service_unavailable` | A dependency is temporarily unavailable; retry later. |

## Example

```bash
curl https://api.eu.keenauth.com/v1/audit-exports \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"from": "2026-07-01T00:00:00Z", "to": "2026-10-01T00:00:00Z", "action": "user."}'
```

```bash
keenauth audit-exports create --from 2026-07-01T00:00:00Z --to 2026-10-01T00:00:00Z --action user.
```

## Personal data

This endpoint carries personal data: audit events. [Personal data in the KeenAuth APIs](/docs/policies/personal-data) describes what is stored, how long it is kept and how it is deleted. Do not send special categories of personal data (health, biometric data, religion, political opinions, sexual orientation) in any field.
