Audit log export
Pull redacted audit rows as NDJSON into your SIEM, or download CSV from the Logs page in one click.
Every gateway decision lands in your audit log. Getting it out has two surfaces:
| Surface | Auth | Formats | Default window | Cap |
|---|---|---|---|---|
GET /api/v1/audit/export | API key with logs:read | NDJSON | last 24 hours | 5,000 rows per page, cursor to continue |
| Logs page download buttons | Dashboard session, any member (viewer role and above) | CSV or NDJSON | last 30 days | 50,000 rows |
Both require the siemExport plan feature (Team and above; otherwise 403), and both serve
the same rows. Payloads are redacted at write time, so nothing sensitive leaves through an export.
The dashboard download (/api/audit/export-download) accepts the same from / to / source /
decision filters as the API and pages internally up to its cap. A capped file carries an
x-axiorank-truncated: 50000 response header: narrow the window, or switch to the API endpoint
for anything ongoing.
The endpoint
GET /api/v1/audit/export. The versioned path is canonical; unversioned /api/audit/export
remains an alias (see Versioning & stability).
curl -s "https://app.axiorank.com/api/v1/audit/export?limit=1000" \
-H "Authorization: Bearer axr_live_..."Authenticate with a bearer API key carrying the logs:read scope. A missing or invalid key
returns 401; a plan without siemExport returns 403.
Query parameters
| Param | Type | Default | Notes |
|---|---|---|---|
from | ISO 8601 timestamp | 24 hours ago | Start of the window. |
to | ISO 8601 timestamp | now | End of the window. |
cursor | opaque string | none | The previous page's x-axiorank-next-cursor value. |
limit | integer, 1–5000 | 1000 | Rows per page. |
source | sdk | mcp | all | Filter by where the call entered. |
decision | allow | deny | hold | all | Filter by gateway decision. |
A bound that is present but unparseable, or a window where from is not before to, returns
400 { "error": "invalid from/to window" }. A malformed cursor returns 400 with
invalid cursor; any other bad parameter (say, limit=0) returns 400 with invalid query.
Pagination
Pages are keyset-paginated on (created_at, id), so the cursor is stable under concurrent
inserts (no skipped or doubled rows, unlike offset pagination). When a page comes back full, the
response carries an x-axiorank-next-cursor header; pass it back as cursor for the next page.
No header means you have everything.
BASE="https://app.axiorank.com/api/v1/audit/export"
QS="from=2026-06-01T00:00:00Z&to=2026-06-10T00:00:00Z&limit=1000"
CURSOR=""
while :; do
HDRS=$(mktemp)
curl -s -D "$HDRS" "$BASE?$QS${CURSOR:+&cursor=$CURSOR}" \
-H "Authorization: Bearer $AXIORANK_API_KEY" >> audit.ndjson
echo >> audit.ndjson # pages have no trailing newline
CURSOR=$(awk -F': ' 'tolower($1)=="x-axiorank-next-cursor"{gsub(/\r/,"",$2); print $2}' "$HDRS")
[ -z "$CURSOR" ] && break
donePersist your position
For a scheduled pull, store the last x-axiorank-next-cursor you saw (the final, partial page
returns none, so also store the created_at of the last row you ingested). On the next run,
resume from the cursor, or pass that timestamp as from, and dedupe on id downstream.
Formats
The API returns NDJSON (application/x-ndjson): one JSON object per line, each a full redacted
audit row. NDJSON is what you want for SIEM pipelines; every field survives, including nested ones.
The dashboard's CSV stays flat so it opens cleanly in a spreadsheet, with these columns:
created_at, id, agent_id, tool_name, source, decision, risk_score,
reason, mcp_server_id, trace_id, step_index, taint_blocked, taint_tagstaint_tags is ;-joined. Cells are RFC 4180 quoted, and a leading =, +, -, or @ is
prefixed with an apostrophe to defuse spreadsheet formula injection (tool names are
agent-supplied, so treat them as untrusted).
SIEM ingestion
Splunk (HTTP Event Collector): wrap each NDJSON line in a HEC envelope.
curl -s "$BASE?$QS" -H "Authorization: Bearer $AXIORANK_API_KEY" |
while IFS= read -r line || [ -n "$line" ]; do # `|| [ -n ... ]` keeps the last line (no trailing newline)
curl -s "https://splunk.example.com:8088/services/collector/event" \
-H "Authorization: Splunk $HEC_TOKEN" \
-d "{\"sourcetype\":\"axiorank:audit\",\"event\":$line}"
doneDatadog (Logs API): batch a page per request.
curl -s "$BASE?$QS" -H "Authorization: Bearer $AXIORANK_API_KEY" \
| jq -s 'map({ddsource: "axiorank", service: "audit"} + .)' \
| curl -s -X POST "https://http-intake.logs.datadoghq.com/api/v2/logs" \
-H "DD-API-KEY: $DD_API_KEY" -H "Content-Type: application/json" -d @-Run either inside the cursor loop above on a schedule, persisting the cursor between runs.
Next steps
- Webhooks: push instead of pull; react to events the moment they fire.
- Audit integrity: the signed transparency log behind these rows.
- Evidence bundle: an offline-verifiable export for auditors.