Home
Home/Developers/Traces
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 schema
Verifying attestations
Traces
Sealing a trace
Audit logs
Organizations
Streams
Portal
Public proof
Event schema
Exporting events
Integrations
Clerk
Auth0
Embeddable viewer
All endpoints
Reference
SDKs
Node.js
Python
Go
Java
Ruby
Rust
.NET
PHP
REST
Verification
API · Traces

Traces

Group events, anchored documents and AI attestations into one sealed bundle with a composite hash.

sha-256 · 3a352297…2592
sha-256 · e0535c4d…8768

A trace groups the events, anchored documents and AI attestations that belong to one process, such as an invoice run or an onboarding case. You create it open, attach items by passing its trace_id when you ingest an event, anchor a document or ingest an attestation, and seal it when the process is complete. Each item keeps its own hash and signature; the trace adds an ordered bundle on top.

Sealing runs in the background. The worker reads the SHA-256 hash of every item in the trace (payload_hash for events, document_hash for documents, payload_hash for attestations), orders them by their timestamps across all three types, and computes the composite hash as SHA-256 over the concatenated raw hash bytes. It then writes a trace.sealed event to the ledger, signed with the tenant's Ed25519 key, and stores the composite hash, the seal event id and the item counts on the trace. A sealed trace accepts no more items and cannot be deleted.

Once sealed, the proof endpoints return the trace with every item's hash, signature and public key plus the seal event, so anyone holding the bundle can recompute the composite hash and check the signatures against the tenant's public key. The owner can also make the trace visible on the public proof endpoint, which needs no API key.

Endpoints
POST/v1/tracesCreate a traceGET/v1/tracesList tracesGET/v1/traces/{trace_id}Get a traceDELETE/v1/traces/{trace_id}Delete an empty tracePOST/v1/traces/{trace_id}/sealSeal a traceGET/v1/traces/{trace_id}/proofGet the proof bundleGET/v1/traces/{trace_id}/proof/pdfDownload the proof bundle as PDFGET/v1/proof/trace/{trace_id}Get the public proof
Limits
label1 to 255 bytes (UTF-8) after trimming
metadataup to 16 KB (16384 bytes) serialized
open traces per labelone per tenant; a second create with the same label answers 409 duplicate_trace
traces per monththe plan's traces_per_month quota, checked on create
list page size1 to 100, default 25
event page size on get1 to 200, default 50
sealrequires at least one event on the trace
deleteonly an open trace with no events
public proof ratea 2 per second, 10 per minute limiter is configured on the proof routes but keys on a request extension nothing sets, so it does not reject requests
Endpoints
POST/v1/traces
API keywrite

Create a trace

Creates an open trace with a label and optional metadata and returns its id; items join it later by carrying its trace_id.

Headers
Authorizationstring · required

Bearer followed by the API key (invoance_live_...); the same key in an X-API-Key header is accepted instead.

Content-Typestring · required

Must be application/json; any other value is rejected by the framework with a plain-text 415 before the handler runs.

Request body
labelstring · required · 1 to 255 bytes (UTF-8) after trimming

A name for the trace; leading and trailing whitespace is removed before validation and the length is checked in UTF-8 bytes.

metadataobject · up to 16 KB (16,384 bytes) as compact JSON

Any JSON value stored with the trace and returned by GET /v1/traces/{trace_id}; not part of the composite hash.

  • An item joins a trace by carrying trace_id in its own create call: POST /v1/events, POST /v1/document/anchor and POST /v1/ai/attestations each accept an optional trace_id.
  • Those calls check that the trace belongs to the caller's tenant and is open; otherwise they answer 404 trace_not_found, 409 trace_sealed or 409 trace_sealing (events), or 409 trace_not_open (documents and attestations).
  • In the SDKs the ingest parameter is traceId (Node, Java, PHP), TraceID (Go), TraceId (.NET) or trace_id (Python, Ruby, Rust).
  • The trace quota counter is incremented before the duplicate-label check, so a request rejected with duplicate_trace still counts against traces_per_month.
  • The label is unique only among open traces; once a trace is sealed a new open trace may reuse its label.
  • This endpoint does not read an Idempotency-Key header; retrying a successful create with the same label returns 409 duplicate_trace.
  • A body that is not valid JSON or lacks label is rejected by the framework before the handler runs with a plain-text 400 or 422 rather than the JSON error shape.
Node.js
import { InvoanceClient } from "invoance";

// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();

const trace = await client.traces.create({
  label: "Invoice batch 2026-09",
  metadata: { batch_id: "b_4471", region: "eu-west" },
});
console.log(trace.trace_id, trace.status);
Response · 201
{
  "trace_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
  "status": "open",
  "created_at": "2026-09-22T08:14:07Z",
  "label": "Invoice batch 2026-09"
}
Response fields
trace_iduuid

The new trace's id; pass it as trace_id when ingesting items.

statusstring

Always open on creation. One of open.

created_attimestamp (ISO 8601)

When the trace was created.

labelstring

The trimmed label as stored.

Errors
missing_api_key401

Neither an Authorization header nor an X-API-Key header is present.

invalid_authorization_scheme401

The Authorization header is present but does not use the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

No API key with this value exists.

api_key_revoked401

The key exists but has been revoked.

ip_not_allowed403

The key has an IP allowlist and the caller's address is not on it.

api_key_lookup_failed500

The key could not be looked up in the database.

insufficient_scope403

The key does not have the write scope.

invalid_label400

label is empty after trimming or longer than 255 bytes; a body with no label at all is rejected by the framework first.

metadata_too_large400

metadata serializes to more than 16,384 bytes of compact JSON.

payment_required402

The organization is on a paid plan and its subscription is past due.

feature_not_available403

The plan's traces_per_month limit is 0.

quota_exceeded429

The monthly traces_per_month quota is used up for the current billing period.

quota_check_failed500

The plan limit or usage counter could not be read.

duplicate_trace409

An open trace with the same label already exists in the tenant.

trace_create_failed500

The duplicate check or the insert failed.

rate_limited429

The tenant exceeded its per-second (plan rate_limit_per_sec, default 10) or per-minute (60 times that) budget; the body carries retry_after, plan_tier and limit_per_sec and the response sets Retry-After, X-RateLimit-Limit and X-RateLimit-Remaining.

GET/v1/traces
API keyread

List traces

Returns a page of the tenant's traces, newest first, optionally filtered by status.

Headers
Authorizationstring · required

Bearer followed by the API key (invoance_live_...); the same key in an X-API-Key header is accepted instead.

Query parameters
pageinteger · default 1

1-based page number; values below 1 are treated as 1.

limitinteger · 1 to 100 · default 25

Traces per page; values outside the range are clamped.

statusstring

Return only traces with this status. One of open, sealing, sealed.

  • Results are ordered by created_at descending.
  • A key with the write scope passes the read check.
  • The SDK parameter types for status list only open and sealed; the backend also accepts sealing.
  • The SDK list-item models declare a composite_hash field that this endpoint does not return, and omit document_count, attestation_count and source, which it does return; read composite_hash from GET /v1/traces/{trace_id}.
  • A page or limit value that is not a whole number is rejected by the framework with a plain-text 400 before the handler runs.
Node.js
import { InvoanceClient } from "invoance";

// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();

const page = await client.traces.list({ status: "open", page: 1, limit: 25 });
for (const trace of page.traces) {
  console.log(trace.trace_id, trace.label, trace.status);
}
console.log(page.total, page.has_more);
Response · 200
{
  "traces": [
    {
      "trace_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
      "label": "Invoice batch 2026-09",
      "status": "sealed",
      "event_count": 4,
      "document_count": 1,
      "attestation_count": 1,
      "created_at": "2026-09-22T08:14:07Z",
      "sealed_at": "2026-09-22T09:01:54Z",
      "source": "api"
    },
    {
      "trace_id": "0d2b6f4a-8c1e-4b3d-9a5f-6e7c8d9a0b1c",
      "label": "Invoice batch 2026-08",
      "status": "open",
      "event_count": 2,
      "document_count": 0,
      "attestation_count": 0,
      "created_at": "2026-08-25T14:02:19Z",
      "sealed_at": null,
      "source": "dashboard"
    }
  ],
  "page": 1,
  "limit": 25,
  "total": 2,
  "has_more": false
}
Response fields
traces[].trace_iduuid

The trace id.

traces[].labelstring

The label given at creation.

traces[].statusstring

The trace's current status. One of open, sealing, sealed.

traces[].event_countinteger

For a sealed trace, the number of items (events, documents and attestations) hashed at seal time; for an open trace, the live number of events excluding any trace.sealed event.

traces[].document_countinteger

Live count of anchored documents attached to the trace.

traces[].attestation_countinteger

Live count of AI attestations attached to the trace.

traces[].created_attimestamp (ISO 8601)

When the trace was created.

traces[].sealed_attimestamp (ISO 8601)

When the seal completed; null until the trace is sealed.

traces[].sourcestring

How the trace was created. One of api, dashboard.

pageinteger

The page returned.

limitinteger

The page size applied after clamping.

totalinteger

Total traces matching the filter.

has_moreboolean

True when page * limit is below total.

Errors
missing_api_key401

Neither an Authorization header nor an X-API-Key header is present.

invalid_authorization_scheme401

The Authorization header is present but does not use the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

No API key with this value exists.

api_key_revoked401

The key exists but has been revoked.

ip_not_allowed403

The key has an IP allowlist and the caller's address is not on it.

api_key_lookup_failed500

The key could not be looked up in the database.

insufficient_scope403

The key has neither the read nor the write scope.

invalid_status400

status is set to anything other than open, sealing or sealed.

db_error500

A database query failed.

rate_limited429

The tenant exceeded its per-second (plan rate_limit_per_sec, default 10) or per-minute (60 times that) budget; the body carries retry_after, plan_tier and limit_per_sec and the response sets Retry-After, X-RateLimit-Limit and X-RateLimit-Remaining.

GET/v1/traces/{trace_id}
API keyread

Get a trace

Returns one trace with its status, composite hash once sealed, live item counts, a page of its events and all of its documents and attestations.

Headers
Authorizationstring · required

Bearer followed by the API key (invoance_live_...); the same key in an X-API-Key header is accepted instead.

Path parameters
trace_iduuid · required

The trace id returned by POST /v1/traces; a value that is not a UUID is rejected by the framework path extractor with a plain-text 400.

Query parameters
event_pageinteger · default 1

1-based page of the events list; values below 1 are treated as 1.

event_limitinteger · 1 to 200 · default 50

Events per page; values outside the range are clamped.

  • Only events are paginated; documents and attestations are returned in full, ordered by anchored_at and created_at ascending.
  • Once the trace is sealed, the events list and event_total include the trace.sealed seal event (its id is seal_event_id); the list is ordered by ingested_at ascending, so the seal event comes last.
  • Poll this endpoint after POST /v1/traces/{trace_id}/seal: status moves from sealing to sealed and composite_hash, sealed_at and seal_event_id are filled in together.
  • The SDK typed models for this response declare trace_id, label, status, metadata, created_at, sealed_at, composite_hash, seal_event_id, event_count and the events page, but not documents, attestations, item_counts, source or events[].source; the JSON carries them, and Ruby and PHP return the raw hash or array.
  • An event_page or event_limit value that is not a whole number is rejected by the framework with a plain-text 400 before the handler runs.
Node.js
import { InvoanceClient } from "invoance";

// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();

const trace = await client.traces.get("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4", {
  event_page: 1,
  event_limit: 50,
});
console.log(trace.status, trace.composite_hash);
for (const event of trace.events) {
  console.log(event.event_id, event.event_type, event.payload_hash);
}
Response · 200
{
  "trace_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
  "label": "Invoice batch 2026-09",
  "status": "sealed",
  "metadata": {
    "batch_id": "b_4471",
    "region": "eu-west"
  },
  "created_at": "2026-09-22T08:14:07Z",
  "sealed_at": "2026-09-22T09:01:54Z",
  "composite_hash": "e01187fee8acb0969976df6f4cd49164e8443195165355990daa878cf7e12b41",
  "event_count": 4,
  "seal_event_id": "c4d7e8f1-2a3b-4c5d-9e6f-0a1b2c3d4e5f",
  "source": "api",
  "item_counts": {
    "events": 3,
    "documents": 1,
    "attestations": 1
  },
  "events": [
    {
      "event_id": "3f9a2c71-6b0d-4e85-9c12-7d4e8a1b5f60",
      "event_type": "invoice.received",
      "payload_hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
      "ingested_at": "2026-09-22T08:15:31Z",
      "source": "api"
    },
    {
      "event_id": "9b2d4e6f-1a3c-4d5e-8f70-1c2b3a4d5e6f",
      "event_type": "invoice.approved",
      "payload_hash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
      "ingested_at": "2026-09-22T08:22:45Z",
      "source": "api"
    },
    {
      "event_id": "c4d7e8f1-2a3b-4c5d-9e6f-0a1b2c3d4e5f",
      "event_type": "trace.sealed",
      "payload_hash": "3b428d41452a757b5466edc0520d0f3028c0335cfab5c7b6dac47531dbfcf321",
      "ingested_at": "2026-09-22T09:01:54Z",
      "source": "api"
    }
  ],
  "documents": [
    {
      "event_id": "5e8f1a2b-3c4d-4e5f-8a6b-7c8d9e0f1a2b",
      "document_ref": "INV-2026-0441.pdf",
      "document_hash": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
      "event_type": "document.anchored",
      "anchored_at": "2026-09-22T08:19:02Z"
    }
  ],
  "attestations": [
    {
      "attestation_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
      "attestation_type": "output",
      "model_provider": "openai",
      "model_name": "gpt-4.1",
      "payload_hash": "bef6d7205ca5b97289bdb4cdb60a6de5b1fd8a2e1e5a9b663f1cdea305b644ab",
      "created_at": "2026-09-22T08:26:10Z"
    }
  ],
  "event_page": 1,
  "event_limit": 50,
  "event_total": 3,
  "event_has_more": false
}
Response fields
trace_iduuid

The trace id.

labelstring

The label given at creation.

statusstring

The trace's current status. One of open, sealing, sealed.

metadataobject

The metadata given at creation, or null.

created_attimestamp (ISO 8601)

When the trace was created.

sealed_attimestamp (ISO 8601)

When the seal completed; null until the trace is sealed.

composite_hashhex

SHA-256 over the concatenated raw item hashes in timestamp order, 64 hex characters; null until the trace is sealed.

event_countinteger

For a sealed trace, the number of items hashed at seal time; for an open trace, event_total plus the number of documents and attestations.

seal_event_iduuid

Id of the trace.sealed event written at seal time; null until sealed.

sourcestring

How the trace was created. One of api, dashboard.

item_counts.eventsinteger

Live count of events with this trace_id, including the trace.sealed event once sealed.

item_counts.documentsinteger

Live count of anchored documents with this trace_id.

item_counts.attestationsinteger

Live count of AI attestations with this trace_id.

events[].event_iduuid

The event id.

events[].event_typestring

The event type given at ingest, or trace.sealed for the seal event.

events[].payload_hashhex

SHA-256 of the event payload, 64 hex characters.

events[].ingested_attimestamp (ISO 8601)

When the event was ingested; the events list is ordered by this ascending.

events[].sourcestring

How the event was created. One of api, dashboard.

documents[].event_iduuid

The document anchor's event id.

documents[].document_refstring

The reference given when the document was anchored.

documents[].document_hashhex

SHA-256 of the document, 64 hex characters.

documents[].event_typestring

The event type given when the document was anchored.

documents[].anchored_attimestamp (ISO 8601)

When the document was anchored; ordered ascending.

attestations[].attestation_iduuid

The attestation id.

attestations[].attestation_typestring

The attestation type given at ingest.

attestations[].model_providerstring

The model provider given at ingest, or null.

attestations[].model_namestring

The model name given at ingest.

attestations[].payload_hashhex

SHA-256 of the attestation's canonical request bytes, 64 hex characters; the same value as its attestation_hash.

attestations[].created_attimestamp (ISO 8601)

When the attestation was created; ordered ascending.

event_pageinteger

The event page returned.

event_limitinteger

The event page size applied after clamping.

event_totalinteger

Total events with this trace_id, including the trace.sealed event once sealed.

event_has_moreboolean

True when more event pages follow.

Errors
missing_api_key401

Neither an Authorization header nor an X-API-Key header is present.

invalid_authorization_scheme401

The Authorization header is present but does not use the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

No API key with this value exists.

api_key_revoked401

The key exists but has been revoked.

ip_not_allowed403

The key has an IP allowlist and the caller's address is not on it.

api_key_lookup_failed500

The key could not be looked up in the database.

insufficient_scope403

The key has neither the read nor the write scope.

trace_not_found404

No trace with this id exists in the caller's tenant.

db_error500

A database query failed.

rate_limited429

The tenant exceeded its per-second (plan rate_limit_per_sec, default 10) or per-minute (60 times that) budget; the body carries retry_after, plan_tier and limit_per_sec and the response sets Retry-After, X-RateLimit-Limit and X-RateLimit-Remaining.

DELETE/v1/traces/{trace_id}
API keywrite

Delete an empty trace

Deletes an open trace that has no events and returns its id with deleted set to true.

Headers
Authorizationstring · required

Bearer followed by the API key (invoance_live_...); the same key in an X-API-Key header is accepted instead.

Path parameters
trace_iduuid · required

The trace id returned by POST /v1/traces; a value that is not a UUID is rejected by the framework path extractor with a plain-text 400.

  • A trace is deletable only while its status is open and no event references it; sealed traces cannot be deleted at all.
  • The emptiness check counts events only; anchored documents and attestations attached to the trace are not counted, so an open trace holding only documents or attestations can still be deleted.
  • The row is removed from the traces table; ledger rows are never deleted.
Node.js
import { InvoanceClient } from "invoance";

// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();

const result = await client.traces.delete("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4");
console.log(result.trace_id, result.deleted);
Response · 200
{
  "trace_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
  "deleted": true
}
Response fields
trace_iduuid

The id of the deleted trace.

deletedboolean

Always true on success.

Errors
missing_api_key401

Neither an Authorization header nor an X-API-Key header is present.

invalid_authorization_scheme401

The Authorization header is present but does not use the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

No API key with this value exists.

api_key_revoked401

The key exists but has been revoked.

ip_not_allowed403

The key has an IP allowlist and the caller's address is not on it.

api_key_lookup_failed500

The key could not be looked up in the database.

insufficient_scope403

The key does not have the write scope.

trace_not_found404

No trace with this id exists in the caller's tenant.

trace_not_open409

The trace is sealed or currently sealing.

trace_has_events409

At least one event carries this trace_id; seal the trace instead.

db_error500

A database query failed.

rate_limited429

The tenant exceeded its per-second (plan rate_limit_per_sec, default 10) or per-minute (60 times that) budget; the body carries retry_after, plan_tier and limit_per_sec and the response sets Retry-After, X-RateLimit-Limit and X-RateLimit-Remaining.

POST/v1/traces/{trace_id}/seal
API keywrite

Seal a trace

Moves an open trace to sealing and queues the seal job, which computes the composite hash, writes a signed trace.sealed event and marks the trace sealed.

Headers
Authorizationstring · required

Bearer followed by the API key (invoance_live_...); the same key in an X-API-Key header is accepted instead.

Path parameters
trace_iduuid · required

The trace id returned by POST /v1/traces; a value that is not a UUID is rejected by the framework path extractor with a plain-text 400.

  • Status transitions: open to sealing in this request (a single conditional update, so two concurrent seal calls cannot both succeed), then sealing to sealed by the worker; if the worker gives up or the job cannot be queued, the status is put back to open and the call can be repeated.
  • The request needs at least one event on the trace; documents and attestations alone do not satisfy the check even though the worker includes them in the hash.
  • What is sealed: the worker locks the trace row, reads payload_hash of every event, document_hash of every document and payload_hash of every attestation carrying this trace_id, and orders the three sets together by ingested_at, anchored_at and created_at ascending.
  • Composite hash rule: SHA-256 over the raw 32-byte hashes concatenated in that order, so composite_hash = SHA-256(h1 || h2 || ... || hN) with hashes as bytes, not hex text.
  • The worker then inserts a trace.sealed event into the ledger with payload {trace_id, composite_hash, event_count, sealed_at} (sealed_at written as RFC 3339 with a +00:00 offset), signs the canonical record {v: 1, event_id, trace_id, tenant_id, event_type, composite_hash, event_count, sealed_at} with the tenant's Ed25519 key, and stores it with retention_policy indefinite and source api (dashboard when the seal was requested without an API key).
  • In the same transaction the trace gets status sealed, sealed_at, seal_event_id, composite_hash, event_count (the number of hashed items across all three types) and item_counts.
  • While the status is sealing or sealed, POST /v1/events, POST /v1/document/anchor and POST /v1/ai/attestations reject the trace_id with 409.
  • The worker retries a failed seal up to 6 times with exponential backoff (500 ms base, 30 s cap), except when the trace is missing or no longer in sealing status, which is not retried; once the attempts are exhausted the status is put back to open.
  • The request body is ignored; the SDKs send an empty JSON object.
Node.js
import { InvoanceClient } from "invoance";

// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();

const traceId = "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4";

const seal = await client.traces.seal(traceId);
console.log(seal.status); // "sealing"

// The seal runs in the background. Poll until the status changes.
let trace = await client.traces.get(traceId);
while (trace.status === "sealing") {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  trace = await client.traces.get(traceId);
}
console.log(trace.status, trace.composite_hash);
Response · 202
{
  "trace_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
  "status": "sealing",
  "message": "Seal initiated. Poll GET /v1/traces/{trace_id} for status."
}
Response fields
trace_iduuid

The trace being sealed.

statusstring

Always sealing when the job was queued. One of sealing.

messagestring

Fixed text telling the caller to poll GET /v1/traces/{trace_id}.

Errors
missing_api_key401

Neither an Authorization header nor an X-API-Key header is present.

invalid_authorization_scheme401

The Authorization header is present but does not use the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

No API key with this value exists.

api_key_revoked401

The key exists but has been revoked.

ip_not_allowed403

The key has an IP allowlist and the caller's address is not on it.

api_key_lookup_failed500

The key could not be looked up in the database.

insufficient_scope403

The key does not have the write scope.

trace_not_found404

No trace with this id exists in the caller's tenant.

trace_not_open409

The trace is already sealed or is currently sealing.

trace_empty400

No event carries this trace_id; the status is put back to open.

encode_failed500

The seal job could not be serialized; the status is left as sealing, unlike the other failure paths.

seal_publish_failed500

The seal job could not be queued; the status is put back to open.

db_error500

A database query failed.

rate_limited429

The tenant exceeded its per-second (plan rate_limit_per_sec, default 10) or per-minute (60 times that) budget; the body carries retry_after, plan_tier and limit_per_sec and the response sets Retry-After, X-RateLimit-Limit and X-RateLimit-Remaining.

GET/v1/traces/{trace_id}/proof
API keyread

Get the proof bundle

Returns a sealed trace as a JSON bundle with every event, document and attestation, their hashes, signatures and public keys, and the seal event.

Headers
Authorizationstring · required

Bearer followed by the API key (invoance_live_...); the same key in an X-API-Key header is accepted instead.

Path parameters
trace_iduuid · required

The trace id returned by POST /v1/traces; a value that is not a UUID is rejected by the framework path extractor with a plain-text 400.

  • Only sealed traces have a bundle; an open or sealing trace answers 409 trace_not_sealed.
  • events excludes the seal event, which is returned separately as seal_event; event_count is the length of events and item_count adds documents and attestations.
  • To verify independently, sort every entry of events (content_hash), documents (document_hash) and attestations (payload_hash) by timestamp, concatenate the hashes as raw bytes and SHA-256 the result; it must equal composite_hash.
  • For attestations, hash payload_raw (the exact canonical request bytes as a string) to reproduce payload_hash; payload is a parsed copy and re-serializing it can change the bytes.
  • verification.composite_hash_valid and verification.all_signatures_valid are written as true by the handler for every bundle it returns; they are not the result of a per-request check.
  • The SDK bundle models declare events, seal_event and verification only; documents, attestations, item_count and source are present in the JSON but not in the typed models. The Node sample casts the object to read them; the Python models are declared with extra="allow", so model_dump() returns them.
  • Event and seal signatures cover a canonical record, not the payload shown here: for an event it is {v, event_id, tenant_id, event_type, payload_hash, request_hash, ingested_at}, for the seal event the record listed under POST /v1/traces/{trace_id}/seal; the exact signed bytes are returned hex-encoded as signed_payload by GET /v1/proof/event/{event_id} for any ledger event, including seal_event.event_id.
Node.js
import { createHash } from "node:crypto";
import { InvoanceClient } from "invoance";

// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();

const bundle = await client.traces.proof("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4");

// The SDK's TraceProofBundle type declares events and seal_event only; the
// JSON also carries documents and attestations, so read them off the raw object.
const raw = bundle as unknown as {
  events: { timestamp: string; content_hash: string }[];
  documents: { timestamp: string; document_hash: string }[];
  attestations: { timestamp: string; payload_hash: string }[];
};

// Recompute the composite hash: SHA-256 over the raw item hashes in
// timestamp order across events, documents and attestations.
const items = [
  ...raw.events.map((e) => ({ at: e.timestamp, hash: e.content_hash })),
  ...raw.documents.map((d) => ({ at: d.timestamp, hash: d.document_hash })),
  ...raw.attestations.map((a) => ({ at: a.timestamp, hash: a.payload_hash })),
].sort((a, b) => a.at.localeCompare(b.at));

const hasher = createHash("sha256");
for (const item of items) hasher.update(Buffer.from(item.hash, "hex"));
const recomputed = hasher.digest("hex");

console.log(bundle.composite_hash);
console.log(recomputed === bundle.composite_hash ? "composite hash matches" : "mismatch");
Response · 200
{
  "version": "1.0",
  "trace_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
  "label": "Invoice batch 2026-09",
  "tenant_domain": "acme.example",
  "status": "sealed",
  "source": "api",
  "created_at": "2026-09-22T08:14:07Z",
  "sealed_at": "2026-09-22T09:01:54Z",
  "composite_hash": "e01187fee8acb0969976df6f4cd49164e8443195165355990daa878cf7e12b41",
  "event_count": 2,
  "item_count": 4,
  "events": [
    {
      "event_id": "3f9a2c71-6b0d-4e85-9c12-7d4e8a1b5f60",
      "event_type": "invoice.received",
      "payload": {
        "invoice_number": "INV-2026-0441",
        "amount_cents": 128500,
        "currency": "EUR"
      },
      "content_hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
      "timestamp": "2026-09-22T08:15:31Z",
      "signature": "b7c1d4e9f2a5068397c0d3e6f1a4b58d2e4f6a8c0b1d3f5e7a9c1b3d5f7e9a0c4d6e8f0a2b4c6d8e1f3a5b7c9d0e2f4a6b8c0d2e4f6a8b1c3d5e7f9a0b2c4d6e",
      "public_key": "1f8e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0"
    },
    {
      "event_id": "9b2d4e6f-1a3c-4d5e-8f70-1c2b3a4d5e6f",
      "event_type": "invoice.approved",
      "payload": {
        "invoice_number": "INV-2026-0441",
        "approved_by": "finance_lead"
      },
      "content_hash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
      "timestamp": "2026-09-22T08:22:45Z",
      "signature": "3e5f7a9c1b2d4f6e8a0c2e4f6a8b0d1f5c7e9a1b3d5f7a9c0e2f4a6b8d0f2a4c7e9b1d3f5a7c9e0b2d4f6a8c1e3f5a7b9d0f2a4c6e8b1d3f5a7c9e0b2d4f6a8c",
      "public_key": "1f8e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0"
    }
  ],
  "documents": [
    {
      "document_id": "5e8f1a2b-3c4d-4e5f-8a6b-7c8d9e0f1a2b",
      "document_ref": "INV-2026-0441.pdf",
      "document_hash": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
      "event_type": "document.anchored",
      "signature": "9a0b2c4d6e8f1a3b5c7d9e0f2a4b6c8d1e3f5a7b9c0d2e4f6a8b1c3d5e7f9a0b2c4d6e8f1a3b5c7d9e0f2a4b6c8d1e3f5a7b9c0d2e4f6a8b1c3d5e7f9a0b2c4d",
      "public_key": "1f8e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0",
      "timestamp": "2026-09-22T08:19:02Z"
    }
  ],
  "attestations": [
    {
      "attestation_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
      "attestation_type": "output",
      "model_name": "gpt-4.1",
      "model_provider": "openai",
      "payload": {
        "type": "output",
        "payload": {
          "input": "Summarize invoice INV-2026-0441",
          "output": "Invoice for 1,285.00 EUR, due 2026-10-22."
        },
        "context": {
          "model_provider": "openai",
          "model_name": "gpt-4.1",
          "model_version": "2026-04-14"
        },
        "subject": null,
        "trace_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4"
      },
      "payload_raw": "{\"type\":\"output\",\"payload\":{\"input\":\"Summarize invoice INV-2026-0441\",\"output\":\"Invoice for 1,285.00 EUR, due 2026-10-22.\"},\"context\":{\"model_provider\":\"openai\",\"model_name\":\"gpt-4.1\",\"model_version\":\"2026-04-14\"},\"subject\":null,\"trace_id\":\"7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4\"}",
      "payload_hash": "bef6d7205ca5b97289bdb4cdb60a6de5b1fd8a2e1e5a9b663f1cdea305b644ab",
      "signature": "5c7e9a1b3d5f7a9c0e2f4a6b8d0f2a4c7e9b1d3f5a7c9e0b2d4f6a8c1e3f5a7b9d0f2a4c6e8b1d3f5a7c9e0b2d4f6a8c3e5f7a9c1b2d4f6e8a0c2e4f6a8b0d1f",
      "public_key": "1f8e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0",
      "timestamp": "2026-09-22T08:26:10Z"
    }
  ],
  "seal_event": {
    "event_id": "c4d7e8f1-2a3b-4c5d-9e6f-0a1b2c3d4e5f",
    "event_type": "trace.sealed",
    "content_hash": "3b428d41452a757b5466edc0520d0f3028c0335cfab5c7b6dac47531dbfcf321",
    "timestamp": "2026-09-22T09:01:54Z",
    "signature": "e1f3a5b7c9d0f2a4b6c8d1e3f5a7b9c0d2e4f6a8b1c3d5e7f9a0b2c4d6e8f1a3b5c7d9e0f2a4b6c8d1e3f5a7b9c0d2e4f6a8b1c3d5e7f9a0b2c4d6e8f1a3b5c7",
    "public_key": "1f8e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0"
  },
  "verification": {
    "composite_hash_valid": true,
    "all_signatures_valid": true
  }
}
Response fields
versionstring

Bundle format version, currently 1.0.

trace_iduuid

The trace id.

labelstring

The label given at creation.

tenant_domainstring

The organization's primary domain, or null when none is set.

statusstring

Always sealed; other statuses are rejected with 409. One of sealed.

sourcestring

How the trace was created. One of api, dashboard.

created_attimestamp (ISO 8601)

When the trace was created.

sealed_attimestamp (ISO 8601)

When the seal completed.

composite_hashhex

SHA-256 over the concatenated raw item hashes in timestamp order, 64 hex characters.

event_countinteger

Number of entries in events; the seal event is not counted.

item_countinteger

Number of entries in events, documents and attestations combined.

events[].event_iduuid

The event id.

events[].event_typestring

The event type given at ingest.

events[].payloadobject

The stored event payload.

events[].content_hashhex

SHA-256 of the event payload, 64 hex characters; this is the value that enters the composite hash.

events[].timestamptimestamp (ISO 8601)

When the event was ingested; ordered ascending.

events[].signaturehex

Ed25519 signature stored with the event, 128 hex characters; empty when the row has none.

events[].public_keyhex

Ed25519 public key that signed the event, 64 hex characters.

documents[].document_iduuid

The document anchor's event id.

documents[].document_refstring

The reference given when the document was anchored.

documents[].document_hashhex

SHA-256 of the document, 64 hex characters; this is the value that enters the composite hash.

documents[].event_typestring

The event type given when the document was anchored.

documents[].signaturehex

Ed25519 signature stored with the anchor, 128 hex characters.

documents[].public_keyhex

Ed25519 public key that signed the anchor, 64 hex characters.

documents[].timestamptimestamp (ISO 8601)

When the document was anchored; ordered ascending.

attestations[].attestation_iduuid

The attestation id.

attestations[].attestation_typestring

The attestation type given at ingest.

attestations[].model_namestring

The model name given at ingest.

attestations[].model_providerstring

The model provider given at ingest, or null.

attestations[].payloadobject

payload_raw parsed into an object, for reading; omitted when the stored bytes could not be fetched.

attestations[].payload_rawstring

The exact canonical request bytes stored at ingest, as a JSON string; SHA-256 of this string reproduces payload_hash; omitted when unavailable.

attestations[].payload_hashhex

SHA-256 of the attestation's canonical request bytes, 64 hex characters, the same value as its attestation_hash; this is the value that enters the composite hash.

attestations[].signaturehex

Ed25519 signature stored with the attestation, 128 hex characters.

attestations[].public_keyhex

Ed25519 public key that signed the attestation, 64 hex characters.

attestations[].timestamptimestamp (ISO 8601)

When the attestation was created; ordered ascending.

seal_event.event_iduuid

Id of the trace.sealed event.

seal_event.event_typestring

Always trace.sealed. One of trace.sealed.

seal_event.content_hashhex

SHA-256 of the seal payload as compact JSON with the keys trace_id, composite_hash, event_count and sealed_at in that order, 64 hex characters.

seal_event.timestamptimestamp (ISO 8601)

When the seal event was written; equals sealed_at.

seal_event.signaturehex

Ed25519 signature over the canonical seal record, 128 hex characters.

seal_event.public_keyhex

Ed25519 public key that signed the seal, 64 hex characters.

verification.composite_hash_validboolean

Set to true by the handler when it returns a bundle; it is not recomputed per request.

verification.all_signatures_validboolean

Set to true by the handler when it returns a bundle; it is not recomputed per request.

Errors
missing_api_key401

Neither an Authorization header nor an X-API-Key header is present.

invalid_authorization_scheme401

The Authorization header is present but does not use the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

No API key with this value exists.

api_key_revoked401

The key exists but has been revoked.

ip_not_allowed403

The key has an IP allowlist and the caller's address is not on it.

api_key_lookup_failed500

The key could not be looked up in the database.

insufficient_scope403

The key has neither the read nor the write scope.

trace_not_found404

No trace with this id exists in the caller's tenant.

trace_not_sealed409

The trace's status is open or sealing.

seal_event_missing500

The trace is sealed but the row for its seal_event_id could not be read.

db_error500

A database query failed.

rate_limited429

The tenant exceeded its per-second (plan rate_limit_per_sec, default 10) or per-minute (60 times that) budget; the body carries retry_after, plan_tier and limit_per_sec and the response sets Retry-After, X-RateLimit-Limit and X-RateLimit-Remaining.

GET/v1/traces/{trace_id}/proof/pdf
API keyread

Download the proof bundle as PDF

Renders the sealed trace's proof bundle as a PDF and returns it as a file attachment.

Headers
Authorizationstring · required

Bearer followed by the API key (invoance_live_...); the same key in an X-API-Key header is accepted instead.

Path parameters
trace_iduuid · required

The trace id returned by POST /v1/traces; a value that is not a UUID is rejected by the framework path extractor with a plain-text 400.

  • The PDF lists the events in ingested_at order with the seal event set apart, then the documents and the attestations; attestation payloads are printed when the stored bytes can be fetched.
  • The same renderer produces the dashboard export, so the two files match.
  • Error responses on this endpoint are JSON with the usual {error, message} shape even though the success body is a PDF.
Node.js
import { writeFileSync } from "node:fs";
import { InvoanceClient } from "invoance";

// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();

const pdf = await client.traces.proofPdf("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4");
writeFileSync("trace-proof.pdf", Buffer.from(pdf));
console.log("wrote trace-proof.pdf", pdf.byteLength, "bytes");
Response · 200
"<binary PDF body>"
Response fields
(body)string

Raw PDF bytes; the response sets Content-Type application/pdf, Content-Disposition attachment with filename trace-{trace_id}-proof.pdf, and Cache-Control no-store.

Errors
missing_api_key401

Neither an Authorization header nor an X-API-Key header is present.

invalid_authorization_scheme401

The Authorization header is present but does not use the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

No API key with this value exists.

api_key_revoked401

The key exists but has been revoked.

ip_not_allowed403

The key has an IP allowlist and the caller's address is not on it.

api_key_lookup_failed500

The key could not be looked up in the database.

insufficient_scope403

The key has neither the read nor the write scope.

trace_not_found404

No trace with this id exists in the caller's tenant.

trace_not_sealed409

The trace's status is open or sealing.

pdf_generation_failed500

The renderer could not produce the document.

db_error500

A database query failed.

rate_limited429

The tenant exceeded its per-second (plan rate_limit_per_sec, default 10) or per-minute (60 times that) budget; the body carries retry_after, plan_tier and limit_per_sec and the response sets Retry-After, X-RateLimit-Limit and X-RateLimit-Remaining.

GET/v1/proof/trace/{trace_id}
Public

Get the public proof

Returns a sealed trace that its owner has made public, with every item's hash, signature and public key, without an API key.

Path parameters
trace_iduuid · required

The trace id returned by POST /v1/traces; a value that is not a UUID is rejected by the framework path extractor with a plain-text 400.

  • This is the public counterpart of GET /v1/traces/{trace_id}/proof: same items and hashes, no API key, and only for traces whose owner switched on public verification in the dashboard (the traces.public flag, settable on sealed traces only).
  • Attestation payload bytes are included only when the owner also enabled ai_public; otherwise each attestation carries its payload_hash, signature and public key but no payload.
  • The seal event is returned in seal_event with the seal payload {trace_id, composite_hash, event_count, sealed_at} as its payload; events holds the other events in ingested_at order.
  • Timestamps on this endpoint are RFC 3339 strings with microseconds and a +00:00 offset, not the Z form used by the API key endpoints.
  • Each successful call records a view in trace_proof_view_events with source public_url, the caller's IP address, user agent and country.
  • A limiter configured for 2 requests per second and 10 per minute is layered on the proof routes, but it reads its per-caller key from a request extension that no middleware sets, so it passes every request through and this endpoint does not answer 429.
  • Field names differ from the API key bundle: documents[].event_id instead of document_id, and ingested_at, anchored_at and created_at instead of timestamp.
  • No SDK wraps this endpoint; call it with any HTTP client.
Node.js
// No API key: the public proof endpoint is unauthenticated.
const traceId = "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4";

const res = await fetch("https://api.invoance.com/v1/proof/trace/" + traceId);
if (!res.ok) {
  throw new Error(res.status + " " + (await res.text()));
}
const proof = await res.json();
console.log(proof.issuer_name, proof.composite_hash);
console.log(proof.events.length, "events", proof.documents.length, "documents", proof.attestations.length, "attestations");
Response · 200
{
  "version": "1.0",
  "trace_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
  "label": "Invoice batch 2026-09",
  "issuer_name": "Acme Finance GmbH",
  "tenant_domain": "acme.example",
  "domain_verified": true,
  "status": "sealed",
  "created_at": "2026-09-22T08:14:07.482913+00:00",
  "sealed_at": "2026-09-22T09:01:54.620015+00:00",
  "composite_hash": "e01187fee8acb0969976df6f4cd49164e8443195165355990daa878cf7e12b41",
  "event_count": 2,
  "item_counts": {
    "events": 2,
    "documents": 1,
    "attestations": 1
  },
  "events": [
    {
      "event_id": "3f9a2c71-6b0d-4e85-9c12-7d4e8a1b5f60",
      "event_type": "invoice.received",
      "payload": {
        "invoice_number": "INV-2026-0441",
        "amount_cents": 128500,
        "currency": "EUR"
      },
      "content_hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
      "signature": "b7c1d4e9f2a5068397c0d3e6f1a4b58d2e4f6a8c0b1d3f5e7a9c1b3d5f7e9a0c4d6e8f0a2b4c6d8e1f3a5b7c9d0e2f4a6b8c0d2e4f6a8b1c3d5e7f9a0b2c4d6e",
      "public_key": "1f8e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0",
      "ingested_at": "2026-09-22T08:15:31.006240+00:00"
    },
    {
      "event_id": "9b2d4e6f-1a3c-4d5e-8f70-1c2b3a4d5e6f",
      "event_type": "invoice.approved",
      "payload": {
        "invoice_number": "INV-2026-0441",
        "approved_by": "finance_lead"
      },
      "content_hash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
      "signature": "3e5f7a9c1b2d4f6e8a0c2e4f6a8b0d1f5c7e9a1b3d5f7a9c0e2f4a6b8d0f2a4c7e9b1d3f5a7c9e0b2d4f6a8c1e3f5a7b9d0f2a4c6e8b1d3f5a7c9e0b2d4f6a8c",
      "public_key": "1f8e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0",
      "ingested_at": "2026-09-22T08:22:45.310556+00:00"
    }
  ],
  "documents": [
    {
      "event_id": "5e8f1a2b-3c4d-4e5f-8a6b-7c8d9e0f1a2b",
      "document_ref": "INV-2026-0441.pdf",
      "document_hash": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
      "event_type": "document.anchored",
      "signature": "9a0b2c4d6e8f1a3b5c7d9e0f2a4b6c8d1e3f5a7b9c0d2e4f6a8b1c3d5e7f9a0b2c4d6e8f1a3b5c7d9e0f2a4b6c8d1e3f5a7b9c0d2e4f6a8b1c3d5e7f9a0b2c4d",
      "public_key": "1f8e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0",
      "anchored_at": "2026-09-22T08:19:02.751108+00:00"
    }
  ],
  "attestations": [
    {
      "attestation_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
      "attestation_type": "output",
      "model_provider": "openai",
      "model_name": "gpt-4.1",
      "payload_hash": "bef6d7205ca5b97289bdb4cdb60a6de5b1fd8a2e1e5a9b663f1cdea305b644ab",
      "signature": "5c7e9a1b3d5f7a9c0e2f4a6b8d0f2a4c7e9b1d3f5a7c9e0b2d4f6a8c1e3f5a7b9d0f2a4c6e8b1d3f5a7c9e0b2d4f6a8c3e5f7a9c1b2d4f6e8a0c2e4f6a8b0d1f",
      "public_key": "1f8e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0",
      "created_at": "2026-09-22T08:26:10.094377+00:00"
    }
  ],
  "seal_event": {
    "event_id": "c4d7e8f1-2a3b-4c5d-9e6f-0a1b2c3d4e5f",
    "event_type": "trace.sealed",
    "payload": {
      "trace_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
      "composite_hash": "e01187fee8acb0969976df6f4cd49164e8443195165355990daa878cf7e12b41",
      "event_count": 4,
      "sealed_at": "2026-09-22T09:01:54.620015+00:00"
    },
    "content_hash": "3b428d41452a757b5466edc0520d0f3028c0335cfab5c7b6dac47531dbfcf321",
    "signature": "e1f3a5b7c9d0f2a4b6c8d1e3f5a7b9c0d2e4f6a8b1c3d5e7f9a0b2c4d6e8f1a3b5c7d9e0f2a4b6c8d1e3f5a7b9c0d2e4f6a8b1c3d5e7f9a0b2c4d6e8f1a3b5c7",
    "public_key": "1f8e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0",
    "ingested_at": "2026-09-22T09:01:54.620015+00:00"
  }
}
Response fields
versionstring

Bundle format version, currently 1.0.

trace_iduuid

The trace id.

labelstring

The label given at creation.

issuer_namestring

The issuer name snapshotted on the most recent event in the trace, falling back to the organization's issuer name; null when neither is set.

tenant_domainstring

The organization's primary domain, or null.

domain_verifiedboolean

Whether the organization has verified that domain; null when unknown.

statusstring

Always sealed; other statuses are rejected with 409. One of sealed.

created_atstring

When the trace was created, RFC 3339 with a +00:00 offset.

sealed_atstring

When the seal completed, RFC 3339 with a +00:00 offset.

composite_hashhex

SHA-256 over the concatenated raw item hashes in timestamp order, 64 hex characters.

event_countinteger

Number of entries in events; the seal event is not counted.

item_countsobject

Counts stored at seal time as {events, documents, attestations}; null for traces sealed before the counts were recorded.

events[].event_iduuid

The event id.

events[].event_typestring

The event type given at ingest.

events[].payloadobject

The stored event payload.

events[].content_hashhex

SHA-256 of the event payload, 64 hex characters.

events[].signaturehex

Ed25519 signature stored with the event, 128 hex characters, or null.

events[].public_keyhex

Ed25519 public key that signed the event, 64 hex characters, or null.

events[].ingested_atstring

When the event was ingested, RFC 3339 with a +00:00 offset; ordered ascending.

documents[].event_iduuid

The document anchor's event id.

documents[].document_refstring

The reference given when the document was anchored.

documents[].document_hashhex

SHA-256 of the document, 64 hex characters.

documents[].event_typestring

The event type given when the document was anchored.

documents[].signaturehex

Ed25519 signature stored with the anchor, 128 hex characters, or null.

documents[].public_keyhex

Ed25519 public key that signed the anchor, 64 hex characters, or null.

documents[].anchored_atstring

When the document was anchored, RFC 3339 with a +00:00 offset.

attestations[].attestation_iduuid

The attestation id.

attestations[].attestation_typestring

The attestation type given at ingest.

attestations[].model_providerstring

The model provider given at ingest, or null.

attestations[].model_namestring

The model name given at ingest.

attestations[].payload_hashhex

SHA-256 of the attestation's canonical request bytes, 64 hex characters; the same value as its attestation_hash.

attestations[].payloadstring

The exact canonical request bytes stored at ingest, as a JSON string whose SHA-256 is payload_hash; present only when the owner enabled ai_public for the trace, omitted otherwise.

attestations[].signaturehex

Ed25519 signature stored with the attestation, 128 hex characters, or null.

attestations[].public_keyhex

Ed25519 public key that signed the attestation, 64 hex characters, or null.

attestations[].created_atstring

When the attestation was created, RFC 3339 with a +00:00 offset.

seal_eventobject

The trace.sealed event in the same shape as an events[] entry, with the seal payload {trace_id, composite_hash, event_count, sealed_at} as its payload; null if the seal event row is missing.

Errors
trace_not_found404

No trace with this id exists in any tenant.

trace_not_sealed409

The trace exists but its status is open or sealing.

trace_not_public403

The trace is sealed but its owner has not enabled public verification.

db_error500

A database query failed.

Terms
openThe status a trace starts in; items can be attached and the trace can be deleted while it has no events.
sealingThe status set by POST /v1/traces/{trace_id}/seal while the worker computes the composite hash; new items are rejected and the status returns to open if the job fails.
sealedThe final status; the trace has a composite hash, a signed seal event, and accepts no changes.
itemAn event, an anchored document or an AI attestation that carries the trace's id.
composite hashSHA-256 over the raw 32-byte hashes of every item in the trace, concatenated in timestamp order across events, documents and attestations.
seal eventA trace.sealed event written to the ledger at seal time, signed with the tenant's Ed25519 key, carrying the composite hash, item count and seal time.
proof bundleThe JSON from GET /v1/traces/{trace_id}/proof: every item with its hash, signature and public key, plus the seal event.
public proofThe same data served at GET /v1/proof/trace/{trace_id} without an API key, for traces whose owner enabled public verification.

Verified documents and records. Invoance records the exact version of every invoice, report and notice your company sends, so the people who receive them can confirm they have the version you sent, without taking your word for it.

Products

  • Evidence Rooms
  • 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

  • Company overview
  • What is Invoance
  • Pricing
  • Security
  • Compliance teams
  • Finance teams
  • Partners
  • Brand assets
  • Resources
  • Help center
  • Contact
© 2025 – 2026 Invoance, Inc. All rights reserved.© 2026 Invoance, Inc. All rights reserved.
PrivacyLegal noticeLegal FAQ