Audit Logs SDK
The audit namespace lives under client.audit. It defaults occurred_at to now, generates the required idempotency key for you, and ships an offline verifier that needs no network call. A key needs audit:write to send events and audit:read to read, verify, and export.
Install
pip install invoanceMethods
Send an event
Append one activity event to an org's signed ledger. occurred_at defaults to now and the Idempotency-Key is generated for you.
ev = await client.audit.events.ingest(
organization_id="org_01J8F3KQ2R7VWX9YB4ND6MCZAH",
action="team.member.invited",
actor={
"type": "user",
"id": "user_42",
"name": "Ada Lovelace",
"metadata": {"role": "admin", "mfa": True},
},
targets=[
{"type": "user", "id": "user_99", "name": "Charles Babbage",
"metadata": {"invited_email": "charles@acme.com"}},
{"type": "team", "id": "team_eng", "name": "Engineering"},
],
context={"location": "203.0.113.10", "user_agent": "Chrome/124.0.0.0"},
metadata={"plan": "growth", "seats": 25, "trial": False},
)
print(ev["event_id"])Get an event
Retrieve a single audit event by id.
event = await client.audit.events.get("aevt_01J…")
print(event["action"], event["seq"])List events
Keyset-paginated listing with action, actor, target, and date filters.
page = await client.audit.events.list(
organization_id="org_01J8F3KQ2R7VWX9YB4ND6MCZAH", actions="user.signed_in", limit=50
)
for e in page["events"]:
print(e["id"], e["action"])Verify an event offline
Reconstruct the canonical signed bytes and check the Ed25519 signature locally, with no trust in the server's answer.
from invoance import verify_audit_event
event = await client.audit.events.get("aevt_01J…")
result = verify_audit_event(event) # offline, no network call
print(result.valid) # TrueVerify an event (server)
Ask the API to re-verify a stored event against your tenant's pinned key. This is a network call, distinct from the offline verifier above.
result = await client.audit.events.verify("aevt_01J…")
print(result["valid"], result["reason"])Create an org
Register one of your end customers as an audit org, addressed by your own organization_id.
org = await client.audit.orgs.create(
organization_id="org_01J8F3KQ2R7VWX9YB4ND6MCZAH", name="Acme Production"
)
print(org["id"])List orgs
Return your audit orgs, newest first.
orgs = await client.audit.orgs.list()
print(orgs["orgs"])Check org integrity
Scan an org's gap-free sequence for holes. A contiguous range proves nothing was deleted; a gap is a tamper signal.
report = await client.audit.orgs.integrity("aorg_01J…")
print(report["contiguous"], report["gaps"])Set org retention
Set how long an org's events are retained, in days. Clamped to your plan's maximum.
await client.audit.orgs.set_retention("aorg_01J…", days=365)Create a webhook stream
Register a destination URL for an org. The HMAC signing secret is returned once. Capped per plan.
stream = await client.audit.streams.create(
"aorg_01J…", url="https://example.com/webhooks/audit"
)
print(stream["signing_secret"]) # shown onceList streams
Return an org's stream destinations and their delivery health. Never returns the signing secret.
streams = await client.audit.streams.list("aorg_01J…")
print(streams["streams"])Test a stream
Send a synthetic delivery to confirm the destination before real traffic flows.
result = await client.audit.streams.test("aorg_01J…", "astr_01J…")
print(result["delivered"], result["http_status"])Delete a stream
Remove a stream destination. Delivery stops immediately.
await client.audit.streams.delete("aorg_01J…", "astr_01J…")Mint a hosted-viewer link
Create a one-time, org-scoped portal link. intent is audit_logs (event viewer) or log_streams (stream config).
session = await client.audit.portal_sessions.create(
organization_id="org_01J8F3KQ2R7VWX9YB4ND6MCZAH", intent="audit_logs",
session_duration_seconds=7200, link_duration_seconds=300
)
print(session["url"])Export events
Queue an async CSV or NDJSON export, then poll for the download URL.
job = await client.audit.exports.create(
organization_id="org_01J8F3KQ2R7VWX9YB4ND6MCZAH", format="csv"
)
status = await client.audit.exports.get(job["id"]) # poll until "ready"
print(status["status"], status.get("download_url"))Next steps
See the Audit Logs quick start for the full request and response shapes, the hosted-viewer hand-off, SIEM streaming, and the endpoint reference.