Home
Home/Developers/Audit logs/streams
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 streams

Push a customer's signed audit events to their SIEM or webhook as they happen.

Audit Logs overview
sha-256 · 3a352297…2592
sha-256 · bae251a9…8ffa
On this page
POST/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
Endpoints
POST/v1/audit/orgs/{id}/streams
API keyaudit:write

Create a webhook stream

Registers an https destination that receives the org's new events as signed JSON batches, and returns the stream with its signing secret shown once.

Headers
Content-Typestring · required

Must be application/json.

Path parameters
idstring · required

The aorg_ id or your organization_id; both resolve to the same org, scoped to your tenant.

Request body
typestring · required

Destination type; only webhook is accepted today. One of webhook.

urlstring · required

Absolute https URL that will receive POST deliveries; the host must resolve to a public address.

  • Deliveries are POST requests with Content-Type application/json, User-Agent Invoance-Audit/1 and X-Invoance-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 with the signing secret over "<t>.<body>".
  • The body is {"events": [...], "count": n, "delivered_at": "<RFC 3339>"}; events are full event objects in ascending seq order, up to 100 per delivery.
  • Only a 2xx from your endpoint counts as delivered; 408, 425, 429, any 5xx, timeouts and connection errors put the stream in state error and retry after 2^failure_streak seconds up to 300; any other 4xx puts it in state invalid and stops it until you delete and recreate the stream.
  • The destination is checked for private, loopback, link-local and metadata addresses both at creation and again at every delivery, and redirects are never followed.
  • The stream starts at the org's current seq; events already in the log are not replayed, and events that reached cold storage before delivery are skipped with a warning in the server log.
  • The SDKs default type to webhook when omitted.
Node.js
import { InvoanceClient } from "invoance";

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

const stream = await client.audit.streams.create("org_8472", {
  url: "https://siem.example.com/hooks/invoance",
});
// Store signing_secret now; it is not returned again.
console.log(stream.id, stream.signing_secret);
Response · 201
{
  "id": "astr_01J0Y3N5P7R9T1V3X5Z7B9D1FG",
  "type": "webhook",
  "endpoint": "https://siem.example.com/hooks/invoance",
  "state": "active",
  "cursor_seq": 41,
  "signing_secret": "whsec_4c1d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d"
}
Response fields
idstring

Stream id, astr_ followed by a ULID.

typestring

Destination type; only webhook can be created. One of webhook.

endpointstring

The https URL deliveries are posted to, as validated at create time.

statestring

active while deliveries succeed, error while a transient failure is being retried with backoff, invalid once a permanent failure stopped the stream. One of active, error, invalid.

cursor_seqinteger

The org's last_seq at creation; delivery starts with the next event, nothing older is replayed.

signing_secretstring

whsec_ followed by 64 hex characters; the HMAC key for X-Invoance-Signature, returned only in this response.

Errors
unsupported_stream_type400

type is not webhook.

invalid_url400

url is not an absolute URL.

not_https400

url does not use https.

no_host400

url has no host.

resolution_failed400

The host did not resolve in DNS.

forbidden_destination400

The host resolves to a private, loopback, link-local or cloud metadata address.

too_many_streams400

The org already has as many streams as the plan allows.

insufficient_scope403

The key does not have audit:write; audit:read alone is not enough.

not_found404

No audit org with that id or organization_id belongs to the tenant.

org_archived409

The org is archived; unarchive it first.

rate_limited429

The tenant used up its per-second or per-minute request budget; the Retry-After header says when to retry.

db_error500

A database query failed.

missing_api_key401

Neither an Authorization header nor an X-API-Key header was sent.

invalid_authorization_scheme401

An Authorization header was sent without the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

The key does not match any API key.

api_key_revoked401

The key 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.

GET/v1/audit/orgs/{id}/streams
API keyaudit:read

List an org's streams

Returns up to 100 streams of the org with their delivery state, newest first, never including secrets.

Path parameters
idstring · required

The aorg_ id or your organization_id; both resolve to the same org, scoped to your tenant.

  • A stream in state invalid stays listed with its last_error until you delete it.
Node.js
import { InvoanceClient } from "invoance";

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

const result = await client.audit.streams.list("org_8472");
for (const stream of result.streams as Array<Record<string, unknown>>) {
  console.log(stream.id, stream.state, stream.cursor_seq, stream.last_error);
}
Response · 200
{
  "streams": [
    {
      "id": "astr_01J0Y3N5P7R9T1V3X5Z7B9D1FG",
      "type": "webhook",
      "endpoint": "https://siem.example.com/hooks/invoance",
      "state": "active",
      "cursor_seq": 42,
      "failure_streak": 0,
      "last_error": null,
      "last_delivery_at": "2026-09-22T08:14:09.104233+00:00",
      "created_at": "2026-09-22T08:10:00.418212+00:00"
    }
  ]
}
Response fields
streamsobject[]

The streams, ordered by created_at descending.

streams[].idstring

Stream id, astr_ followed by a ULID.

streams[].typestring

Destination type; only webhook can be created. One of webhook.

streams[].endpointstring

The https URL deliveries are posted to, as validated at create time.

streams[].statestring

active while deliveries succeed, error while a transient failure is being retried with backoff, invalid once a permanent failure stopped the stream. One of active, error, invalid.

streams[].cursor_seqinteger

The highest seq delivered so far; the next delivery starts at cursor_seq + 1.

streams[].failure_streakinteger

Consecutive failed deliveries; reset to 0 on success and used to compute the backoff.

streams[].last_errorstring

Message from the last failed delivery, or null.

streams[].last_delivery_attimestamp (ISO 8601)

When the last successful delivery was recorded, or null.

streams[].created_attimestamp (ISO 8601)

When the stream was created.

Errors
insufficient_scope403

The key has neither audit:read nor audit:write; a ledger key with only read or write is rejected on audit routes.

not_found404

No audit org with that id or organization_id belongs to the tenant.

rate_limited429

The tenant used up its per-second or per-minute request budget; the Retry-After header says when to retry.

db_error500

A database query failed.

missing_api_key401

Neither an Authorization header nor an X-API-Key header was sent.

invalid_authorization_scheme401

An Authorization header was sent without the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

The key does not match any API key.

api_key_revoked401

The key 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.

DELETE/v1/audit/orgs/{id}/streams/{stream_id}
API keyaudit:write

Delete a stream

Removes the stream so no further deliveries are made, and returns the deleted id.

Path parameters
idstring · required

The aorg_ id or your organization_id; both resolve to the same org, scoped to your tenant.

stream_idstring · required

The astr_ id of the stream.

  • Deleting works on archived orgs; only creating and testing streams is blocked by archiving.
Node.js
import { InvoanceClient } from "invoance";

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

const result = await client.audit.streams.delete("org_8472", "astr_01J0Y3N5P7R9T1V3X5Z7B9D1FG");
console.log(result.deleted, result.id);
Response · 200
{
  "deleted": true,
  "id": "astr_01J0Y3N5P7R9T1V3X5Z7B9D1FG"
}
Response fields
deletedboolean

Always true on success.

idstring

The astr_ id that was deleted.

Errors
insufficient_scope403

The key does not have audit:write; audit:read alone is not enough.

not_found404

No org with that id belongs to the tenant, or no stream with that id belongs to the org.

rate_limited429

The tenant used up its per-second or per-minute request budget; the Retry-After header says when to retry.

db_error500

A database query failed.

missing_api_key401

Neither an Authorization header nor an X-API-Key header was sent.

invalid_authorization_scheme401

An Authorization header was sent without the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

The key does not match any API key.

api_key_revoked401

The key 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.

POST/v1/audit/orgs/{id}/streams/{stream_id}/test
API keyaudit:write

Send a test delivery

Posts one synthetic stream.test event to the stream's destination with a real signature and returns what the destination answered.

Path parameters
idstring · required

The aorg_ id or your organization_id; both resolve to the same org, scoped to your tenant.

stream_idstring · required

The astr_ id of the stream.

  • The test event is {id: aevt_..., action: "stream.test", actor: {type: "system", id: "invoance"}, targets: [], occurred_at, metadata: {test: true}}; it is not stored, not signed with the tenant key and carries no seq, payload_hash or signature.
  • The delivery is wrapped and signed exactly like a real batch, so it exercises your X-Invoance-Signature check.
  • A test never moves cursor_seq or changes the stream's state; the response waits for the destination up to 15 seconds.
Node.js
import { InvoanceClient } from "invoance";

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

const result = await client.audit.streams.test("org_8472", "astr_01J0Y3N5P7R9T1V3X5Z7B9D1FG");
console.log(result.delivered, result.http_status, result.error);
Response · 200
{
  "delivered": true,
  "http_status": 200,
  "error": null,
  "retryable": false
}
Response fields
deliveredboolean

True when the destination answered with a 2xx.

http_statusinteger

The destination's status code, or null when no response arrived.

errorstring

Why the delivery failed, or null on success.

retryableboolean

True when the failure is one the dispatcher would retry (408, 425, 429, 5xx, network); false on success or a permanent failure.

Errors
unsupported_stream_type400

The stream is not a webhook stream.

no_endpoint400

The stream has no endpoint stored.

insufficient_scope403

The key does not have audit:write; audit:read alone is not enough.

not_found404

No org with that id belongs to the tenant, or no stream with that id belongs to the org.

org_archived409

The org is archived; unarchive it first.

rate_limited429

The tenant used up its per-second or per-minute request budget; the Retry-After header says when to retry.

db_error500

A database query failed or the stream's stored secret could not be decrypted.

missing_api_key401

Neither an Authorization header nor an X-API-Key header was sent.

invalid_authorization_scheme401

An Authorization header was sent without the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

The key does not match any API key.

api_key_revoked401

The key 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.

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
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
Public proofGET/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

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