Skip to content

Audit log export

The Arbitex audit log records every platform event — chat completions, policy decisions, DLP triggers, authentication events, and administrative changes. This guide covers how to query the audit log via the search API, export records as a signed package for offline analysis or compliance retention, and verify the HMAC-SHA256 signature on exported data.

All audit log endpoints require an admin role.


GET /api/admin/audit-logs/

Returns a paginated, filterable list of audit log entries. Results are ordered by created_at descending (newest first).

ParameterTypeDescription
limitinteger (1–500, default 50)Maximum number of results to return
offsetinteger (default 0)Number of results to skip for pagination
actionstringFilter by exact action type (e.g., chat_completion, dlp_block)
user_idstring (UUID)Filter by user UUID
model_idstringFilter by model identifier (e.g., claude-sonnet-4-6)
providerstringFilter by provider name (e.g., anthropic, openai)
created_afterdatetime (ISO 8601)Lower bound on created_at (inclusive)
created_beforedatetime (ISO 8601)Upper bound on created_at (inclusive)
searchstringCase-insensitive substring search on prompt_text and response_text
Terminal window
curl "https://api.arbitex.ai/api/admin/audit-logs/?limit=20&offset=0" \
-H "Authorization: Bearer <admin_api_key>"
Terminal window
curl "https://api.arbitex.ai/api/admin/audit-logs/?action=dlp_block&created_after=2026-03-01T00:00:00Z&created_before=2026-03-11T23:59:59Z&limit=100" \
-H "Authorization: Bearer <admin_api_key>"
Terminal window
curl "https://api.arbitex.ai/api/admin/audit-logs/?search=confidential&limit=50" \
-H "Authorization: Bearer <admin_api_key>"

The search parameter performs a case-insensitive substring match against both prompt_text and response_text.

{
"items": [
{
"id": "a1b2c3d4-...",
"user_id": "3fa85f64-...",
"action": "chat_completion",
"model_id": "claude-sonnet-4-6",
"provider": "anthropic",
"prompt_text": "What is the capital of France?",
"response_text": "Paris.",
"token_count_input": 12,
"token_count_output": 4,
"cost_estimate": 0.0002,
"latency_ms": 340,
"created_at": "2026-03-11T08:30:00.000Z"
}
],
"total": 4721,
"limit": 20,
"offset": 0
}

total is the count of all matching records (not just the current page). Use total, limit, and offset to compute pagination:

page_count = (total + limit - 1) // limit
next_offset = offset + limit if offset + limit < total else None
import requests
base_url = "https://api.arbitex.ai/api/admin/audit-logs/"
headers = {"Authorization": "Bearer <admin_api_key>"}
limit = 500
offset = 0
all_items = []
while True:
resp = requests.get(
base_url,
headers=headers,
params={"limit": limit, "offset": offset, "action": "dlp_block"},
)
resp.raise_for_status()
data = resp.json()
all_items.extend(data["items"])
offset += limit
if offset >= data["total"]:
break

POST /api/admin/audit/export

Exports audit log records for a date range as a signed JSON package. The package includes:

  • metadata: date range, record count, exporter username, export timestamp, HMAC chain status
  • records: the full audit records in chronological order
  • signature: HMAC-SHA256 over the serialized records
  • verification_instructions: human-readable steps to verify the signature offline
{
"start_date": "2026-03-01",
"end_date": "2026-03-11",
"action": null,
"user_id": null,
"model_id": null,
"provider": null
}
FieldTypeRequiredDescription
start_datedate (YYYY-MM-DD)YesStart of export window (inclusive, UTC midnight)
end_datedate (YYYY-MM-DD)YesEnd of export window (inclusive, UTC end-of-day)
actionstringNoFilter to a specific action type
user_idUUIDNoFilter to a specific user
model_idstringNoFilter to a specific model
providerstringNoFilter to a specific provider

Constraints:

  • Maximum window: 90 days (MAX_WINDOW_DAYS = 90). Requests exceeding this return 422 Unprocessable Entity.
  • AUDIT_HMAC_KEY must be configured on the Platform. Requests without it return 400 Bad Request.
Terminal window
curl -X POST https://api.arbitex.ai/api/admin/audit/export \
-H "Authorization: Bearer <admin_api_key>" \
-H "Content-Type: application/json" \
-d '{
"start_date": "2026-03-04",
"end_date": "2026-03-11"
}'
{
"metadata": {
"exported_at": "2026-03-11T09:00:00.000Z",
"exported_by": "admin@example.com",
"date_range": "2026-03-04 to 2026-03-11",
"record_count": 14821,
"hmac_chain_status": "intact"
},
"records": [
{
"id": "a1b2c3d4-...",
"user_id": "3fa85f64-...",
"action": "chat_completion",
"model_id": "claude-sonnet-4-6",
"provider": "anthropic",
"prompt_text": "...",
"response_text": "...",
"token_count_input": 312,
"token_count_output": 847,
"cost_estimate": 0.0024,
"latency_ms": 1840,
"hmac": "sha256:3f2a1b...",
"previous_hmac": "sha256:7c4e9d...",
"created_at": "2026-03-04T08:00:00.000Z"
}
],
"signature": "a3f8e2d1c9b4...",
"verification_instructions": "To verify this export: 1) Extract the 'records' array from the export JSON. 2) Serialize it with json.dumps(records, sort_keys=True, default=str). 3) Compute HMAC-SHA256 over the serialized string using your AUDIT_HMAC_KEY. 4) Compare the hex digest with the 'signature' field. They must match exactly."
}

The hmac_chain_status field in metadata reflects the integrity of the per-record HMAC chain across the exported records:

StatusMeaning
intactAll record HMACs are valid and the chain is unbroken
brokenOne or more records have invalid HMACs — possible tampering or data corruption
disabledAUDIT_HMAC_KEY is not configured; records do not have HMACs

For exports containing more than 10,000 records, the response is returned as a streaming JSON response with Content-Disposition: attachment; filename=audit-export.json. The response body is a single JSON document written incrementally to keep memory usage bounded.

When processing a large export:

Terminal window
# Save directly to file to avoid buffering the full response in memory
curl -X POST https://api.arbitex.ai/api/admin/audit/export \
-H "Authorization: Bearer <admin_api_key>" \
-H "Content-Type: application/json" \
-d '{"start_date": "2026-01-01", "end_date": "2026-03-31"}' \
--output audit-export-q1-2026.json

For very large date ranges (weeks to months), split the export into weekly or monthly windows:

Terminal window
for week in 01 08 15 22; do
curl -X POST https://api.arbitex.ai/api/admin/audit/export \
-H "Authorization: Bearer <admin_api_key>" \
-H "Content-Type: application/json" \
-d "{\"start_date\": \"2026-03-$week\", \"end_date\": \"2026-03-$(printf '%02d' $((10#$week+6)))\"}" \
--output "audit-export-2026-03-week-$week.json"
done

The signature field is an HMAC-SHA256 hex digest computed over the records array serialized with json.dumps(records, sort_keys=True, default=str). Use this to verify that the export has not been tampered with after download.

Verification procedure (Python):

import hashlib
import hmac
import json
def verify_export(export_path: str, audit_hmac_key: str) -> bool:
with open(export_path) as f:
export = json.load(f)
records = export["records"]
claimed_signature = export["signature"]
records_json = json.dumps(records, sort_keys=True, default=str)
computed = hmac.new(
audit_hmac_key.encode("utf-8"),
records_json.encode("utf-8"),
hashlib.sha256,
).hexdigest()
match = hmac.compare_digest(computed, claimed_signature)
if match:
print(f"Signature verified. {len(records)} records intact.")
else:
print(f"SIGNATURE MISMATCH. Expected: {computed}, got: {claimed_signature}")
return match

The AUDIT_HMAC_KEY is the same value configured on the Platform via the AUDIT_HMAC_KEY environment variable. This key should be stored in your secrets manager and provided to the verification script at runtime — do not embed it in the script itself.


FieldTypeDescription
idUUIDUnique record identifier
user_idUUIDUser who made the request
actionstringEvent type (e.g., chat_completion, dlp_block, login)
model_idstringModel identifier (e.g., claude-sonnet-4-6)
providerstringProvider name (e.g., anthropic)
prompt_textstringInput prompt text (may be [REDACTED] if DLP policy applied)
response_textstringModel response text (may be [REDACTED])
token_count_inputintegerInput token count
token_count_outputintegerOutput token count
cost_estimatefloatEstimated cost in USD
latency_msintegerEnd-to-end request latency in milliseconds
hmacstringPer-record HMAC for chain integrity verification
previous_hmacstringHMAC of the preceding record in the chain
created_atdatetimeRecord creation timestamp (UTC)

Audit records are retained indefinitely by default. Org-level retention policies can be configured by administrators via the admin operations panel — see Admin operations guide. For SOC 2 and ISO 27001 compliance programs, a minimum 90-day retention window is recommended; 12 months is typical for comprehensive incident response readiness.

If your compliance framework requires audit log immutability evidence, use the signed export package as a tamper-evident snapshot: export records monthly, verify the signature at export time, and store the export file alongside the signature value in an append-only store (S3 with Object Lock, Azure Blob immutable storage, etc.).