Audit log export, chain verification, and refresh workflow
This page covers the audit-log export flow: a queued job that bundles your workspace’s audit trail over a date range you choose, plus the chain-verify endpoint that replays the tamper-evident hash chain over that same range and returns the daily Merkle roots your auditor cross-checks against the exported bundle. Use this when a regulator, buyer, or internal reviewer needs a downloadable, provable slice of the audit ledger — SOC 2 evidence requests, GDPR supervisory replies, or quarterly internal reviews — rather than a continuous stream.Export vs stream — pick the right surface
Two surfaces move your audit trail out of Orbit. Choose by lifecycle, not by format:- Export (this page) — a bounded date range, bundled into a file you hand to an auditor and keep as evidence. The export is day-aligned on UTC because the daily Merkle roots the chain-verifier returns anchor one digest per UTC day, and a partial day would anchor against an incomplete root.
- Stream (SIEM sinks) — every audit event pushed continuously to Splunk, Datadog, an S3 bucket, or an HTTPS drain you own. Configure a sink when your SOC policy requires live delivery, and use exports for point-in-time evidence requests.
Queue an export
GET /api/v1/compliance/audit-export accepts three query parameters and
returns 202:
422 VALIDATION_ERROR with the offending field; a
queue-write failure returns 500 AUDIT_EXPORT_QUEUE_FAILED.
Job lifecycle: pending → running → complete | failed
PollGET /api/v1/compliance/audit-export/:jobId until status settles on
complete or failed:
The job row is scoped to your workspace — a job id from another organization
returns
404 NOT_FOUND.
Signed-URL expiry and refresh
The signed download URL has a 7-day TTL. Poll the job inside that window and mirror the bundle into your own evidence store instead of bookmarking the link. If you click a stale link, storage refuses it with a403. The status
response handles this explicitly: once the URL lapses, the job returns
download_url: null and download_url_expired: true rather than handing you
a link that will fail. The recovery path is to queue a fresh export over
the same range — the queue call is cheap, the export is deterministic against
the ledger, and two runs over the same UTC days produce the same rows.
Chain-verify: replay the tamper-evident chain
GET /api/v1/compliance/audit-export/:jobId/verify replays the hash chain
over the job’s exact range and returns the verdict plus every daily root that
anchors it. This is the endpoint auditors hit to prove the exported rows match
the tamper-evident chain Orbit persists.
Each audit event carries the hash of the event before it, so inserting,
removing, or reordering a row breaks the link at that position. The verifier
responds with:
Issue
reason codes flag exactly which link broke:
Each issue carries the affected row
id, its created_at, and expected /
actual hashes, so a break narrows to one event rather than invalidating the
range.
How an auditor cross-checks the bundle
- Run
verifyagainst the job id.chain_valid: truewith an emptyissueslist means every link in the range held at verification time. - Compare
rows_checkedagainst thetotal_rowson the export job — the counts must agree, proving the bundle contains every row the chain covers. - Compare
first_hash/last_hashagainst the first and last rows of the exported bundle. - For each entry in
daily_roots, themerkle_rootcommits to the set of rows Orbit recorded on that UTC day, andanchor_urlpoints to the persisted root record the auditor can pin independently. Date-aligned exports make this comparison exact.
chain_valid: false as a signal to hold the export internally and
contact security@devotel.io with the job id and the issues entries — do not
hand a broken-chain bundle to an external party without the caveat agreed
first. Orbit records every verification call in your audit log with the range,
row count, verdict, and issue count.
Worked example — queue → poll → download → verify
Worked example — SOC 2 evidence request
An auditor asks for your access-control audit trail for Q1 with proof the records are complete and untampered:- Queue the export with
from=2026-01-01&to=2026-03-31. Chooseformat=jsonfor the raw ledger, orformat=scimwhen the recipient’s evidence tooling ingests SCIM Event Token (RFC 8417) envelopes. - Poll to
complete, download inside the 7-day window, and mirror the file into your own store so the evidence survives the URL TTL. - Run
verifyand capturerows_checked,chain_valid,first_hash,last_hash, and thedaily_rootslist. Attach that JSON alongside the bundle. - Hand the auditor both files plus the cross-check steps above. They confirm the row counts agree and the chain endpoints match before reading a single event.
- If the signed URL lapses before the auditor fetches from your mirror, queue the same range again — do not forward the expired link.
Rate limits and roles
- All three endpoints are gated to the workspace owner or admin role (API keys follow the same gate), because the payload exposes every audit event on the workspace — user ids, IP addresses, and event details.
- The queue endpoint (
GET /audit-export) is write-weighted: 5 per minute per workspace, so scripted evidence pulls should queue a handful of ranges, not hundreds. - Status-poll and chain-verify are read-weighted: 120 per minute per workspace, so a 1–2 second poll loop from a dashboard or script stays well under the ceiling.
- Queueing writes a
compliance.audit_export_requestedaudit entry; each verification call writes acompliance.audit_export_verifiedentry with the verdict and issue count.
Tenant-owned posture
Audit export and chain verification are tenant-owned controls: Orbit produces the bundle and anchors the chain and Merkle roots; you retain the evidence. Mirror downloads into your own store within the 7-day window, keep the verify response alongside the bundle, and treat the export’s integrity as yours to check — the endpoint gives you the verdict, the retention decision is yours.Related
- Stream audit logs to your SIEM — continuous delivery instead of bounded exports.
- Compliance evidence binder — framework-mapped packs with the chain pre-checked for SOC 2, ISO 27001, GDPR, and HIPAA.
- The export families model — where this export sits among Orbit’s four outbound-data families.
- Audit log — query the ledger directly and subscribe to
the signed
audit.log.createdwebhook. - Immutable archival export — WORM bundles of messages and recordings, the corresponding control for payload evidence.