Skip to content

fintech-sdk/treasuryprime-sdk

Repository files navigation

treasuryprime-sdk

An unofficial, production-grade TypeScript client for the Treasury Prime Ledger API — bank accounts, ACH, wires, book transfers, FedNow, cards, KYC/KYB account opening, check deposit, webhooks, sandbox simulations, and more.

Zero runtime dependencies. Dual ESM/CJS with bundled type declarations. Node.js 18.17+.

Install

npm install treasuryprime-sdk

Quick start

import { Client, TreasuryPrimeError } from "treasuryprime-sdk";

const client = new Client(
  process.env.TREASURY_PRIME_KEY_ID!,
  process.env.TREASURY_PRIME_KEY_VALUE!,
  { environment: "sandbox" }, // default; use "production" when you're ready
);

try {
  const account = await client.accounts.get("acct_0112233445566");
  console.log(`${account.name}: ${account.available_balance} available`);
} catch (err) {
  if (err instanceof TreasuryPrimeError) {
    console.error(`treasury prime error (${err.type}, HTTP ${err.statusCode}): ${err.message}`);
  }
  throw err;
}

Architecture

A lightweight ports-and-adapters (hexagonal) layout, scaled to what an API client actually needs — there's no business logic to isolate, so the boundary that matters is the network:

  • PortFetch (the shape of the global fetch function) is the one seam between this library and the network. Swap in an instrumented, rate-limited, or test-double implementation via ClientOptions.fetch.
  • Adapter — the internal Transport class is the one concrete adapter: it turns method calls into authenticated, retried, JSON HTTP requests and turns responses into typed values or TreasuryPrimeError.
  • Domain — every resource (Account, Ach, Wire, Card, ...) is a plain TypeScript interface mirroring the wire format. Each resource's *Service class (AccountService, AchService, ...) is a thin, explicit adapter mapping list/get/create/update/delete onto Transport calls — a repository pattern, with the repetitive plumbing shared via a BaseService<T> and each resource's own file keeping its documented, typed public methods.

Resource coverage

Domain Client property Notes
Accounts accounts + lock/unlock, setInterestRate, averageBalances, dailyBalances, transactions, pendingTransactions, generateStatement/fetchStatement, tax documents
Account applications accountApplications
Account locks accountLocks read-only history
Account number reservations accountNumberReservations
Account products accountProducts read-only
ACH ach + cancel
Additional person applications additionalPersonApplications
Book transfers books
Businesses businesses
Business applications businessApplications
Cards cards + activate/suspend/terminate/reissue; get supports show_pan/show_cvv via options.query
Card auth loop endpoints cardAuthLoopEndpoints
Card events cardEvents read-only
Card products cardProducts
Checks checks + cancel, stopPayment
Check deposits checkDeposits
Counterparties counterparties
Deposits (account funding) deposits
Deposit sweeps depositSweeps
Digital wallet tokens digitalWalletTokens + provisionWithApplePay, provisionWithGooglePay
Documents documents
FedNow fednow + routingNumberStatus
Files files raw upload/download/get
Green Dot greendot + locations
Incoming ACH incomingAch + returnTransfer
Incoming wires incomingWires read-only
Invoice account numbers invoiceAccountNumbers
KYC kyc
KYC products kycProducts read-only
Manual holds manualHolds + release
Network transfers networkTransfers
People people
Person applications personApplications
Reserve accounts reserveAccounts
Routing numbers routingNumbers read-only lookup
Statement configs statementConfigs
Transactions transactions
Webhooks webhooks + enable/disable
Wires wires + cancel
Marqeta marqeta jsAccessToken, uxToolkitAccessToken (for Marqeta-processor programs)
Sandbox simulations simulation create (general) + achStatus, wireStatus, checkDepositStatus, fedNowReceive, greendotComplete, cardAuthRequest

Idempotency

Every create accepts an idempotencyKey option:

import { generateIdempotencyKey } from "treasuryprime-sdk";

const key = generateIdempotencyKey();
const ach = await client.ach.create(
  {
    account_id: "acct_1",
    counterparty_id: "cp_1",
    amount: "104.20",
    direction: "debit",
  },
  { idempotencyKey: key },
);

Generate one key per business operation and persist it alongside that operation — reuse it across retries of the same operation rather than minting a fresh one per HTTP call.

Pagination

list() returns a Page<T>, which implements AsyncIterable<T> — the idiomatic way to consume it is for await...of, which fetches pages lazily and stops as soon as you break:

const page = await client.ach.list({ query: { status: "pending" } });

for await (const transfer of page) {
  if (transfer.amount === "0.00") break; // no further pages are fetched
  console.log(transfer.id, transfer.status);
}

// or, eagerly collect everything:
const all = await page.all();

// or iterate page-by-page instead of item-by-item:
for await (const p of page.pages()) {
  console.log(`got a page of ${p.data.length} items`);
}

Webhooks

import { validateWebhookSignature, WebhookEvent } from "treasuryprime-sdk";

app.post("/webhooks/treasury-prime", async (req, res) => {
  if (!validateWebhookSignature(req.headers["authorization"], whUser, whSecret)) {
    return res.sendStatus(401);
  }

  const event = WebhookEvent.parse(req.rawBody);

  if (event.event.startsWith("ach.")) {
    const ach = await event.fetch<Ach>(client);
    // handle ach
  }

  res.sendStatus(200);
});

Treasury Prime intentionally sends a thin payload and doesn't guarantee delivery order — always re-fetch via WebhookEvent#fetch rather than trusting embedded state.

Sandbox simulations

await client.simulation.achStatus(achId, "settled");

Fails against a "production" client — Treasury Prime enforces this itself.

Errors

Every network-touching call rejects with a TreasuryPrimeError — always instanceof TreasuryPrimeError, with a type ("api_error", "network_error", "timeout", "validation_error", "decode_error"), and for API errors, statusCode and the decoded response body.

Escape hatch

Endpoints not covered by a typed resource method (or a brand-new one Treasury Prime just shipped) are reachable via the same authenticated, retried transport everything else uses:

const result = await client.request<Record<string, unknown>>("GET", "/some/new/endpoint");

Configuration

const client = new Client(keyId, keyValue, {
  environment: "production",
  maxRetries: 3,
  retryBaseDelayMs: 500,
  maxRetryDelayMs: 10_000,
  receiveTimeoutMs: 30_000,
  fetch: myInstrumentedFetch, // matches the global fetch signature
  onRequest: ({ method, url }) => logger.debug(`${method} ${url}`),
});

Every method also accepts a per-call options object (idempotencyKey, headers, query, signal) as its last argument.

A note on field fidelity

Treasury Prime doesn't publish a single machine-readable schema. The field set here was extracted from a companion, hand-maintained Elixir client and cross-checked against Treasury Prime's docs where available. Entity and params interfaces mirror the wire format directly (snake_case, matching Treasury Prime's JSON) rather than being translated to camelCase, so what you see in a debugger or curl response matches what you see in your editor. Deeply nested, bank-specific, or rarely-documented sub-objects (physical_address, bankdata, card_controls, fx_quote, and similar) are intentionally typed as Record<string, unknown> rather than guessed-at nested interfaces, so this client never silently drops a field Treasury Prime adds or a particular bank partner requires. Verify field-level behavior against Treasury Prime's docs before relying on it for anything money-moving.

Development

npm run check   # typecheck, lint, format check, test
npm run build   # dual ESM/CJS + .d.ts via tsup

License

MIT — see LICENSE.

About

An unofficial, production-grade TypeScript client for the [Treasury Prime](https://www.treasuryprime.com/) Ledger API — bank accounts, ACH, wires, book transfers, FedNow, cards, KYC/KYB account opening, check deposit, webhooks, sandbox simulations, and more.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages