SDK
Non-blocking TypeScript client for capturing automated decisions. Fire-and-forget recording, bounded retry, and a sendBeacon unload flush mean your application never waits on the ledger.
Installation
npm install @decisionkeep/clientQuick start
Import the client, configure it with your instance URL and API key, and call record(). The call returns immediately and never throws.
import DecisionKeepClient from "@decisionkeep/client";
const client = new DecisionKeepClient({
baseUrl: "https://keep.your-co.com",
apiKey: process.env.DECISIONKEEP_API_KEY!,
});
client.record({
system: "Underwriting",
agent: "credit-model",
version: "v3",
payload: { application_id: "APP-1", amount: 50000 },
});
const result = await client.verifyReceipt("RCP-20260710-1A2B3C");
console.log(result);API reference
record(input: DecisionInput): RecordResultQueue a decision for async dispatch. Returns immediately; never blocks and never throws. The result reports whether the entry was queued, flushed, or dropped (if the bounded buffer overflowed).
const result = client.record({
system: "Underwriting",
agent: "credit-model",
version: "v3",
payload: { application_id: "APP-1", amount: 50000 },
});
// result = { queued: true, flushed: false, dropped: false }verifyReceipt(receipt: string): Promise<VerifyResult>Verify a receipt against the public verification endpoint. Never rejects: on any error it resolves with { ok: false, error }.
const result = await client.verifyReceipt("RCP-20260710-1A2B3C");
// result = { ok: true, receipt: "RCP-20260710-1A2B3C" }dispose(): Promise<void>Flush any buffered entries and stop the retry timer. Call on shutdown (e.g. serverless function teardown, page unload in browser).
await client.dispose();Configuration options
Pass an options object to the DecisionKeepClient constructor. All options are optional except baseUrl and apiKey.
| Option | Type | Description | Default |
|---|---|---|---|
| baseUrl | string | Base URL of the Decision Keep instance. | https://decisionkeep.com |
| apiKey | string | Integration/org secret sent as the x-dk-secret header. | — |
| ingestPath | string | Ingest path. Defaults to /api/integrations/ingest. | /api/integrations/ingest |
| provider | string | Integration provider sent as x-dk-provider. Must match an org integration. | aws |
| timeoutMs | number | Hard timeout for a single dispatch (ms). | 2000 |
| bufferSize | number | Bounded in-memory retry buffer size. | 100 |
| flushIntervalMs | number | How often to retry buffered entries (ms). | 5000 |
Error handling
The SDK is designed to never throw and never block the caller. Errors are surfaced through result objects:
- •
record()returns{ queued, flushed, dropped, error? }. If the buffer overflows, the oldest entries are dropped and counted indroppedTotal. - •
verifyReceipt()never rejects. It resolves with{ ok, receipt?, error? }. - •Network failures, timeouts, and non-2xx responses are retried with exponential backoff up to 5 attempts. After exhaustion the entry is dropped and counted.
Browser vs Node.js
The SDK is dependency-free and runs anywhere with a standard fetch implementation and timers:
- •Browser: Uses
sendBeacon(viakeepalive: true) on page unload to flush the buffer without delaying navigation. - •Node.js / Edge: Uses a background timer for periodic flush. Call
dispose()on shutdown to flush synchronously. - •Serverless: The unref'd timer avoids blocking process exit, and
dispose()flushes pending entries before the function cold-starts.
Source & issues
The SDK lives in the sdk/ directory of the main repository. Report bugs and request features on GitHub:
See the Forensic Witness on your stack
Book a personalised demo and we'll map Decision Keep to your automated-decision obligations and show the evidence trail end to end.