Package@compense/sdk (ESM and CommonJS, with TypeScript declarations)
Version1.1.0 · API v1
RequirementsNode.js 18+ (uses native fetch); also Deno, Bun, Cloudflare Workers and Vercel/Next.js edge
DependenciesNone at runtime
RepositoryAppiasoft/compense-saas-sdk-js · guides in docs/ · examples in examples/

Install #

Shell
npm install @compense/sdk
TypeScript
import { CompenseClient } from "@compense/sdk";        // ESM / TypeScript
// const { CompenseClient } = require("@compense/sdk"); // CommonJS

Client #

TypeScript
const client = new CompenseClient({
  apiKey: process.env.COMPENSE_API_KEY!, // required; sent as X-API-Key
  environment: "sandbox",                // "production" (default) | "sandbox" | "dev"
  baseUrl: undefined,                    // another URL; takes precedence over environment
  timeout: 30_000,                       // ms per attempt
  retry: { maxAttempts: 3, backoffBaseMs: 500, backoffMaxMs: 30_000, jitter: true, retryWrites: false },
  fetch: undefined,                      // your own fetch (proxy, instrumentation, tests)
  logger: console,                       // deprecation warnings
});

There’s a single client and everything returns a Promise. The key is a server secret: even though the client works in browsers, never put a key in frontend code.

Conventions #

  • camelCase methods and an options object for parameters: client.wallet.bills.pay({ document, billerCode, reference, amount, idempotencyKey }).
  • Free-form fields (extra, and fields in the administrative module) are sent as is, with the contract’s snake_case names.
  • Durations in milliseconds (timeout, backoffBaseMs), except err.retryAfter, which is in seconds like the header.
  • Path parameters are percent-encoded.

Errors #

TypeScript
import {
  CompenseError, CompenseConfigError, CompenseAPIError, AuthError, NotFoundError,
  IdempotencyConflictError, ValidationError, RateLimitError, UpstreamError, ServerError,
  CompenseConnectionError, CompenseTimeoutError,
} from "@compense/sdk";

Unlike Python, transport failures are typed: CompenseConnectionError (and its subclass CompenseTimeoutError). They aren’t retried automatically: resend with the same idempotencyKey.

Async pagination #

Page and OffsetPage walk through all following pages with for await, repeating the same call with the same filters:

TypeScript
for await (const delivery of await client.webhooks.deliveries.list({ status: "failed" })) {
  await client.webhooks.deliveries.replay(String(delivery.id));
}

const first = await client.wallet.transactions.list("1032456789", { limit: 100 });
for await (const page of first.iterPages()) console.log(page.offset, page.length);
const next = await first.getNextPage(); // or null

Webhooks #

ImportRuntimeAPI
@compense/sdk/webhooksNode.js (also Bun, Deno and Workers with Node compatibility)synchronous verifyWebhook(...)
@compense/sdk/webhooks/webWeb Crypto: browsers, Cloudflare Workers, Vercel/Next.js edge, Denoawait verifyWebhook(...)
TypeScript
import { verifyWebhook, WebhookVerificationError } from "@compense/sdk/webhooks";

const event = verifyWebhook(rawBody, req.headers, [NEW_SECRET, OLD_SECRET], { tolerance: 300 });
event.eventId; event.event; event.resource; event.isTest;

It takes the raw body (express.raw(), never express.json()). Complete receivers are in Verifying the signature.

CLI #

The package includes the compense command (Node): npx compense health, events, endpoints, endpoint-create, trigger, deliveries, replay, clock, simulate, listen and --version. See CLI.

Testing #

Pass your own fetch:

TypeScript
import { CompenseClient } from "@compense/sdk";

const fakeFetch: typeof fetch = async (input) => {
  const url = new URL(String(input instanceof Request ? input.url : input));
  if (url.pathname === "/v1/gateway/wallet/balance/") {
    return new Response(JSON.stringify({ total: "150000.00", available_cash: "150000.00", currency: "COP" }),
      { status: 200, headers: { "content-type": "application/json" } });
  }
  return new Response(JSON.stringify({ title: "Not Found", status: 404, trace_id: "t" }), { status: 404 });
};

const client = new CompenseClient({ apiKey: "cmp_test", environment: "sandbox", fetch: fakeFetch });
console.assert((await client.wallet.balance("1032456789")).available.amount === "150000.00");

Differences from the Python SDK #

Same operations, bodies, idempotency and retries (the test suite compares each call with what Python sends), with these deliberate differences: a single async client; durations in ms; options objects instead of named arguments; none of the deprecated 1.0 aliases; typed transport errors; async pagination; two webhook verification entry points.