InvoanceInvoance
Log inStart free
In this article
Resources/How to Export Audit Logs for Enterprise Customers: Signed, Verifiable, Audit-Ready
Compliance·11 min read·June 22, 2026

How to Export Audit Logs for Enterprise Customers: Signed, Verifiable, Audit-Ready

By Adeola Okunola, Founder, Invoance·

Any system can dump audit logs to a CSV. The problem is that a CSV is a file you are asking an auditor to trust. This guide shows how to export audit logs as signed, independently verifiable records: paginated from a real API, with a per-record Ed25519 signature and a gapless sequence that proves nothing was dropped or altered.

Every system can export a CSV. That is the problem.

When an enterprise customer asks whether they can export their audit logs, the question behind the question is rarely "can I get a file." Any system can produce a file. Their security and compliance teams are asking something harder: can we hand this export to an auditor, a regulator, or opposing counsel, and have it hold up when someone has a reason to doubt it.

A CSV or JSON dump from a typical logging stack does not clear that bar. It is a file generated by the vendor, from a database the vendor controls, with timestamps from a clock the vendor can set. Nothing inside the file proves it is complete. Nothing proves a row was not edited, reordered, or quietly removed before the export ran. The auditor is being asked to trust the exporter, which is precisely the thing an audit exists to avoid.

This guide is about exporting audit logs in a form that does not require that trust. The mechanics are straightforward, you paginate an API and write the results to a file. The difference is in what each record carries: a per-tenant Ed25519 signature applied when the event was written, and a gapless sequence number that makes a missing record detectable. The export becomes evidence that verifies itself, instead of a document that depends on your good word.

What enterprise customers actually mean by "export"

Strip away the format debate and three concrete requirements remain. They map almost exactly to what an auditor checks.

Completeness. The export has to represent every event in the requested scope, with a way to detect if any are missing. "Here is a CSV" tells the auditor nothing about what is not in the CSV. A gap has to be detectable from the data itself, not taken on faith.

Integrity. Each record has to be provably unchanged since the moment it was created. Not "we have strict database permissions," that is a statement about your operational controls, which the auditor would then have to audit. The record itself has to carry the proof.

Portability. The auditor has to be able to verify the export without access to your systems and without trusting you or your vendor. If verification requires logging into your dashboard or believing your assurances, it is not independent verification.

Most audit-log exports satisfy none of these. They are point-in-time snapshots whose trustworthiness collapses to "the vendor says so." The rest of this guide walks through producing an export that satisfies all three.

Key insight. An audit-log export is only as useful as it is hard to forge. If the recipient has to trust the exporter, the export has not proven anything, it has just moved the trust problem into a spreadsheet.

Signed-at-write-time changes what an export is

On Invoance, an audit event is signed the instant it is recorded, not when it is exported. When you POST an event to the audit API, the backend assigns it the next sequence number for that organization, canonicalizes it, hashes the canonical bytes, and signs the result with your tenant's own Ed25519 private key. The signature and the hash are stored on the row alongside the data.

Two properties fall out of this. First, every record is independently verifiable for as long as you keep it, the proof travels with the data, so a record pulled out of the system years later still verifies against your published public key. Second, the sequence number is gapless and assigned in order, so a missing record leaves a hole that anyone can see.

That is why "export" here is not a special feature you have to wait for. The records are already proof-carrying. Exporting them is reading them out, and because each one is signed and sequenced, the file you produce is evidence the moment it lands on disk.

Three-stage flow: an audit event is signed with a per-tenant Ed25519 key when written, exported by paginating the signed query API into an NDJSON file, then verified independently by an auditor who checks the signature and a contiguous sequence without trusting the vendor.
An audit-log export on Invoance: signed at write time, paginated out through the API, and verified independently by the recipient.

Step 1: Export the log by paginating the signed API

The export mechanism is the audit query endpoint, GET /v1/audit/events, called with an API key that holds the audit:read scope. It returns events newest-first and uses keyset pagination, so you page through an arbitrarily large log without the skips and duplicates that offset-based paging suffers under concurrent writes.

You scope the export with query parameters: org_id is required, and you can narrow by occurred_after and occurred_before (RFC3339 timestamps), by action (a comma-separated allowlist), by actor_id, and by target_id. limit defaults to 50 and caps at 100 per page. Each response carries an events array and a next_cursor; when next_cursor comes back null, you have reached the end of the range.

The loop is the whole export: call the endpoint, write the events, and if next_cursor is set, call again with cursor set to that value. Keep going until it is null.

One page of an export, scoped to an org and a date range
curl -s https://api.invoance.com/v1/audit/events \
  -H "Authorization: Bearer $INVOANCE_AUDIT_READ_KEY" \
  --get \
  --data-urlencode "org_id=aorg_K2Q8Z3X9V7" \
  --data-urlencode "occurred_after=2026-01-01T00:00:00Z" \
  --data-urlencode "occurred_before=2026-04-01T00:00:00Z" \
  --data-urlencode "limit=100"

# Response:
# {
#   "events": [ { "id": "aevt_...", "seq": 4831, "action": "user.role.granted",
#                 "payload_hash": "9f86...a08", "signature": "..." }, ... ],
#   "next_cursor": "3231323..."   // null on the last page
# }

Key insight. Keyset pagination matters for exports specifically: new events always sort onto the first page, so paging backward in time under live writes never skips or double-counts a record. An offset-based export can silently drop rows the moment the log is written to mid-export.

The same export, as a script

In practice you wrap that one call in a loop and stream the results to a file. NDJSON, one JSON object per line, is the friendliest format to hand an auditor: every line is a complete, independently verifiable record, and the file streams without ever holding the whole log in memory.

The pattern below is the entire export. It writes each page as it arrives and follows next_cursor until the API reports there is nothing left. The same shape works in any language, the logic is request, append, advance the cursor, repeat.

Node: stream a full export to NDJSON
import { writeFile, appendFile } from "node:fs/promises";

const BASE = "https://api.invoance.com/v1/audit/events";
const KEY = process.env.INVOANCE_AUDIT_READ_KEY;

async function exportAuditLog(orgId, outFile) {
  await writeFile(outFile, "");
  let cursor = null;
  let total = 0;

  do {
    const url = new URL(BASE);
    url.searchParams.set("org_id", orgId);
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);

    const res = await fetch(url, { headers: { Authorization: "Bearer " + KEY } });
    if (!res.ok) throw new Error("export failed: HTTP " + res.status);

    const page = await res.json();
    // One signed record per line (NDJSON).
    const lines = page.events.map((e) => JSON.stringify(e)).join("\n");
    if (lines) await appendFile(outFile, lines + "\n");

    total += page.events.length;
    cursor = page.next_cursor;
  } while (cursor);

  return total;
}

const n = await exportAuditLog("aorg_K2Q8Z3X9V7", "events.ndjson");
console.log("exported " + n + " signed audit records");

Step 2: Verify every record's signature

A signed export is only worth something if the recipient can check the signatures, so the audit API exposes verification as a first-class endpoint. GET /v1/audit/events/{id}/verify takes a single event, rebuilds the exact canonical bytes that were signed from the stored columns, recomputes the hash, and checks the Ed25519 signature against your tenant's registered public key. Not the key stored on the row, the key pinned in your tenant record, which is what makes a forged or swapped key fail rather than pass.

The response is unambiguous: valid is a boolean, reason names the failure when valid is false (for example payload_hash_mismatch), and the body echoes the schema, the payload_hash it computed, and key_source so the verifier knows exactly which key was used.

For an export, you run this across the file. A practical pattern is to verify every record on the way out, or verify a random sample plus every record above a sensitivity threshold, and attach the results to the export as a manifest. Either way the point stands: the auditor does not have to take the file on faith, and neither do you. Because the signature is Ed25519 and your tenant's public key is publishable, a recipient can also verify the whole export offline, with a standard crypto library, without ever calling Invoance.

GET /v1/audit/events/{id}/verify → 200
{
  "id": "aevt_01JZ8M4K7Q2W9R3X5Y6Z7A8B9C",
  "valid": true,
  "reason": null,
  "schema": "invoance.audit/1",
  "payload_hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  "signed_data": { "action": "user.role.granted", "actor": { "id": "u_42" } },
  "key_source": "tenant_keys"
}
See it in action
  • Why traditional audit logs fail under regulatory scrutiny— The longer argument for why database-backed logs do not survive adversarial examination, and what cryptographic proof changes.

Step 3: Prove the export is complete

Verification tells you each record you exported is authentic. It does not, on its own, tell you that you exported all of them. Completeness is a separate question, and it is the one a CSV can never answer.

This is what the gapless sequence number is for. Every audit event for an org carries a strictly increasing seq, assigned in a single transaction at write time. GET /v1/audit/orgs/{id}/integrity walks that sequence and reports whether it is contiguous: you get last_seq, a count, a contiguous boolean, and a gaps array listing any missing ranges. A complete export covers a contiguous block of sequence numbers; a hole in the middle is mathematically visible.

Run the integrity scan as part of the export and record its result. Together with the per-record signatures, it lets you make a precise, defensible claim: these are records 1 through last_seq for this organization, each one signed and unaltered, with no gaps.

GET /v1/audit/orgs/{id}/integrity → 200
{
  "org_id": "aorg_K2Q8Z3X9V7",
  "from": null,
  "to": null,
  "last_seq": 48213,
  "count": 48213,
  "expected": 48213,
  "contiguous": true,
  "gaps": [],
  "gaps_truncated": false,
  "note": "Sequence gaps reveal modification or middle-deletion. Truncation of the newest events is not detectable from the sequence alone; that requires the signed checkpoint (see roadmap)."
}

Key insight. Be precise about what the integrity scan proves. A gap in the sequence reveals a modified or deleted record in the middle of the log. Detecting truncation of the very newest events requires a signed checkpoint of the sequence head. Claim what the math supports and nothing more, overclaiming integrity is how a trust product loses trust.

Step 4: Set the retention window your contracts require

Enterprise audit-log requirements almost always include a retention term, "logs retained for N years, available on request." On Invoance, retention is set per organization with PUT /v1/audit/orgs/{id}/retention, and the window you can request is bounded by your plan tier. Higher tiers unlock longer retention; the API clamps any request to your plan's cap rather than silently accepting a value it cannot honor.

Behind the endpoint, recent events stay hot in Postgres for fast querying, and older events are tiered to dedicated cold object storage as compressed, signed segments. The signature and sequence number travel with the data into cold storage, so an event exported from a three-year-old cold segment verifies exactly the same way as one written this morning. Retention changes where a record lives, never whether it can still be proven.

Set a 365-day retention window for an org
curl -s -X PUT https://api.invoance.com/v1/audit/orgs/aorg_K2Q8Z3X9V7/retention \
  -H "Authorization: Bearer $INVOANCE_AUDIT_WRITE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"days": 365}'

On the roadmap: one-call bulk export and SIEM streaming

Everything above works against the live API today. Two higher-level conveniences are on the roadmap, and both are wrappers around the same signed records, not new sources of truth.

One-call bulk export will let you request an entire range as a single job: submit a scope and a format (CSV or NDJSON), poll for completion, and download one file covering both hot and cold storage, without writing the pagination loop yourself. SIEM streaming will push each event to a destination such as a webhook or Splunk as it is written, so audit events land in your customer's existing security tooling in near real time.

Until those ship, the paginate-then-verify pattern in this guide is the supported path, and it is the exact mechanism the bulk-export job will automate. Nothing about the records changes, the convenience layer is the only thing being added.

From signup to your first verifiable export

The fastest way to evaluate this is to produce a real export against a free account. Sign up, create an audit organization, and mint an API key with the audit:read and audit:write scopes. Write a handful of events with the ingest endpoint, then run the export loop above, you get back records that are actually signed with your tenant's own key, with a real sequence and a real integrity scan.

The records your free account produces verify through the same endpoints and against the same kind of per-tenant key that back Compliance and Enterprise accounts. The infrastructure is identical; the plan tiers change limits and retention, not whether a record can be proven.

If you are working through an enterprise security review, that free export is the asset to hand over. Give the reviewer an exported record and the verify endpoint, let them confirm the signature and the contiguous sequence themselves, and the trust question stops being a conversation and becomes a demo. That is usually what unblocks the deal.

See it in action
  • Create a free account— Sign up, create an audit organization, and generate audit:read / audit:write API keys in a few minutes.
  • Developer documentation— SDKs, authentication, and the API reference for building on Invoance.
  • Compare plans and retention limits— Monthly event limits and retention windows by tier.

Append-only, signed records of business events for audits, compliance, and regulatory proof, independently verifiable.

Start freeEvent LedgerDiscuss your use case
Adeola Okunola
Adeola Okunola

Founder, Invoance

About the author

I'm Adeola, founder of Invoance. I've spent most of my engineering life building systems where everything is provable. Invoance is what happens when you turn that obsession into infrastructure other people can use. Most "audit trails" can be quietly edited after the fact, which makes them stories, not proof. Most people use "evidence" and "proof" interchangeably. They aren't the same thing. I write here about audit integrity, AI attestation, and the gap between documenting controls and proving outcomes.

All articles by Adeola

Recommended

Compliance·7 min read

Why Traditional Audit Logs Fail Under Regulatory Scrutiny

Your application logs record what happened. But in an audit or legal proceeding, the first question is not what your logs say, it is whether anyone can trust your logs. Traditional logging has a fundamental integrity problem that most teams do not address until it is too late.

Read
Product·10 min read

Event Ledger: Immutable Compliance Records for Business Events

Logs can be edited. Databases can be modified. The Event Ledger is different, every event is hashed with SHA-256, signed with Ed25519, and stored in an append-only ledger that cannot be altered after ingestion.

Read
Compliance·12 min read

SOC 2 Compliance: The Complete Guide for Modern Organizations

SOC 2 has become the baseline trust standard for SaaS companies and service providers. This guide covers the trust service criteria, audit types, preparation strategies, and how verifiable evidence closes the gap between controls and proof.

Read
Compliance·11 min read

HIPAA Compliance: The Guide for Technology Organizations

HIPAA governs how protected health information is handled across healthcare and technology. This guide covers what technology organizations need to know about HIPAA requirements, common pitfalls, and how verifiable evidence strengthens compliance posture.

Read

How to Export Audit Logs for Enterprise Customers: Signed, Verifiable, Audit-Ready

A practical guide to exporting audit logs for enterprise customers and their auditors. Learn how to paginate a signed audit log API, verify every record's Ed25519 signature, prove the export is complete with an integrity scan, and set retention windows, so the export is cryptographic proof, not a CSV anyone has to trust.

Category: Compliance. Published 2026-06-22 by Adeola Okunola, Founder, Invoance. Tags: Audit Logs, Audit Log Export, Enterprise, Compliance, SIEM, Tamper Evidence, Ed25519, Audit Trail, API.

Invoance

Neutral digital proof infrastructure for business. Tamper-evident, independently verifiable records.

Subscribe to our newsletter

Products
Platform
How It Works
Developers
Verify
Resources
Help & Legal
Products
  • Event Ledger
  • Document Anchoring
  • AI Attestation
  • Traces
Platform
  • Why Invoance
  • For Compliance Teams
  • For Finance Teams
  • Pricing
How It Works
  • Overview
  • Event Ledger
  • Document Anchoring
  • AI Attestation
Developers
  • Overview
  • Endpoints
  • Authentication
  • Concepts
Verify
  • Verify Document
  • Verify AI Attestation
  • Verify Event
  • Verify Trace
Resources
  • All Resources
  • SOC 2 Guide
  • HIPAA Guide
  • ISO 27001 Guide
Help & Legal
  • Support
  • Status
  • Verification Help
  • FAQ

Invoance provides technical verification and proof infrastructure for digital records. Invoance does not issue legal, financial, or regulatory advice.

Records anchored through Invoance are cryptographically signed and tamper-evident by design. Invoance does not verify the accuracy, legality, or authenticity of document contents, only that a record existed in a specific form at a specific time. Verification links are publicly resolvable and do not require authentication. Invoance does not act as a custodian of funds, a legal authority, or a regulated financial entity. Use of Invoance does not constitute legal compliance. Consult qualified counsel for your specific obligations.

© 2025 – 2026 Invoance, Inc. All rights reserved.••