Developers

Introducing the official Pandascrow SDK for Node.js

The official Pandascrow Node.js SDK is now on npm. Create escrows, send payouts, issue invoices and verify webhooks in a few lines of typed code.

Photo of Precious Tom

Precious Tom

3 min read

Image of Introducing the official Pandascrow SDK for Node.js

The official Pandascrow Node.js SDK is out. It's on npm as pandascrow, and it wraps the whole v3 API: escrow, wallets, payouts, bank transfers, virtual accounts, invoices, payment links, KYC and webhook verification.

If you've built on our REST API before, you know the routine. Set the Token header, pick the right base URL, check status on every response, and verify webhook signatures by hand. The SDK does that part, so your code reads like your business logic: create an escrow, release it, pay out.

Install the Node.js SDK

npm install pandascrow

It's written in TypeScript and ships its own types. It works with both import and require, and it has no runtime dependencies. It uses the built-in fetch, so it runs on Node.js 18+, Bun, Deno, Cloudflare Workers and Vercel Edge.

Warning

Use the SDK on your server only. Your API key gives full access to your Pandascrow account, so never ship it to a browser or a mobile app.

Create your first escrow

Grab your API key from Dashboard → Developers. The client starts in sandbox, so you can test without moving real money.

import Pandascrow from "pandascrow";

const ps = new Pandascrow(process.env.PANDASCROW_API_KEY!); // sandbox by default

const escrow = await ps.escrow.createOnetime({
  uuid,                        // the Pandascrow user you're acting for
  initiator_id: uuid,
  initiator_role: "seller",    // "buyer" | "seller" | "broker"
  title: "Apartment 4B rent",
  description: "12-month rent, 12 Admiralty Way",
  currency: "NGN",
  amount: 350000,
  buyer_details: { name: "Tenant", email: "tenant@example.com" },
});

console.log(escrow.payment_url); // send the buyer here to fund it

That's a complete one-time escrow. The buyer pays through payment_url, we hold the money, and it's released to the seller once the buyer confirms delivery. Every method returns a promise of the data object from the API, typed, so your editor shows you the fields as you type.

Going live is one option:

const ps = new Pandascrow(process.env.PANDASCROW_API_KEY!, { sandbox: false });

What the SDK covers

Each part of the API has its own property on the client:

PropertyWhat it does
escrowOne-time, milestone and recurring escrows, releases, disputes
wallet and payoutsBalances, transactions, settlement accounts, payouts
bankBank list, account validation, NGN transfers
virtualAccountsDynamic and static accounts to collect transfers
invoicesCreate, send, pay and cancel invoices
paymentLinksShareable links with custom slugs
kycBVN, NIN, CAC, TIN, NUBAN, phone and driver's licence lookups

For an endpoint we haven't wrapped yet, ps.request() sends any call with your key and base URL already set:

await ps.request("GET", "/clients", { uuid });

Milestone projects work the same way. Pass the stages and the buyer funds them one at a time:

await ps.escrow.createMilestone({
  uuid, initiator_id: uuid, initiator_role: "buyer",
  title: "Website build", description: "Company website", currency: "NGN",
  seller_details: { name: "Dev Studio", email: "studio@example.com" },
  deposit_option: "milestone",
  milestones: [
    { title: "Design", amount: 150000 },
    { title: "Build", amount: 350000 },
  ],
});

Verify webhooks in one call

We sign every webhook with HMAC-SHA256 of the raw request body, using your secret key, and send the signature in the X-Pandascrow-Signature header. verifyWebhook checks it, rejects events older than five minutes to stop replays, and returns the parsed event.

Here it is in a Next.js App Router route:

// app/api/webhooks/pandascrow/route.ts
import { verifyWebhook } from "pandascrow";

export async function POST(req: Request) {
  const raw = await req.text();
  try {
    const event = await verifyWebhook(
      process.env.PANDASCROW_SECRET_KEY!,
      raw,
      req.headers.get("x-pandascrow-signature"),
    );

    if (event.event === "escrow.paid") {
      // mark the order as funded
    }
    return new Response("ok");
  } catch {
    return new Response("invalid signature", { status: 400 });
  }
}
Tip

Always pass the raw body, not a parsed object. In Express, use express.raw({ type: "application/json" }) on the webhook route.

You'll receive events such as escrow.created, escrow.paid, escrow.completed, escrow.milestone.completed, escrow.dispute.opened, invoice.paid and wallet.deposit.success. The full list is in the webhooks reference.

Keep your own IDs on every record

Escrows, invoices and virtual accounts accept a metadata object. We store it as you sent it, return it when you fetch the record, and include it in every webhook for that record. So you don't need a lookup table to match a Pandascrow escrow to your order.

await ps.escrow.createOnetime({ ...details, metadata: { order_id: "A-1001" } });

// later, in your webhook handler
event.data.metadata?.order_id; // "A-1001"

Errors you can act on

Every failure rejects with a PandascrowError, whether it's a network error, a timeout, an HTTP error or a response with status: false. You get the message, the HTTP status, the full response and, where we have one, a link to the right page in the docs.

import { PandascrowError } from "pandascrow";

try {
  await ps.escrow.complete(uuid, escrowId, otp);
} catch (err) {
  if (err instanceof PandascrowError) {
    console.log(err.message); // "Invalid OTP"
    console.log(err.status);  // 400, or 0 for network errors
    console.log(err.docUrl);  // link to the API reference
  }
}

Bank transfers also take an idempotency key, so a retry after a timeout can't send the money twice:

await ps.bank.transfer(uuid, 25000,
  { account_number: "0123456789", account_name: "Ada Obi", bank_code: "058" },
  { idempotencyKey: "payout-7781" },
);

Prefer the terminal or PHP?

The Node.js SDK isn't the only new way in. The Pandascrow CLI (npm install -g pandascrow-cli) lets you create escrows, check balances and test webhooks from your terminal. PHP teams can use our PHP SDK on Packagist. SDKs for Python, Java, Ruby and Rust are in progress. Until they ship, every endpoint works over plain HTTPS from any language.

Start building

  1. Create a free account and get your sandbox key from the dashboard.
  2. Run npm install pandascrow.
  3. Follow the quick start on our developer page, or read the API reference.

The source is on GitHub under the MIT licence. If something's missing or broken, open an issue there or email us at hi@pandascrow.io. We read every one.

New to escrow? See how Pandascrow escrow protects both sides of a deal.

  • Node.js
  • SDK
  • TypeScript
  • Escrow API
  • Webhooks
  • Developers