← All code samples

Export audit events

Aiden keeps a cursor-paginated audit trail (tool calls, policy decisions, break-glass on/off, and more). The first page alone is not enough when you need a full window for compliance or a SIEM. The SDK pages for you and writes one JSON object per line.

This sample exports the last N days to a local NDJSON file, with optional filters for event type and OCSF shape.

When you need this

  • Hand a window to an external auditor. Write native or OCSF events for a date range to a file you can attach to a ticket.
  • Feed a SIEM. Pass format: "ocsf" so each line is OCSF API Activity (class_uid 6003).
  • Spot-check break-glass. Filter event_type=break_glass_activated (or break_glass_deactivated) after an outage.

How to run

export STACKGEN_URL=https://app.stackgen.com
export STACKGEN_TOKEN=stackgen_…
export STACKGEN_WORKSPACE_ID=<workspace-uuid>

pip install stackgen-sdk==0.4.0
python examples/automation/audit-export.py --days 7 --output audit.ndjson

Optional flags: --event-type, --format ocsf, --max-events (dry run).

Minimal

from datetime import datetime, timedelta, timezone
from stackgen import StackgenClient, StackgenConfig

client = StackgenClient(
    StackgenConfig(
        base_url="https://app.stackgen.com",
        api_token="stackgen_…",
        workspace_id="<workspace-uuid>",
    )
)

now = datetime.now(timezone.utc)
start = now - timedelta(days=7)
result = client.aiden.export_audit_events(
    "audit.ndjson",
    **{
        "from": start.strftime("%Y-%m-%dT%H:%M:%SZ"),
        "to": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
        "format": "ocsf",  # or omit / "native"
    },
)
print(result.count, result.output_path)

# Stream without a file:
for event in client.aiden.iter_audit_events(event_type="break_glass_activated"):
    print(event["id"], event.get("actor_login"))

Notes

  • list_audit_events / listAuditEvents still return one page only. Use iter_* or export_* when you need the full window.
  • Native list responses omit large payload fields unless you pass include_payload=true. get_audit_event(id) always returns the full payload.
  • OCSF export (format=ocsf) expects a from/to window on the server; the sample always sets both.
  • Pass max_events / maxEvents to stop early while you dry-run a filter.

Complete scripts

#!/usr/bin/env python3
"""Export Aiden audit events to a local NDJSON file.

Pages past the server's per-page cap automatically. Use ``--format ocsf`` when
handing the file to a SIEM or external auditor.

Usage:
  export STACKGEN_URL=https://app.stackgen.com
  export STACKGEN_TOKEN=stackgen_…
  export STACKGEN_WORKSPACE_ID=<workspace-uuid>
  python examples/automation/audit-export.py --days 7 --output audit.ndjson
  python examples/automation/audit-export.py --days 30 --event-type break_glass_activated
  python examples/automation/audit-export.py --days 7 --format ocsf --output audit-ocsf.ndjson
"""

from __future__ import annotations

import argparse
import os
from datetime import datetime, timedelta, timezone

from stackgen import StackgenClient, StackgenConfig


def require_env(name: str, *fallbacks: str) -> str:
    for key in (name, *fallbacks):
        value = (os.environ.get(key) or "").strip()
        if value:
            return value
    raise SystemExit(f"missing env {name}")


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--days",
        type=int,
        default=7,
        help="how many days back to export (default: 7)",
    )
    parser.add_argument(
        "--output",
        default="audit.ndjson",
        help="local NDJSON path (default: audit.ndjson)",
    )
    parser.add_argument(
        "--event-type",
        default=None,
        help="optional event_type filter (e.g. break_glass_activated)",
    )
    parser.add_argument(
        "--format",
        choices=("native", "ocsf"),
        default="native",
        help="native AuditEvent objects, or OCSF API Activity for SIEM hand-off",
    )
    parser.add_argument(
        "--max-events",
        type=int,
        default=None,
        help="optional cap for a dry run",
    )
    args = parser.parse_args()
    if args.days < 1:
        raise SystemExit("--days must be >= 1")

    client = StackgenClient(
        StackgenConfig(
            base_url=require_env("STACKGEN_URL"),
            api_token=require_env("STACKGEN_TOKEN"),
            workspace_id=require_env("STACKGEN_WORKSPACE_ID", "STACKGEN_PROJECT"),
        )
    )

    now = datetime.now(timezone.utc)
    start = now - timedelta(days=args.days)
    params: dict[str, object] = {
        "from": start.strftime("%Y-%m-%dT%H:%M:%SZ"),
        "to": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
        "format": args.format,
    }
    if args.event_type:
        params["event_type"] = args.event_type
    if args.max_events is not None:
        params["max_events"] = args.max_events

    result = client.aiden.export_audit_events(args.output, **params)
    print(f"wrote {result.count} events to {result.output_path}")


if __name__ == "__main__":
    main()

See also