Home
Home/Developers/Audit logs/Proof
Ed25519 signaturesSHA-256 hashes8 SDKs
Status
Sign inStart free
Home
Start here
Overview
Authentication
Errors
FAQ
API
Events
Canonical JSON and hashes
Documents
Anchoring a file
AI attestations
Attestation schemaVerifying attestations
Traces
Sealing a trace
Audit logs
OrganizationsStreamsPortalPublic proofEvent schemaExporting eventsIntegrationsEmbeddable viewer
All endpoints
Reference
SDKs
Verification
API · Audit Logs

Audit public proof

The two public endpoints behind /proof/audit: no key, the signed envelope and a verify call for the copy someone holds.

Audit Logs overview
sha-256 · 3a352297…2592
sha-256 · bae251a9…8ffa
On this page
GET/v1/proof/audit/{event_id}Get the public proof of an audit eventPOST/v1/proof/audit/{event_id}/verifyVerify a copy of an audit event
Endpoints
GET/v1/proof/audit/{event_id}
Public

Get the public proof of an audit event

Returns the issuer, the org, the event's envelope fields and a server-computed verification verdict, without the event's actor, targets, context or metadata.

Path parameters
event_idstring · required

The aevt_ id of the event.

  • The event's actor, targets, context and metadata are read to recompute the canonical bytes but are never serialized into the response; only the holder of a full copy can check content, via the verify route below.
  • The lookup is by id across all tenants; the id itself is the only access control, and an unknown id gets 404 event_not_found.
  • The route sits behind a limiter configured for 2 requests per second and 10 per minute, but that limiter keys on a request extension unauthenticated calls do not carry, so it passes public traffic through.
  • Events that left the hot log for cold storage return 404 here.
Node.js
// No API key: the public proof endpoint is unauthenticated.
const eventId = "aevt_01J0Y1Z2A3B4C5D6E7F8G9H0JK";

const res = await fetch("https://api.invoance.com/v1/proof/audit/" + eventId);
if (!res.ok) {
  throw new Error(res.status + " " + (await res.text()));
}
const proof = await res.json();
console.log(proof.organization.issuer_name, proof.event.action, proof.event.seq);
console.log(proof.verification.valid, proof.verification.reason);
Response · 200
{
  "organization": {
    "name": "Northwind Legal",
    "issuer_name": "Northwind Legal Ltd",
    "primary_domain": "northwindlegal.example",
    "domain_verified": true,
    "logo_url": "https://cdn.example.com/northwind/logo.svg"
  },
  "org": {
    "org_id": "aorg_01J0XW9K3RQ5T7V8Y2C4E6G8HM",
    "name": "Acme Robotics"
  },
  "event": {
    "event_id": "aevt_01J0Y1Z2A3B4C5D6E7F8G9H0JK",
    "org_id": "aorg_01J0XW9K3RQ5T7V8Y2C4E6G8HM",
    "seq": 42,
    "schema_id": "invoance.audit/1",
    "action": "user.signed_in",
    "occurred_at": "2026-09-22T08:14:07.000Z",
    "ingested_at": "2026-09-22T08:14:07.312Z",
    "payload_hash": "df929158c7ce2107eff769fbcd58376c1d84dee0b5212b314f6f423dd20534d3",
    "signature": "c6a2bf3bc3895915ead5f0b99eaf84534d4e8b9aa68bef8b0508cfd473f06e694d76d2bc330b83cfa613e49e7d2490d0413285017acfb78746bcc1524d05160d",
    "signing_public_key": "bee215a9d2a0170176a88733056d8a0ae5372b0da8238796bef4a0b75d4c0974"
  },
  "verification": {
    "valid": true,
    "reason": null,
    "schema": "invoance.audit/1",
    "payload_hash": "df929158c7ce2107eff769fbcd58376c1d84dee0b5212b314f6f423dd20534d3",
    "key_source": "tenant_keys"
  }
}
Response fields
organizationobject

The tenant that recorded the event, from its organization profile.

organization.namestring

Organization name.

organization.issuer_namestring

Name shown as the issuer on proof pages.

organization.primary_domainstring

Primary domain registered for the organization.

organization.domain_verifiedboolean

True when the primary domain passed DNS verification.

organization.logo_urlstring

Logo URL, or null when none is set.

orgobject

The audit org the event belongs to.

org.org_idstring

The aorg_ id.

org.namestring

The org's display name, or null.

eventobject

Envelope fields only; the content fields are used for the verdict but never returned.

event.event_idstring

The aevt_ id.

event.org_idstring

The aorg_ id.

event.seqinteger

Position in the org's log.

event.schema_idstring

Always invoance.audit/1. One of invoance.audit/1.

event.actionstring

The action string.

event.occurred_attimestamp (ISO 8601)

Canonical occurred_at, UTC with three fractional digits.

event.ingested_attimestamp (ISO 8601)

Canonical ingested_at.

event.payload_hashhex

Stored SHA-256 of the canonical bytes.

event.signaturehex

Stored Ed25519 signature.

event.signing_public_keyhex

Key recorded with the row, for display; the verdict uses tenant_keys.

verificationobject

Verdict computed on the server when the call is made.

verification.validboolean

True when hash, domain tag and signature all check out against the tenant's registered key.

verification.reasonstring

First failed check, or null. One of canonicalization_failed, payload_hash_mismatch, wrong_domain, signature_invalid.

verification.schemastring

Always invoance.audit/1. One of invoance.audit/1.

verification.payload_hashhex

Recomputed hash; equals event.payload_hash when the row is intact.

verification.key_sourcestring

Always tenant_keys. One of tenant_keys.

Errors
event_not_found404

No audit event with that id exists in any tenant.

db_error500

A database query failed.

key_unavailable500

The event's tenant has no registered key in tenant_keys.

POST/v1/proof/audit/{event_id}/verify
Public

Verify a copy of an audit event

Takes the full event JSON you hold, rebuilds its canonical bytes on the server, and reports whether it reproduces the anchored hash and whether the anchored signature verifies over it.

Headers
Content-Typestring · required

application/json; the body is parsed as JSON regardless of the header.

Path parameters
event_idstring · required

The aevt_ id of the anchored event to compare against.

Request body
eventobject

The event as returned by get, list, the portal, a stream delivery or an export; the bare event object is also accepted as the whole body.

event.idstring · required

The aevt_ id; copied to event_id for the recompute when event_id is absent.

event.org_idstring · required

The aorg_ id, part of the signed bytes.

event.seqinteger · required

Part of the signed bytes.

event.ingested_attimestamp (ISO 8601) · required

Part of the signed bytes; any RFC 3339 form is normalized.

event.actionstring · required

Part of the signed bytes.

event.occurred_attimestamp (ISO 8601) · required

Part of the signed bytes; any RFC 3339 form is normalized.

event.actorobject · required

Part of the signed bytes.

event.targetsobject[] · required

Part of the signed bytes.

event.contextobject

Part of the signed bytes when present and not null.

event.metadataobject

Part of the signed bytes when present and not null; null values inside are dropped.

  • Extra keys on the copy (payload_hash, signature, signing_public_key, schema_id) are ignored; only the signed fields feed the recompute, so a copy from any surface verifies as long as its signed fields are unchanged.
  • A copy that cannot be canonicalized (missing required field, float in metadata) is reported as a failed check with reason canonicalization_failed, not as a 4xx.
  • The submitted copy is used only for the recompute; it is neither stored nor echoed back.
  • Any RFC 3339 form of the timestamps is accepted because they are normalized before hashing, so a copy whose timestamps were reformatted still matches.
  • None of the SDKs call this route; they verify offline instead with verifyAuditEvent / verify_audit_event, which implements the same canonicalization.
Node.js
import { readFile } from "node:fs/promises";

// No API key: the public verify endpoint is unauthenticated.
// event.json holds the event exactly as get, an export or a stream delivered it.
const event = JSON.parse(await readFile("event.json", "utf8"));

const res = await fetch("https://api.invoance.com/v1/proof/audit/" + event.id + "/verify", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ event }),
});
if (!res.ok) {
  throw new Error(res.status + " " + (await res.text()));
}
const result = await res.json();
console.log(result.match_result, result.signature_valid, result.reason);
Response · 200
{
  "event_id": "aevt_01J0Y1Z2A3B4C5D6E7F8G9H0JK",
  "match_result": true,
  "signature_valid": true,
  "reason": null,
  "anchored_hash": "df929158c7ce2107eff769fbcd58376c1d84dee0b5212b314f6f423dd20534d3",
  "submitted_hash": "df929158c7ce2107eff769fbcd58376c1d84dee0b5212b314f6f423dd20534d3",
  "anchored_at": "2026-09-22T08:14:07.312+00:00",
  "schema": "invoance.audit/1",
  "key_source": "tenant_keys"
}
Response fields
event_idstring

The id from the path.

match_resultboolean

True when the submitted copy's canonical hash equals the anchored payload_hash.

signature_validboolean

True when the bytes carry the audit domain tag and the anchored signature verifies over the submitted canonical bytes under the tenant's registered key.

reasonstring

First failed check, or null when both flags are true. One of canonicalization_failed, payload_hash_mismatch, wrong_domain, signature_invalid.

anchored_hashhex

The payload_hash stored for the event.

submitted_hashhex

SHA-256 of the canonical bytes rebuilt from your copy; empty when it could not be canonicalized.

anchored_attimestamp (ISO 8601)

The stored ingested_at, written with a +00:00 offset.

schemastring

Always invoance.audit/1. One of invoance.audit/1.

key_sourcestring

Always tenant_keys. One of tenant_keys.

Errors
invalid_json400

The body is not valid JSON or contains a duplicate key in any object.

invalid_event400

Neither the body nor its event key is a JSON object.

event_not_found404

No audit event with that id exists in any tenant.

db_error500

A database query failed.

key_unavailable500

The event's tenant has no registered key in tenant_keys.

Other audit logs endpointsOverview
EventsPOST/v1/audit/eventsIngest an audit eventGET/v1/audit/eventsList audit eventsGET/v1/audit/events/{id}Get an audit eventGET/v1/audit/events/{id}/verifyVerify an audit event
OrganizationsPOST/v1/audit/orgsCreate an audit orgGET/v1/audit/orgsList audit orgsPATCH/v1/audit/orgs/{id}Rename an audit orgDELETE/v1/audit/orgs/{id}Delete an audit orgPOST/v1/audit/orgs/{id}/archiveArchive an audit orgPOST/v1/audit/orgs/{id}/unarchiveUnarchive an audit orgGET/v1/audit/orgs/{id}/integrityCheck an org's sequence integrityPUT/v1/audit/orgs/{id}/retentionSet an org's retention
StreamsPOST/v1/audit/orgs/{id}/streamsCreate a webhook streamGET/v1/audit/orgs/{id}/streamsList an org's streamsDELETE/v1/audit/orgs/{id}/streams/{stream_id}Delete a streamPOST/v1/audit/orgs/{id}/streams/{stream_id}/testSend a test delivery
Portal sessionsPOST/v1/audit/portal_sessionsCreate a portal session
ExportsPOST/v1/audit/exportsCreate an exportGET/v1/audit/exports/{id}Get an export
Portal, with a portal tokenPOST/v1/audit/portal/exchangeExchange a portal link tokenGET/v1/audit/portal/eventsList events through the portalGET/v1/audit/portal/events/{id}Get an event through the portalGET/v1/audit/portal/events/{id}/verifyVerify an event through the portalGET/v1/audit/portal/orgGet the portal's org and issuerGET/v1/audit/portal/streamsList streams through the portalPOST/v1/audit/portal/streamsCreate a stream through the portalDELETE/v1/audit/portal/streams/{id}Delete a stream through the portalPOST/v1/audit/portal/streams/{id}/testTest a stream through the portal

Proof infrastructure. Records are hashed, signed with your organization's Ed25519 key, and stored append-only, so anyone can check them later.

Products

  • Audit Logs
  • Event Ledger
  • AI Attestation
  • Document Anchoring
  • Traces

Developers

  • Documentation
  • API reference
  • SDKs
  • How it works
  • How traces seal
  • System status

Verify

  • Audit Log
  • Event
  • AI Attestation
  • Document
  • Trace

Company

  • Why Invoance
  • Pricing
  • Security
  • Compliance teams
  • Finance teams
  • Partners
  • Resources
  • Help center
  • Contact
© 2026 Invoance
PrivacyLegal noticeLegal FAQGitHubLinkedInX