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

@venlyfinance/sdk

v0.8.2

Published

TypeScript SDK for the Venly Finance and Fundflow APIs. Generated from the OpenAPI specs, zero runtime dependencies.

Readme

Venly Finance TypeScript SDK

TypeScript SDK for the Venly Finance and Fundflow APIs. Types are generated from the OpenAPI specs in specs/; the runtime layer is hand-written and has zero runtime dependencies (Node ≥ 18, or any environment with fetch).

MIT licensed. See the CHANGELOG for the current version and what each release added.

Building with a coding agent? Start from AGENTS.md.

Packages in this repository

| Package | What it is | |---|---| | @venlyfinance/sdk (root) | The TypeScript SDK: typed client for every Finance + Fundflow operation | | @venlyfinance/settlement-mcp | Venly Finance MCP: SDK-backed tools, runtime-contract blueprints and deterministic review/verify gates for building international money experiences. Mock-first; live writes fail closed. | | @venlyfinance/react | Headless React layer: provider, TanStack Query hooks, and flow state machines for staged transfers, four-eyes approval, and ramp lifecycles | | @venlyfinance/ui | Copy-owned UI kit: design tokens (the white-label contract) plus components encoding fintech density, money typography, and state-legibility rules. Not npm-published | | examples/mock-bank | Runnable example assembling the two: a full account experience in mock mode – fake data, zero credentials. npm install && npm run dev |

Build an international account experience with an AI agent

The MCP extends this SDK rather than maintaining a second API client. In explicit mock mode it gives a coding agent atomic party, account, wallet/balance, EUR receiving account and transfer tools without credentials or network access. It also publishes a build_international_account prompt plus capability and safety resources. Its initialize response and journey blueprints push the package/provider/hook contract, while the review and verify CLIs gate screen and runtime composition in generated apps.

The product boundary is deliberate: Venly supplies financial infrastructure through regulated partners. Creating a party does not complete KYC/KYB; the current public contract documents EUR/SEPA virtual bank accounts and does not expose card issuing.

See the Venly Finance MCP quickstart.

What the SDK handles for you

| Concern | Behaviour | |---|---| | Auth | OAuth2 client credentials. Tokens expire after ~5 min; cached, refreshed 30 s early, single-flighted, transparently re-fetched on a 401. | | Idempotency | Every mutating request (POST/PUT/PATCH) carries an Idempotency-Key. Pass your OWN key and reuse it across retries – the auto-generated one is fresh per call, so a retry without your own key is a second operation (and, in mock mode, a second debit). | | Retries | Exponential backoff + jitter on 429/502/503/504 and network errors, Retry-After respected, 3 attempts by default. | | Errors | Non-2xx throws VenlyApiError with status, errors[] and the traceCode to quote at support. | | Envelope | {success, errors[], result} is unwrapped – methods return result directly. | | Pagination | list() returns {items, pagination}; iterate() walks all pages as an async iterator. |

Try it in 0 minutes (mock mode)

No signup, no credentials, no network. Mock mode is a stateful, spec-validated simulation of the documented lifecycle: creates mint real ids and read back, verification starts pending (exactly as the docs describe - creating a party starts KYC/KYB, it does not complete it), transfers start PENDING, and request bodies are validated against the vendored OpenAPI specs so an invented field fails here instead of in staging.

import { VenlyFinanceClient } from "@venlyfinance/sdk";

const venly = new VenlyFinanceClient({ environment: "mock" });

const party = await venly.parties.create({
  partyType: "INDIVIDUAL", firstName: "Ada", lastName: "Lovelace",
});
party.kycStatus;                                       // "VERIFICATION_PENDING" - honest
venly.mock!.advanceVerification(party.id!);            // play the Venly admin
(await venly.parties.get(party.id!)).kycStatus;        // "VERIFIED"

// A fresh account holds nothing, exactly as in production. Money arrives the way
// it really does: a credit lands on a vIBAN and converts to that vIBAN's target
// asset - so fund in the asset you intend to send (EUR transfers spend EURC).
venly.mock!.simulations.inbound.credit(eurcIban.id!, 500);   // -> 500 EURC available

const transfer = await venly.transfers.createFiat(accountId, {
  receiverAccountId, currency: "EUR", amount: 25, idempotencyKey: crypto.randomUUID(),
});
transfer.status;                                       // "PENDING" - poll it like production
venly.mock!.advanceTransfer(transfer.id!);             // → COMPLETED, with a transactionHash
venly.mock!.advanceTransfer(other.id!, "FAILED");      // exercise the failure path

venly.mock!.failNext("NOT_FOUND");                     // simulate an API error
venly.mock!.respondNext({ success: true }, "GET /accounts/{accountId}/virtual-bank-accounts");
venly.mock!.delayNext(1500, "GET /accounts/{accountId}/virtual-bank-accounts");
venly.mock!.calls;                                     // inspect everything your code sent
venly.mock!.reset();                                   // back to the seed fixtures

Error simulation throws the same VenlyApiError the live API produces (failNext("OPTIMISTIC_LOCK_EXCEPTION"), or a custom {status, code, message}; add a route filter like failNext("VALIDATION_ERROR", "POST /parties")). respondNext and delayNext accept the same exact route filter, so sparse response envelopes and loading states can be exercised without network calls. Paginated SDK results include resultPresent; it is false when a response omits result, which lets applications distinguish malformed data from a genuine empty list. Ready for real calls? Change environment to "staging" and add clientId/clientSecret - nothing else changes, and the same options object type-checks in all three environments.

Quickstart

import { VenlyFinanceClient } from "@venlyfinance/sdk";

const venly = new VenlyFinanceClient({
  clientId: process.env.VENLY_CLIENT_ID!,
  clientSecret: process.env.VENLY_CLIENT_SECRET!,
  environment: "staging", // or "production" (default)
});

// Onboard a party, open an account (its wallet is auto-provisioned), assign an IBAN
const party = await venly.parties.create({
  partyType: "INDIVIDUAL",
  firstName: "Ada",
  lastName: "Lovelace",
});

const account = await venly.accounts.create({
  externalId: "customer-42",
  chain: "BASE",
  partyId: party.id,
});

const wallets = await venly.wallets.list(account.id!);

// vIBANs require a KYC-VERIFIED account. Live, a Venly admin verifies it;
// in mock mode play the operator: venly.mock?.advanceVerification(account.id!)
const iban = await venly.virtualBankAccounts.create(account.id!, {
  name: "EUR Payouts",
  inCurrency: "EUR",
  targetCryptocurrency: "USDC",
  idempotencyKey: crypto.randomUUID(),
});

// A new mock account holds nothing, exactly as in production. Fund it the way
// money really arrives: a credit lands on the vIBAN and converts to the
// account's target asset.
// The credit converts to this vIBAN's targetCryptocurrency, so it lands as USDC
// and funds USDC-denominated sends. To fund EUR sends, create a vIBAN with
// targetCryptocurrency: "EURC" - the asset you fund is the asset you can spend.
venly.mock!.simulations.inbound.credit(iban.id!, 500);   // -> 500 USDC available

// Transfers now debit for real: a PENDING transfer reserves against `available`,
// settling moves it out of `total`, and an over-balance send is refused with
// 402 insufficient-funds instead of silently succeeding.
// simulations.ledger.verify() asserts the books balance at any point.

Card settlement lifecycle

// Reserve funds for an authorization, then settle (or reverse) it.
const pr = await venly.paymentRequests.create(account.id!, {
  amount: 25, currency: "USD", idempotencyKey: crypto.randomUUID(),
});

await venly.paymentRequests.settle(pr.id!, {
  amount: 25, currency: "USD", idempotencyKey: crypto.randomUUID(),
}); // 202 → status SETTLING, then SETTLED once on-chain transfers confirm

// or void it:
await venly.paymentRequests.reverse(pr.id!, {
  reason: "MERCHANT_VOID", idempotencyKey: crypto.randomUUID(),
});

Third-party payouts (contract 1.3.0)

Crypto leaves the account; fiat lands in a registered beneficiary bank account. Three resources deep: a bank account on the party, a route on the account (activated by wallet-ownership proof), payouts against the route.

// Payouts require a VERIFIED account. In mock mode, play the operator for
// both pending states: account KYC and the beneficiary bank account below.
venly.mock?.advanceVerification(account.id!);

const beneficiary = await venly.payoutBankAccounts.register(party.id!, {
  rail: "SEPA", fiatCurrency: "EUR", accountHolderName: "Supplier GmbH",
  railDetails: { iban: "DE89...", bic: "DEUTDEDBFRA" },
}); // starts PENDING; an operator activates it
venly.mock?.advancePayoutBankAccount(beneficiary.id!, "ACTIVE");

const route = await venly.payoutRoutes.create(account.id!, {
  payoutBankAccountId: beneficiary.id!,
  depositAsset: { chain: "BASE", name: "USDC" },
}); // AWAITING_OWNERSHIP_PROOF until the funding wallet signs

// No body: the server derives the funding wallet and chain from the route.
const proof = await venly.payoutRoutes.prepareOwnershipProof(account.id!, route.id!);
await venly.payoutRoutes.completeOwnershipProof(account.id!, route.id!, {
  message: proof.message!, signature: "0x...",
}); // route ACTIVE

const payout = await venly.payouts.request(account.id!, {
  payoutRouteId: route.id!, cryptoAmount: 250.5,
  idempotencyKey: crypto.randomUUID(),
}); // REQUESTED → SENDING → PROVIDER_PROCESSING → COMPLETED | REJECTED | FAILED | RETURNED

// Mock drivers walk the lifecycle: venly.mock.advancePayout(payout.id!, "COMPLETED")

Supported assets and decimals

Every asset carries its on-chain decimals – the render contract for amounts. A UI that assumes two decimals shows a 6-decimal asset's sub-cent balance as 0.00, and its totals stop reconciling with the rows.

const assets = await venly.supportedAssets.list(); // tenant-wide, with decimals
const decimals = new Map(assets.items.map((a) => [a.contractAddress, a.decimals]));

// Per account: the same rows plus permitStatus (READY, ACTIVATING,
// ACTION_REQUIRED, PENDING, FAILED, NO_WALLET).
const mine = await venly.supportedAssets.listForAccount(account.id!);

Pagination

for await (const party of venly.parties.iterate({ status: "ACTIVE" })) {
  console.log(party.id);
}

Errors

import { VenlyApiError } from "@venlyfinance/sdk";

try {
  await venly.transfers.createFiat(accountId, body);
} catch (err) {
  if (err instanceof VenlyApiError) {
    console.error(err.status, err.traceCode, err.errors);
  }
}

Fundflow (on/off-ramps with four-eyes approvals)

import { FundflowClient } from "@venlyfinance/sdk";

const fundflow = new FundflowClient({ clientId, clientSecret, environment: "staging" });

const ramp = await fundflow.rampRequests.create({ /* ... */ });
// maker-checker: the API rejects self-approval server-side, and approvals
// carry the optimistic-locking version of the request you reviewed
await fundflow.rampRequests.approve(ramp.id!, { version: ramp.version! });

Escape hatch

Any endpoint without a named wrapper is still reachable with auth, retries and idempotency applied:

const user = await fundflow.request("GET", "/v1/auth/user");

Development

npm install
npm run generate   # regenerate src/generated/ from specs/*.yaml
npm run build      # ESM + CJS + d.ts into dist/
npm test           # build + node:test suite (mocked fetch, no network)

The vendored specs in specs/ are the source of truth for the generated types. When the API changes, update the spec, run npm run generate, and the compiler surfaces every affected call site.