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

moov-client

v1.0.0

Published

Production-grade, zero-dependency TypeScript SDK for the Moov payments API — accounts, transfers, cards, ACH, issuing, wallets, and more.

Readme

moov-client

A production-grade, zero-runtime-dependency TypeScript SDK for the Moov payments API — accounts, transfers, cards, ACH, issuing, wallets, disputes, and 30+ more resource areas covering the full API surface.

  • Zero runtime dependencies — built entirely on the Web Fetch API, Web Crypto, and FormData, so it runs unmodified on Node.js 18+, browsers, Deno, Bun, Cloudflare Workers, and other Fetch-compatible edge runtimes.
  • Dual ESM/CJSimport and require() both work out of the box, with full type declarations for each.
  • Fully typed — every request and response shape is a hand-derived TypeScript interface, not any.
  • Idempotent by default — mutation-safe endpoints (like transfers.create) auto-generate a UUID v4 idempotency key that's reused across retries.
  • Automatic retries — transient failures (429, 5xx, network errors) are retried with exponential backoff and full jitter, honoring the API's Retry-After hints.
  • Webhook verification — constant-time HMAC-SHA512 signature verification via a separate moov-client/webhooks entry point (keeps crypto code out of your main bundle unless you need it).

Install

npm install moov-client

Quick start

import { Moov } from "moov-client";

const moov = new Moov({
  publicKey: process.env.MOOV_PUBLIC_KEY!,
  privateKey: process.env.MOOV_PRIVATE_KEY!,
});

const account = await moov.accounts.create({
  accountType: "business",
  profile: {
    business: {
      legalBusinessName: "Whole Foods",
      businessType: "llc",
    },
  },
});

const transfer = await moov.transfers.create(account.accountID, {
  source: { paymentMethodID: sourcePaymentMethodID },
  destination: { paymentMethodID: destinationPaymentMethodID },
  amount: { currency: "USD", value: 2500 },
});

console.log(transfer.transferID, transfer.status);

Client-side (e.g. with a Moov.js-issued token) instead of a public/private key pair:

const moov = new Moov({ token: shortLivedAccessToken });

Error handling

Every method rejects with one of three typed errors:

| Class | When | | ------------------ | ------------------------------------------------------------------ | | MoovAPIError | Moov returned a non-2xx response | | MoovNetworkError | The request never reached Moov (DNS, TCP, TLS, timeout, abort) | | MoovDecodeError | A 2xx response body wasn't valid JSON |

import { isNotFound, isValidationError, MoovAPIError } from "moov-client";

try {
  return await moov.accounts.get(accountID);
} catch (err) {
  if (isNotFound(err)) return null;
  if (isValidationError(err)) {
    console.error("validation failed:", err.body);
  }
  if (err instanceof MoovAPIError) {
    console.error(err.statusCode, err.requestID, err.message);
  }
  throw err;
}

Other guards: isUnauthorized, isForbidden, isConflict, isRateLimited, isTransientError.

Idempotency

transfers.create() is idempotent by nature on Moov's API, so the SDK auto-generates a UUID v4 X-Idempotency-Key and reuses the same key across every retry of a single logical call. To stay deduplicated across process restarts (e.g. a webhook handler that might be invoked twice for the same order), pass your own key:

await moov.transfers.create(accountID, req, {
  idempotencyKey: `order-${orderID}`,
});

Retries & timeouts

const moov = new Moov({
  publicKey,
  privateKey,
  maxRetries: 3, // additional attempts beyond the first (default: 3)
  retryBaseDelayMs: 250, // default: 250
  retryMaxDelayMs: 8000, // default: 8000
  timeoutMs: 30_000, // per-request timeout; 0 disables it (default: 30000)
  onRetry: (attempt, delayMs, err) => console.warn(`retry ${attempt} in ${delayMs}ms`, err),
});

Every call also accepts a signal (AbortSignal) as part of its per-request options:

const controller = new AbortController();
await moov.accounts.get(accountID, { signal: controller.signal });

Webhooks

Import from the dedicated moov-client/webhooks entry point so signature-verification code (and its Web Crypto usage) stays out of bundles that don't need it:

import { parseEvent, InvalidSignatureError } from "moov-client/webhooks";

// Express example — req.headers works directly; for raw bytes/text, pass those instead.
app.post("/webhooks/moov", express.raw({ type: "application/json" }), async (req, res) => {
  try {
    const event = await parseEvent(req.headers, req.body, process.env.MOOV_WEBHOOK_SECRET!);
    switch (event.type) {
      case "transfer.updated":
        // event.data is `unknown` — validate/cast to your own payload shape
        break;
    }
    res.sendStatus(200);
  } catch (err) {
    if (err instanceof InvalidSignatureError) return res.sendStatus(401);
    throw err;
  }
});

parseEvent/verifySignature accept a native Headers, a Map, or a plain object as the header source, so they work the same in Express, Fastify, Next.js Route Handlers, or Cloudflare Workers.

Resources

Every one of Moov's resource areas is available as a property on the Moov instance:

| Property | Covers | | ------------------------------------------ | ---------------------------------------------------- | | accounts, capabilities, representatives, underwriting, billing, files, onboarding, resolution, partner | Account lifecycle, KYC/KYB, billing, partner tooling | | bankAccounts, cards, applePay, googlePay, paymentMethods, terminal, wallets | Payment sources & rails | | transfers, sweeps, refunds, disputes, issuing, invoices, paymentLinks, receipts, schedules | Money movement | | images, products, support | Account-level tooling | | branding, enrichment, institutions | Enrichment & lookups | | auth, e2ee | OAuth2 tokens & end-to-end encryption keys |

Every method also accepts a final RequestOptions argument (idempotencyKey, waitFor, apiVersion, maxRetries, signal, headers) to override defaults for a single call.

Requirements

  • Node.js 18+ (for native fetch, FormData, and crypto.subtle), or any modern browser / edge runtime with the same APIs.
  • TypeScript 5+ if you want the bundled type declarations (the package works fine from plain JavaScript too).

License

MIT