Docs · SDKs
Node.js SDK
Install the Node.js SDK, build a client, see every method with a Node.js sample, and verify records offline.
Install
Node.js
invoance on npm.
Node 18 or later. No runtime dependencies; Ed25519 checks use node:crypto.
Terminal
npm install invoance
Client
| apiKey | The API key. Falls back to INVOANCE_API_KEY; the client throws when neither is set. |
|---|---|
| baseUrl | API host. Falls back to INVOANCE_BASE_URL, then https://api.invoance.com. Trailing slashes are removed. |
| apiVersion | Path prefix put before every request path. Default v1. |
| timeoutMs | Per-request timeout in milliseconds. Default 30000; past it the call throws TimeoutError. |
| idempotencyKey | Default Idempotency-Key header for every mutating request; a per-call key wins. |
| extraHeaders | Headers merged into every request. |
| Retries | None. Each request is sent once; on TimeoutError or NetworkError, retry it yourself with the same Idempotency-Key. |
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY and INVOANCE_BASE_URL from the environment.
const client = new InvoanceClient();
// Or pass options.
const configured = new InvoanceClient({
apiKey: "invoance_live_...",
baseUrl: "https://api.invoance.com",
timeoutMs: 60_000,
});
// GET /v1/me checks no scope, so any live key passes. Never throws.
const { valid, reason } = await client.validate();
console.log(valid, reason);
Methods
Every endpoint with a Node.js sample, by resource. Open a row for the sample; the link opens the endpoint card with its fields, response and errors.
EventsReference
POST/v1/events
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.events.ingest({
eventType: "policy.approval",
eventTime: "2026-09-22T08:14:07Z",
payload: {
policy_id: "pol_8472",
approved_by: "risk_committee",
decision: "approved",
},
idempotencyKey: "policy-approval-pol_8472",
});
console.log(result.event_id, result.ingested_at);
GET/v1/events
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const page = await client.events.list({
page: 1,
limit: 50,
eventType: "policy.approval",
});
console.log(page.total, page.has_more);
for (const event of page.events) {
console.log(event.event_id, event.ingested_at, event.payload_hash);
}
GET/v1/events/{event_id}
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const event = await client.events.get("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4");
console.log(event.event_type, event.ingested_at);
console.log(event.payload_hash, event.event_hash, event.request_hash);
console.log(event.payload);
POST/v1/events/{event_id}/verify
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.events.verify("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4", {
payload: {
policy_id: "pol_8472",
approved_by: "risk_committee",
decision: "approved",
},
});
console.log(result.match_result, result.matched_field);
console.log(result.anchored_hash, result.submitted_hash, result.anchored_at);
DocumentsReference
POST/v1/document/anchor
Node.js
import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";
import { InvoanceClient } from "invoance";
const file = readFileSync("./INV-2026-0917.pdf");
const documentHash = createHash("sha256").update(file).digest("hex");
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.documents.anchor({
documentHash,
documentRef: "INV-2026-0917.pdf",
eventType: "invoice.issued",
metadata: { invoice_number: "INV-2026-0917", amount: 5230, currency: "USD" },
idempotencyKey: "anchor-" + documentHash,
});
console.log(result.event_id, result.status);
GET/v1/document
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const page = await client.documents.list({
limit: 25,
dateFrom: "2026-09-01T00:00:00Z",
});
console.log(page.total, page.has_more);
for (const d of page.documents) {
console.log(d.event_id, d.document_ref, d.has_original);
}
GET/v1/document/{event_id}
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const doc = await client.documents.get("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4");
console.log(doc.document_hash, doc.has_original, doc.created_at);
console.log(doc.organization?.issuer_name, doc.organization?.domain_verified);
GET/v1/document/{event_id}/original
Node.js
import { writeFileSync } from "node:fs";
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const bytes = await client.documents.getOriginal("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4");
writeFileSync("./INV-2026-0917.pdf", Buffer.from(bytes));
console.log(bytes.byteLength);
POST/v1/document/{event_id}/verify
Node.js
import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";
import { InvoanceClient } from "invoance";
const file = readFileSync("./INV-2026-0917.pdf");
const documentHash = createHash("sha256").update(file).digest("hex");
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.documents.verify("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4", {
documentHash,
});
console.log(result.match_result, result.anchored_hash, result.anchored_at);
AI AttestationsReference
POST/v1/ai/attestations
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.attestations.ingest({
type: "output",
input: "Summarize the termination clause in contract CT-8472.",
output: "Either party may terminate with 30 days written notice. Early termination fees do not apply after month 12.",
modelProvider: "openai",
modelName: "gpt-4.1",
modelVersion: "2026-04-14",
subject: { userId: "u_4821", sessionId: "sess_9f3a", department: "legal" },
idempotencyKey: "ct-8472-summary-1",
});
console.log(result.attestation_id, result.payload_hash);
GET/v1/ai/attestations
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const page = await client.attestations.list({
limit: 50,
attestationType: "output",
modelProvider: "openai",
});
console.log(page.total, page.has_more, page.attestations.length);
GET/v1/ai/attestations/{attestation_id}
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const att = await client.attestations.get("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4");
console.log(att.attestation_hash, att.signature_alg, att.public_key);
GET/v1/ai/attestations/{attestation_id}/raw
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const raw = await client.attestations.getRaw("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4");
console.log(raw.type, raw.context);
POST/v1/ai/attestations/{attestation_id}/verify
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.attestations.verify("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4", {
contentHash: "c4efe15781214a84046ad7e0592977c634a5cf45f4c1e06160daf760a295a8df",
});
console.log(result.match_result, result.matched_field);
TracesReference
POST/v1/traces
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);
GET/v1/traces
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);
GET/v1/traces/{trace_id}
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);
}
DELETE/v1/traces/{trace_id}
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);
POST/v1/traces/{trace_id}/seal
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);
GET/v1/traces/{trace_id}/proof
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");
GET/v1/traces/{trace_id}/proof/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");
GET/v1/proof/trace/{trace_id}
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");
Audit LogsReference
POST/v1/audit/events
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.audit.events.ingest({
organizationId: "org_8472",
action: "user.signed_in",
occurredAt: "2026-09-22T08:14:07Z",
actor: { type: "user", id: "u_4821", name: "Ada Lovelace" },
targets: [{ type: "workspace", id: "ws_17" }],
context: { location: "203.0.113.10", user_agent: "Mozilla/5.0" },
metadata: { method: "sso", mfa: true },
idempotencyKey: "signin-u_4821-2026-09-22T08:14:07Z",
});
console.log(result.event_id, result.ingested_at);
GET/v1/audit/events
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const page = await client.audit.events.list({
organizationId: "org_8472",
actions: "user.signed_in,user.signed_out",
rangeStart: "2026-09-01T00:00:00Z",
limit: 50,
});
for (const event of page.events) {
console.log(event.seq, event.action, event.actor?.id);
}
console.log(page.next_cursor);
GET/v1/audit/events/{id}
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const event = await client.audit.events.get("aevt_01J0Y1Z2A3B4C5D6E7F8G9H0JK");
console.log(event.seq, event.action, event.payload_hash);
GET/v1/audit/events/{id}/verify
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.audit.events.verify("aevt_01J0Y1Z2A3B4C5D6E7F8G9H0JK");
console.log(result.valid, result.reason, result.payload_hash);
POST/v1/audit/orgs
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const org = await client.audit.orgs.create({
organizationId: "org_8472",
name: "Acme Robotics",
});
console.log(org.id, org.retention_days);
GET/v1/audit/orgs
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.audit.orgs.list({ includeArchived: true });
for (const org of result.orgs as Array<Record<string, unknown>>) {
console.log(org.id, org.organization_id, org.archived_at);
}
PATCH/v1/audit/orgs/{id}
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const org = await client.audit.orgs.update("org_8472", { name: "Acme Robotics Ltd" });
console.log(org.name);
// Pass null to clear the name.
await client.audit.orgs.update("org_8472", { name: null });
DELETE/v1/audit/orgs/{id}
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.audit.orgs.delete("org_8472");
console.log(result.deleted, result.id);
POST/v1/audit/orgs/{id}/archive
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const org = await client.audit.orgs.archive("org_8472");
console.log(org.archived_at);
POST/v1/audit/orgs/{id}/unarchive
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const org = await client.audit.orgs.unarchive("org_8472");
console.log(org.archived_at);
GET/v1/audit/orgs/{id}/integrity
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const report = await client.audit.orgs.integrity("org_8472");
console.log(report.contiguous, report.count, report.expected, report.gaps);
PUT/v1/audit/orgs/{id}/retention
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.audit.orgs.setRetention("org_8472", 365);
console.log(result.retention_days, result.clamped, result.plan_cap_days);
POST/v1/audit/orgs/{id}/streams
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);
GET/v1/audit/orgs/{id}/streams
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);
}
DELETE/v1/audit/orgs/{id}/streams/{stream_id}
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);
POST/v1/audit/orgs/{id}/streams/{stream_id}/test
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);
POST/v1/audit/portal_sessions
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const session = await client.audit.portalSessions.create({
organizationId: "org_8472",
intent: "audit_logs",
sessionDurationSeconds: 3600,
});
console.log(session.url, session.link_expires_in, session.session_expires_in);
POST/v1/audit/exports
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const job = await client.audit.exports.create({
organizationId: "org_8472",
format: "ndjson",
filters: {
actions: "user.signed_in,user.signed_out",
occurred_after: "2026-09-01T00:00:00Z",
},
});
console.log(job.id, job.status);
GET/v1/audit/exports/{id}
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
let job = await client.audit.exports.get("aexp_01J0Y4Q6S8V0X2Z4B6D8F0H2JK");
while (job.status === "pending" || job.status === "running") {
await new Promise((r) => setTimeout(r, 5000));
job = await client.audit.exports.get("aexp_01J0Y4Q6S8V0X2Z4B6D8F0H2JK");
}
console.log(job.status, job.row_count, job.download_url ?? job.error);
POST/v1/audit/portal/exchange
Node.js
// No API key: the exchange is public and the link token is the credential.
const linkToken = process.env.PORTAL_LINK_TOKEN;
const res = await fetch("https://api.invoance.com/v1/audit/portal/exchange", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token: linkToken }),
});
if (!res.ok) {
throw new Error(res.status + " " + (await res.text()));
}
const session = await res.json();
console.log(session.intent, session.expires_in);
// session.token is the Bearer token for the /v1/audit/portal/* routes.
GET/v1/audit/portal/events
Node.js
// Portal reads use the short-lived JWT from POST /v1/audit/portal/exchange,
// not an API key. PORTAL_TOKEN holds that JWT.
const portalToken = process.env.PORTAL_TOKEN;
const url = new URL("https://api.invoance.com/v1/audit/portal/events");
url.searchParams.set("actions", "user.signed_in");
url.searchParams.set("limit", "50");
const res = await fetch(url, { headers: { Authorization: "Bearer " + portalToken } });
if (!res.ok) {
throw new Error(res.status + " " + (await res.text()));
}
const page = await res.json();
for (const event of page.events) {
console.log(event.seq, event.action, event.actor.id);
}
console.log(page.next_cursor);
GET/v1/audit/portal/events/{id}
Node.js
// Portal reads use the short-lived JWT from POST /v1/audit/portal/exchange,
// not an API key. PORTAL_TOKEN holds that JWT.
const portalToken = process.env.PORTAL_TOKEN;
const res = await fetch("https://api.invoance.com/v1/audit/portal/events/aevt_01J0Y1Z2A3B4C5D6E7F8G9H0JK", {
headers: { Authorization: "Bearer " + portalToken },
});
if (!res.ok) {
throw new Error(res.status + " " + (await res.text()));
}
const event = await res.json();
console.log(event.seq, event.action, event.payload_hash);
GET/v1/audit/portal/events/{id}/verify
Node.js
// Portal reads use the short-lived JWT from POST /v1/audit/portal/exchange,
// not an API key. PORTAL_TOKEN holds that JWT.
const portalToken = process.env.PORTAL_TOKEN;
const res = await fetch("https://api.invoance.com/v1/audit/portal/events/aevt_01J0Y1Z2A3B4C5D6E7F8G9H0JK/verify", {
headers: { Authorization: "Bearer " + portalToken },
});
if (!res.ok) {
throw new Error(res.status + " " + (await res.text()));
}
const result = await res.json();
console.log(result.valid, result.reason, result.key_source);
GET/v1/audit/portal/org
Node.js
// Portal reads use the short-lived JWT from POST /v1/audit/portal/exchange,
// not an API key. PORTAL_TOKEN holds that JWT.
const portalToken = process.env.PORTAL_TOKEN;
const res = await fetch("https://api.invoance.com/v1/audit/portal/org", {
headers: { Authorization: "Bearer " + portalToken },
});
if (!res.ok) {
throw new Error(res.status + " " + (await res.text()));
}
const info = await res.json();
console.log(info.issuer.name, info.org.name, info.intent);
GET/v1/audit/portal/streams
Node.js
// Portal reads use the short-lived JWT from POST /v1/audit/portal/exchange,
// not an API key. PORTAL_TOKEN holds that JWT.
const portalToken = process.env.PORTAL_TOKEN;
const res = await fetch("https://api.invoance.com/v1/audit/portal/streams", {
headers: { Authorization: "Bearer " + portalToken },
});
if (!res.ok) {
throw new Error(res.status + " " + (await res.text()));
}
const { streams } = await res.json();
for (const stream of streams) {
console.log(stream.id, stream.state, stream.endpoint);
}
POST/v1/audit/portal/streams
Node.js
// Portal reads use the short-lived JWT from POST /v1/audit/portal/exchange,
// not an API key. PORTAL_TOKEN holds that JWT.
const portalToken = process.env.PORTAL_TOKEN;
const res = await fetch("https://api.invoance.com/v1/audit/portal/streams", {
method: "POST",
headers: {
Authorization: "Bearer " + portalToken,
"Content-Type": "application/json",
},
body: JSON.stringify({ type: "webhook", url: "https://siem.example.com/hooks/invoance" }),
});
if (!res.ok) {
throw new Error(res.status + " " + (await res.text()));
}
const stream = await res.json();
// Store signing_secret now; it is not returned again.
console.log(stream.id, stream.signing_secret);
DELETE/v1/audit/portal/streams/{id}
Node.js
// Portal reads use the short-lived JWT from POST /v1/audit/portal/exchange,
// not an API key. PORTAL_TOKEN holds that JWT.
const portalToken = process.env.PORTAL_TOKEN;
const res = await fetch("https://api.invoance.com/v1/audit/portal/streams/astr_01J0Y3N5P7R9T1V3X5Z7B9D1FG", {
method: "DELETE",
headers: { Authorization: "Bearer " + portalToken },
});
if (!res.ok) {
throw new Error(res.status + " " + (await res.text()));
}
const result = await res.json();
console.log(result.deleted, result.id);
POST/v1/audit/portal/streams/{id}/test
Node.js
// Portal reads use the short-lived JWT from POST /v1/audit/portal/exchange,
// not an API key. PORTAL_TOKEN holds that JWT.
const portalToken = process.env.PORTAL_TOKEN;
const res = await fetch("https://api.invoance.com/v1/audit/portal/streams/astr_01J0Y3N5P7R9T1V3X5Z7B9D1FG/test", {
method: "POST",
headers: { Authorization: "Bearer " + portalToken },
});
if (!res.ok) {
throw new Error(res.status + " " + (await res.text()));
}
const result = await res.json();
console.log(result.delivered, result.http_status, result.error);
GET/v1/proof/audit/{event_id}
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);
POST/v1/proof/audit/{event_id}/verify
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);
PlatformReference
GET/v1/me
Node.js
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const me = await client.me();
console.log(me.organization.primary_domain, me.organization.plan_tier);
console.log(me.api_key.scopes, me.limits.rate_limit_per_sec);
GET/keys/{domain}
Node.js
import { createPublicKey, verify } from "node:crypto";
const domain = "acme.com";
const eventId = "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4";
// 1. The key for the issuer's verified domain (no API key needed).
const keyRes = await fetch("https://api.invoance.com/keys/" + domain);
if (!keyRes.ok) throw new Error("key lookup failed: " + keyRes.status);
const key = await keyRes.json();
// 2. The signed record, from the public event proof endpoint.
const proofRes = await fetch("https://api.invoance.com/v1/proof/event/" + eventId);
if (!proofRes.ok) throw new Error("proof lookup failed: " + proofRes.status);
const { event } = await proofRes.json();
// 3. Raw 32-byte Ed25519 key to SPKI DER, which node:crypto can load.
const raw = Buffer.from(key.public_key, "base64url");
const spki = Buffer.concat([Buffer.from("302a300506032b6570032100", "hex"), raw]);
const pinned = createPublicKey({ key: spki, format: "der", type: "spki" });
// 4. The record must name the same key, and the signature must verify with it.
const sameKey = Buffer.from(event.public_key, "hex").equals(raw);
const signatureValid = verify(
null,
Buffer.from(event.signed_payload, "hex"),
pinned,
Buffer.from(event.signature, "hex"),
);
console.log(key.key_id, sameKey, signatureValid);
GET/v1/proof/event/{event_id}
Node.js
const eventId = "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4";
const res = await fetch("https://api.invoance.com/v1/proof/event/" + eventId);
if (!res.ok) throw new Error("proof lookup failed: " + res.status);
const { organization, event } = await res.json();
console.log(organization.primary_domain, organization.domain_verified);
console.log(event.event_type, event.payload_hash, event.signature_alg);
POST/v1/proof/event/{event_id}/verify
Node.js
const eventId = "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4";
const res = await fetch("https://api.invoance.com/v1/proof/event/" + eventId + "/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
payload: {
policy_id: "pol_8472",
approved_by: "risk_committee",
decision: "approved",
},
}),
});
if (!res.ok) throw new Error("verify failed: " + res.status);
const result = await res.json();
console.log(result.match_result, result.signature_valid, result.method);
GET/v1/proof/{event_id}/organization
Node.js
const eventId = "3f9d2a71-5c6e-4b8a-9d1f-8e2c47b0a5d3";
const res = await fetch("https://api.invoance.com/v1/proof/" + eventId + "/organization");
if (!res.ok) throw new Error("proof lookup failed: " + res.status);
const { organization, event } = await res.json();
console.log(organization.issuer_name, organization.domain_verified);
console.log(event.event_id, event.created_at);
POST/v1/proof/{event_id}/verify
Node.js
import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";
const eventId = "3f9d2a71-5c6e-4b8a-9d1f-8e2c47b0a5d3";
const documentHash = createHash("sha256").update(await readFile("./contract.pdf")).digest("hex");
const res = await fetch("https://api.invoance.com/v1/proof/" + eventId + "/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ document_hash: documentHash }),
});
if (!res.ok) throw new Error("verify failed: " + res.status);
const result = await res.json();
console.log(result.match_result, result.signature_valid, result.anchored_at);
GET/v1/proof/ai/{attestation_id}
Node.js
const attestationId = "a1d4f8c2-7b3e-4e9a-b5c6-0d2e8f4a7c19";
const res = await fetch("https://api.invoance.com/v1/proof/ai/" + attestationId);
if (!res.ok) throw new Error("proof lookup failed: " + res.status);
const { organization, attestation } = await res.json();
console.log(organization.primary_domain, organization.domain_verified);
console.log(attestation.attestation_type, attestation.model_name, attestation.output_hash);
POST/v1/proof/ai/{attestation_id}/verify
Node.js
import { createHash } from "node:crypto";
const attestationId = "a1d4f8c2-7b3e-4e9a-b5c6-0d2e8f4a7c19";
const output = "Either party may terminate with 30 days written notice. Early termination fees do not apply after month 12.";
const contentHash = createHash("sha256").update(output).digest("hex");
const res = await fetch("https://api.invoance.com/v1/proof/ai/" + attestationId + "/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ content_hash: contentHash }),
});
if (!res.ok) throw new Error("verify failed: " + res.status);
const result = await res.json();
console.log(result.match_result, result.matched_field, result.signature_valid);
Verify offlineHow verification works
Two checks run without trusting the server: attestations.verifySignature fetches the record and checks its Ed25519 signature over signed_payload; verifyAuditEvent rebuilds the invoance.audit/1 bytes of an audit event and checks its signature. Pass publicKey to pin the key from GET /keys/{domain} instead of the key on the row.
Node.js
import { InvoanceClient, verifyAuditEvent } from "invoance";
const client = new InvoanceClient();
// AI attestation: Ed25519 over signed_payload, checked locally.
const sig = await client.attestations.verifySignature("a1d4f8c2-7b3e-4e9a-b5c6-0d2e8f4a7c19");
console.log(sig.valid, sig.reason);
// Audit event: canonical bytes rebuilt locally, signature checked
// against a pinned key (the public_key from GET /keys/{domain}, base64url decoded to hex).
const pinnedHexKey = "d4443bd9d30ef4c2e0e5db03467e3c5c740358482c1a1e30cdb27a2e02a38176";
const event = await client.audit.events.get("aevt_01J8F3KQ2R7VWX9YB4ND6MCZAH");
const result = verifyAuditEvent(event, { publicKey: pinnedHexKey });
console.log(result.valid, result.reason, result.keySource); // keySource: "pinned"