Home
Home/Developers/Audit logs/Viewer
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
Docs · Audit logs

Embeddable viewer

@invoance/audit-viewer renders one org's signed audit log inside your React app. Your backend mints a portal session; the browser does the rest.

On npmSource on GitHub
sha-256 · 3a352297…2592
sha-256 · e51db071…5f1f

What it shows

  • A table of signed events with actor, action, targets and time, a filter bar and keyset paging; the row detail adds seq.
  • CSV and JSON export of every event matching the filters, up to 10,000 rows, and a refresh of page 1 every 15 seconds while idle.
  • A detail drawer with the raw signed event and a step-by-step check: SHA-256 of the canonical bytes, then the Ed25519 signature, in the browser with Web Crypto.
  • An editor in the drawer that changes a local copy of the event, so the visitor can watch the check fail.
  • The issuer name and logo from GET /v1/audit/portal/org in the header.
  • For a session with intent log_streams, the stream destination screen instead of the table.
Package@invoance/audit-viewer 0.1.2, MIT
NeedsReact 18 or newer; no other runtime dependency
ShipsESM and CommonJS builds with types, plus styles.css
StylesCompiled inv- prefixed CSS with no reset; Tailwind is not needed
FrameworkReact only; there is no script tag build
Readsthe /v1/audit/portal routes with the session JWT
Sourcegithub.com/Invoance/invoance-audit-viewer
Install
npm
npm install @invoance/audit-viewer
Token flow
  1. Mint on your server

    POST /v1/audit/portal_sessions with an audit:write key, your organization_id and intent audit_logs. The reply carries a one-time token, valid for link_duration_seconds, 300 by default.

  2. Return the token

    Your getPortalToken callback fetches it from your backend. The API key stays on the server.

  3. Exchange

    The component POSTs the token to /v1/audit/portal/exchange and gets a JWT scoped to that org and intent. The token is spent. A 429 is retried up to twice after Retry-After.

  4. Read

    Events, checks and the org header come from the portal routes with the JWT. It lasts session_duration_seconds, 7,200 by default; on expiry the component calls getPortalToken again.

Mint a sessionFields, errors and samples
POST/v1/audit/portal_sessionsCreate a portal session

Send organization_id and intent with an audit:write key. The component then calls POST /v1/audit/portal/exchange and the portal routes listed on the Audit Logs API page.

Embed
// app/api/audit-portal-token/route.ts
// Runs on your server. The API key never reaches the browser.
import { InvoanceClient } from "invoance";

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

export async function POST() {
  const organizationId = await currentCustomerOrgId(); // your own lookup

  const session = await client.audit.portalSessions.create({
    organizationId,
    intent: "audit_logs",
    sessionDurationSeconds: 3600,
  });

  return Response.json({ token: session.token });
}
Props

All props are optional in type, but the component needs getPortalToken or tokenFromUrl to obtain a session.

AuditLogViewer
getPortalToken() => Promise<string>

Returns a fresh one-time portal token from your backend; called on mount and again when the session expires. Use this or tokenFromUrl to get a session.

tokenFromUrlboolean · default false

Reads the one-time token from ?token= in the page URL, exchanges it and removes it from the URL.

persistSessionboolean · default false

Keeps the session JWT in sessionStorage, so a page refresh does not need a new token.

baseUrlstring · default https://api.invoance.com

API origin; point it at your development backend.

pageSizeinteger · default 25

Initial rows per page; the visitor can change it. One of 25, 50, 100.

autoRefreshboolean · default true

Re-fetches page 1 every 15 seconds while idle.

themeobject

CSS variable overrides as { variables: { name: value } }; bare names get the --inv- prefix.

classNamestring

Extra classes on the root element.

onSessionExpired() => void

Called when the session expired and could not be renewed.

Theming

Nine CSS variables, each an HSL triple, drive every color. Set them on :root, on any ancestor of the viewer, or through the theme prop.

Dark embed
<AuditLogViewer
  getPortalToken={getPortalToken}
  theme={{
    variables: {
      background: "240 6% 12%",
      foreground: "0 0% 98%",
      border: "240 6% 20%",
      muted: "240 5% 16%",
      popover: "240 6% 12%",
      "popover-foreground": "0 0% 98%",
    },
  }}
/>
--inv-background0 0% 100%
--inv-foreground240 10% 8%
--inv-border240 6% 90%
--inv-muted240 4% 95%
--inv-popover0 0% 100%
--inv-popover-foreground240 10% 8%
--inv-success161 94% 30%
--inv-danger0 72% 51%
--inv-warning32 95% 44%
Sessions and errors
  • The JWT is scoped to one org and one intent; it cannot read another org.
  • On a 401 mid-session the component calls getPortalToken once and retries. If that fails it shows an expiry message and calls onSessionExpired.
  • An invalid, expired or already used link shows a message instead of the viewer. Mint a fresh session to recover.
  • Portal routes are limited per visitor IP: 10 requests per second on exchange, 60 on reads.
  • In a browser whose Web Crypto has no Ed25519, the check still recomputes the hash and reports the signature as not verifiable, which is not a failed check.
  • Every field the API returns for an event is shown, including context and metadata.
Verify without the component

The package exports the check it runs in the drawer. verifyAuditEventDetail rebuilds the invoance.audit/1 canonical bytes, recomputes the SHA-256 hash and checks the Ed25519 signature against the key on the event.

The SDKs do the same outside the browser, with the option to pin your registered key; see verifying an export.

TypeScript
import { verifyAuditEventDetail, canonicalAuditBytes } from "@invoance/audit-viewer";

// event: one row as the portal, get, an export or a stream delivered it.
const detail = await verifyAuditEventDetail(event);
console.log(detail.canonicalOk, detail.recomputedHash === detail.storedHash);
console.log(detail.ed25519Supported, detail.signatureChecked, detail.signatureValid);

// The exact bytes that were hashed and signed, for your own tooling.
const bytes = canonicalAuditBytes(event);
Questions
Does it work outside React?
No. The package is a React component with React 18 or newer as a peer dependency.
Can I import it from a Next.js server component?
Yes. The build carries a use client banner, so the import works from a server component tree without a wrapper.
Can the visitor change an event?
No. The portal routes are read-only for events. The drawer's editor changes a local copy only, so the check can be seen to fail.
How do I show the stream settings instead?
Mint the session with intent log_streams. The same component then renders the webhook destination screen for that org.

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

  • 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