npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

unit-client

v1.0.0

Published

Complete, production-grade TypeScript client for the Unit (unit.co) embedded banking API

Readme

unit-client

A complete, production-grade TypeScript client for the Unit embedded banking API — applications, customers, accounts, cards, payments, transactions, counterparties, repayments, recurring payments, check deposits, check payments, stop payments, chargebacks, statements, webhooks, events, tokens, institutions, authorizations, and sandbox simulation.

Zero runtime dependencies. Dual ESM/CJS. Full type declarations.

Design

unit-client mirrors the structure of the sibling unit and unit-go SDKs: a domain-driven design where each of Unit's 20 API surface areas is its own bounded-context module under src/domain/, with its own typed entities, value objects, and a *Service interface + factory function:

unit-client/
├── src/
│   ├── client.ts               # createUnitClient() facade wiring every bounded context together
│   ├── index.ts                  # package entry point / public exports
│   ├── shared/                     # shared kernel: Money, Address, FullName, Phone, Tags, Relationship
│   ├── telemetry/                    # request lifecycle instrumentation hooks
│   ├── internal/
│   │   ├── transport/                  # fetch-based client: auth, retry/backoff, idempotency, pagination
│   │   └── jsonapi/                      # JSON:API envelope types + decode helpers (anti-corruption layer)
│   └── domain/
│       ├── application/                    # KYC/KYB onboarding applications
│       ├── customer/                         # customers, authorized users
│       ├── account/                            # deposit accounts, limits, balance history
│       ├── card/                                 # debit/credit, individual/business, virtual/physical
│       ├── payment/                                # ACH, wire, book, bulk payments
│       ├── transaction/                              # the transaction ledger
│       ├── counterparty/                               # saved external bank accounts
│       ├── repayment/                                    # book/ACH credit repayments
│       ├── recurring-payment/                              # scheduled recurring payments/repayments
│       ├── check-deposit/                                    # mobile check deposit
│       ├── check-payment/                                      # print-and-mail check payments
│       ├── stop-payment/                                         # ACH/check stop payment orders
│       ├── chargeback/                                             # card transaction disputes
│       ├── statement/                                                # account statements (HTML/PDF)
│       ├── webhook/                                                    # webhook subscriptions + signature verification
│       ├── event/                                                        # the 90-day event log
│       ├── token/                                                          # customer/cardholder/org tokens, 2FA
│       ├── institution/                                                      # routing number lookup
│       ├── authorization/                                                      # pending card authorizations
│       └── sandbox/                                                              # sandbox event simulation

Every domain factory returns a plain object implementing that context's Service interface, so any part of UnitClient is independently mockable in tests without touching the rest.

Install

npm install unit-client

Requires Node 18.17+ (or any runtime with a global fetch and Web Crypto API — browsers, Deno, Bun, and edge runtimes all work; pass fetch explicitly via config for anything unusual).

Usage

import { createUnitClient, moneyFromDollars } from 'unit-client';

const client = createUnitClient({
  token: process.env.UNIT_TOKEN!,
  // baseUrl defaults to Unit's sandbox host; use 'https://api.unit.co' in production.
});

const account = await client.accounts.createDeposit({
  customerId: 'cus_123',
  depositProduct: 'checking',
  name: 'Primary Checking',
});

const payment = await client.payments.createAch({
  accountId: account.id,
  counterpartyId: 'cp_456',
  amount: moneyFromDollars(150),
  direction: 'Credit',
  description: 'Vendor payment',
});

console.log(payment.id, payment.status);

Pagination

List calls return the first page plus a lazy Paginator<T>, which supports both explicit paging and for await...of:

const { items, paginator } = await client.transactions.list({
  accountId: account.id,
  limit: 100,
});

// Explicit paging:
while (paginator.hasNext) {
  const next = await paginator.nextPage();
  items.push(...next);
}

// Or async iteration:
for await (const page of paginator) {
  console.log(page.length, 'more transactions');
}

// Or drain everything at once:
// const all = await paginator.all();

Errors

Every failed call rejects with a UnitError, which exposes the HTTP status, Unit's request ID, the JSON:API error objects, and whether the failure was retryable:

import { UnitError } from 'unit-client';

try {
  await client.accounts.get('does-not-exist');
} catch (error) {
  if (error instanceof UnitError) {
    console.log(error.statusCode, error.code, error.requestId);
  }
}

Retries

Every request automatically retries on 429 and 5xx responses (and on bare network failures) with exponential backoff and jitter, honoring Retry-After when Unit sends it. Configure or disable this:

import { createUnitClient, noRetry } from 'unit-client';

const client = createUnitClient({
  token,
  retryPolicy: { maxAttempts: 2, baseDelayMs: 100, maxDelayMs: 1000, jitter: 0.3 },
  // or: retryPolicy: noRetry(),
});

Idempotency

POST/PATCH requests get an auto-generated Idempotency-Key header when the caller doesn't supply one, so accidental retries never double-submit a payment. Supply your own for calls you want to control explicitly:

await client.payments.createAch({ ..., idempotencyKey: 'order-8842' });

Telemetry

Every request emits start/stop/retry/exception events through an injectable handler, for wiring into your metrics or logging stack:

const client = createUnitClient({
  token,
  telemetry: (event) => {
    metrics.observe(event.type, event.method, event.path, event.statusCode, event.durationMs);
  },
});

Webhook signature verification

import { verifySignatureSha512 } from 'unit-client';

const isValid = await verifySignatureSha512(
  rawBody,
  request.headers.get('x-unit-signature')!,
  signingKey,
);
if (!isValid) {
  return new Response('invalid signature', { status: 401 });
}

verifySignatureSha1 is also available for webhook subscriptions still on Unit's legacy signing scheme. Both use the Web Crypto API (crypto.subtle) and constant-time comparison — no third-party crypto dependency.

Money

Amounts are a branded Money type (an integer number of cents) so dollar values can't accidentally be passed where cents are expected:

import { money, moneyFromDollars, moneyToDollars, formatMoney } from 'unit-client';

const amount = moneyFromDollars(19.99); // 1999
formatMoney(amount); // "$19.99"

Testing this package

npm run typecheck   # tsc --noEmit
npm run lint         # eslint . (strict type-checked)
npm run format:check # prettier --check .
npm run test:coverage
npm run build         # tsup -> dist/ (ESM + CJS + .d.ts)
npm run verify          # all of the above, in order

The test suite runs entirely against an injected fetch mock — no network access or live Unit credentials required.

A note on field coverage

Resource attribute sets here reflect Unit's well-documented, stable API shapes as of this package's construction. Unit's OpenAPI spec is the source of truth for exact field names and any newly added attributes; if you hit a field this package doesn't expose yet, it's straightforward to extend the relevant src/domain/<context> module's attribute interface — the JSON:API decode plumbing in internal/jsonapi and internal/transport doesn't need to change.

License

MIT