Skip to main content

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/client

Quick 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): RecordResult

Queue 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.

OptionTypeDescriptionDefault
baseUrlstringBase URL of the Decision Keep instance.https://decisionkeep.com
apiKeystringIntegration/org secret sent as the x-dk-secret header.
ingestPathstringIngest path. Defaults to /api/integrations/ingest./api/integrations/ingest
providerstringIntegration provider sent as x-dk-provider. Must match an org integration.aws
timeoutMsnumberHard timeout for a single dispatch (ms).2000
bufferSizenumberBounded in-memory retry buffer size.100
flushIntervalMsnumberHow 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 in droppedTotal.
  • 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 (via keepalive: 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:

github.com/jluketic/decisionkeep

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.

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.