TypeScript SDK
Official @zapapi/sdk package — typed access to every ZapAPI API: payments, wallets, addresses, marketplace, and the AI gateway. Zero dependencies.
The official ZapAPI SDK is the fastest way to integrate. It's a zero-dependency TypeScript client that wraps the whole REST API with full types, idempotency-key support, streaming AI completions, and built-in webhook signature verification. Works in Node 18+, Deno, Bun, and modern browsers.
Install
npm install @zapapi/sdkWorks in Node 18+, Deno, Bun, and modern browsers. TypeScript types are
included — no @types/* package needed.
Quick start
import { ZapAPI } from '@zapapi/sdk';
const zap = new ZapAPI(process.env.ZAPAPI_KEY); // zap_live_… or zap_test_…
// Create an invoice (BOLT11) — settles to the project's settlement wallet.
// With a project-scoped key (Dashboard → Project → API Keys), the API key
// is all you need — no projectId, no providerId.
const invoice = await zap.invoices.create(
{
amount: 21000,
description: 'Pro plan',
},
'unique-idempotency-key', // safe retries — never double-charge
);
console.log(invoice.paymentRequest); // lnbc210n1…Where do the sats land?
Every project has a settlement wallet (Dashboard → Project → Settlement
Wallet): your default wallet, a dedicated project wallet, an account wallet,
or your own external wallet via NWC. Invoices always settle there — you
never pass a provider id. With an account-level key, pass
projectId: 'proj_…' explicitly.
You can also pass a config object for full control:
const zap = new ZapAPI({
apiKey: process.env.ZAPAPI_KEY,
baseUrl: 'https://api.zapapi.com/v1', // override for self-hosted
timeoutMs: 30_000,
fetchFn: myFetch, // custom fetch (older Node, proxies, testing)
});Test mode
Keys starting with zap_test_ create test invoices. Webhook events carry
livemode: false, and the client exposes a convenience flag:
const zap = new ZapAPI('zap_test_…');
zap.isTestMode; // trueFiat-denominated invoices
Pass a fiat currency and the invoice is converted to sats at the current rate. The rate is snapshotted on the invoice for audit:
const invoice = await zap.invoices.create({
amount: 25, // $25
currency: 'USD',
description: 'Pro plan',
});
console.log(invoice.amount); // 30684 (sats)
console.log(invoice.fiatRate); // 81476.00 (USD/BTC at creation)Supported fiat codes: USD, EUR, GBP, INR, BRL, JPY, CAD, AUD.
Idempotency
All POST endpoints accept an Idempotency-Key. Retrying with the same key
returns the same response — payments are never double-created:
await zap.invoices.create(params, `order-${orderId}`);If a request with the same key but a different body is replayed, the API
returns 409 Conflict (err.isConflict on the typed error).
Resources
Invoices
const invoice = await zap.invoices.create(params, idempotencyKey?);
const one = await zap.invoices.get(id);
const { invoices, total } = await zap.invoices.list({ projectId, status, limit, offset });
const fresh = await zap.invoices.checkStatus(id); // poll the settlement wallet
const cancelled = await zap.invoices.cancel(id); // pending invoices only
const { qrCode } = await zap.invoices.getQRCode(id); // data URL for <img>Refunds
// Full repayment to the customer's lightning address
const refund = await zap.refunds.create(invoice.id, {
destination: 'customer@wallet.com',
reason: 'Customer requested cancellation',
});
// Partial repayment
await zap.refunds.create(invoice.id, { destination: '…', amountSats: 5000 });
// Credit note (record only — settle off-band)
await zap.refunds.create(invoice.id, { type: 'credit', amountSats: 5000 });
const forInvoice = await zap.refunds.listForInvoice(invoice.id);
const { refunds, total } = await zap.refunds.list({ limit: 50 });Payment links (hosted checkout)
const link = await zap.paymentLinks.create({
description: 'Tip jar ☕',
type: 'flexible',
minAmount: 21,
maxAmount: 100000,
});
// share link.paymentUrl — QR via zap.paymentLinks.getQRCode(link.id)
await zap.paymentLinks.updateStatus(link.id, 'disabled');
const all = await zap.paymentLinks.list();
await zap.paymentLinks.delete(link.id);Customers (CRM)
// Project customers — auto-built from settled invoices/payment links
const { customers } = await zap.customers.list({ projectId: 'proj_…', segment: 'vip' });
const customer = await zap.customers.get(id);
const stats = await zap.customers.stats('proj_…');
const top = await zap.customers.leaderboard(10);Subscription plans (recurring billing)
// Create a plan on a project — renewals settle to the settlement wallet
const plan = await zap.plans.create({
name: 'Pro',
amountSats: 21000,
interval: 'month', // 'week' | 'month' | 'year'
trialDays: 7,
perks: ['Priority support'],
});
// Hosted checkout: https://zapapi.app/subscribe/<plan.id>
// Subscribers get a magic-link portal (pause / cancel / NWC auto-pay);
// failed renewals get a 3-day grace → past_due → cancel after 7 days.
const { subscriptions, total } = await zap.subscriptions.list({ status: 'active' });
const { mrrSats, arrSats, activeSubscribers } = await zap.subscriptions.revenue();
await zap.subscriptions.cancel(subscriptionId);Discounts (coupons & offers)
// Coupon — the customer enters the code at checkout
await zap.discounts.create({
projectId: 'proj_…',
code: 'LAUNCH20',
type: 'percent', // or 'amount' (fixed sats off)
percentOff: 20,
maxRedemptions: 100, // optional cap
});
// Offer — auto-applied at checkout in its window, no code needed
await zap.discounts.create({
projectId: 'proj_…',
mode: 'offer',
code: 'LAUNCH-WEEK',
type: 'percent',
percentOff: 15,
endsAt: '2026-11-04T00:00:00Z',
});
// The payer pays amount − discount; the discount is snapshotted on the invoice
const invoice = await zap.invoices.create({
amount: 21000,
description: 'Pro plan',
couponCode: 'LAUNCH20',
});Gift cards
// Sell a card — the buyer pays now into your settlement wallet;
// the card activates on settlement
const { card, purchase } = await zap.giftCards.issue({
balanceSats: 21000,
issueType: 'paid', // or 'free' (merchant-funded comp cards)
});
// The recipient redeems real sats into their own wallet
// (public — the code is the capability)
const { lnurl, remainingSats } = await zap.giftCards.redeem(card.code, 21000);
// → show lnurl as a QR; their wallet pulls the sats (LNURL-withdraw)Webhook endpoints
const endpoint = await zap.webhookEndpoints.create({
url: 'https://api.yourapp.com/hooks/zapapi',
events: ['invoice.paid', 'zap.received'],
projectId: 'proj_…',
});
// ⚠️ endpoint.secret is returned EXACTLY ONCE — store it for verification
const all = await zap.webhookEndpoints.list({ projectId: 'proj_…' });
await zap.webhookEndpoints.updateStatus(endpoint.id, 'disabled');
await zap.webhookEndpoints.delete(endpoint.id);Webhooks
ZapAPI signs every delivery with a Stripe-style t=,v1= scheme. Verify using
the raw request body (not the parsed JSON) and the signing secret shown
once when you created the endpoint:
import express from 'express';
import { ZapAPI } from '@zapapi/sdk';
const app = express();
app.post(
'/webhooks/zapapi',
express.raw({ type: 'application/json' }),
(req, res) => {
const ok = ZapAPI.webhooks.verify(
req.body.toString(),
req.headers['x-zapapi-signature'] as string,
process.env.ZAPAPI_WEBHOOK_SECRET!,
);
if (!ok) return res.sendStatus(400);
const event = JSON.parse(req.body.toString());
switch (event.type) {
case 'invoice.paid':
// fulfill the order — event.data.paymentHash, event.data.preimage
break;
case 'zap.received':
// someone zapped you — event.data.amountSats, event.data.senderPubkey
break;
}
res.json({ received: true });
},
);Replay protection: events older than 5 minutes are rejected by default.
Tune with the fourth parameter:
ZapAPI.webhooks.verify(raw, header, secret, toleranceSeconds).
Other frameworks — just make sure you pass the raw body:
| Framework | Raw body setup |
|---|---|
| Express | express.raw({ type: 'application/json' }) on the route |
| Fastify | fastify.addContentTypeParser('application/json', { parseAs: 'string' }, …) |
| Next.js (route handlers) | await req.text() — already raw |
| Hono | await c.req.text() — already raw |
Errors
All non-2xx responses throw a typed ZapAPIError with the HTTP status and
helpers for common cases:
import { ZapAPI, ZapAPIError } from '@zapapi/sdk';
try {
await zap.invoices.create(params);
} catch (err) {
if (err instanceof ZapAPIError) {
err.status; // 400, 401, 403, 409, 429, …
err.code; // machine-readable code when available
err.payload; // raw API error body
err.isUnauthorized; // 401 — bad/expired key
err.isForbidden; // 403 — missing scope
err.isNotFound; // 404
err.isConflict; // 409 — idempotency conflict
err.isRateLimited; // 429 — slow down
}
}AI Gateway
500+ models paid per query in sats — catalog, chat (streaming), balance, Lightning topups, usage, and NWC auto-topup:
// Browse the live catalog
const models = await zap.ai.listModels('chat');
// Non-streaming completion
const completion = await zap.ai.completions.create({
model: 'anthropic/claude-sonnet-5.5',
messages: [{ role: 'user', content: 'Hello!' }],
});
// Streaming (async generator of text deltas)
for await (const delta of zap.ai.completions.stream({
model: 'openai/gpt-6-luna',
messages: [{ role: 'user', content: 'Write a haiku about Lightning' }],
stream: true,
})) {
process.stdout.write(delta);
}
// Balance, topups, usage
const balance = await zap.ai.getBalance(); // sats
const topup = await zap.ai.createTopup(5000); // Lightning invoice
await zap.ai.checkTopup(topup.id); // credits when paid
const usage = await zap.ai.getUsageSummary(); // spend + top models
// NWC auto-topup (never hit a 402)
await zap.ai.saveAutoTopup({
nwcConnectionId: 'conn-uuid',
thresholdSats: 500,
topupSats: 5000,
dailyCapSats: 20000,
});AI-specific error helpers on ZapAPIError: isInsufficientBalance (402),
isModelNotFound (404), isModelRetired (404), isRateLimited (429).
See AI Gateway docs for the full model catalog, vision,
web search and thinking levels.
API coverage
Every user-facing API — payments, wallets, addresses, marketplace, and the AI gateway. (Platform-admin endpoints are intentionally excluded.)
| Resource | Methods |
|---|---|
zap.invoices | create, get, list, checkStatus, cancel, getQRCode |
zap.refunds | create, listForInvoice, list |
zap.customers | list, get, stats, leaderboard |
zap.plans | create, list, get, update, delete |
zap.subscriptions | list, revenue, cancel |
zap.discounts | create, list, update, delete, validate, resolveOffer |
zap.giftCards | issue, list, get, disable, check, checkPurchase, redeem |
zap.paymentLinks | create, get, list, updateStatus, delete, getQRCode |
zap.webhookEndpoints | create, list, get, updateStatus, delete |
zap.projects | create, list, listShared, get, getStats, update, delete |
zap.payments | list, get, getStats, getDailyStats, exportCsv |
zap.apiKeys | create, list, get, revoke, delete |
zap.fx | currencies, rates, rate, convert |
zap.providers (deprecated — use project settlement wallets / zap.nwcConnections) | create, list, get, test, disconnect, delete |
zap.wallet | getInfo, listWallets, createWallet, updateWallet, deleteWallet, setDefaultWallet, getPayments, checkPayment, createInvoice, cancelInvoice, payInvoice, payLnurl, decode, lnurlScan |
zap.nwcConnections | list, create, get, update, refresh, payInvoice, delete |
zap.lightningAddresses | listDomains, search, register, list, get, setActive, setWallet, delete |
zap.paywalls | create, list, get, setStatus, delete |
zap.swaps | getInfo, list, createSubmarine, createReverse, get, refresh |
zap.withdrawals | create, list, get, cancel, getLimits |
zap.directory | listApps, getApp, categories, boostProducts, listMyApps, createApp, updateApp, boostApp, checkBoost |
zap.marketplace | listProviders, search, applyAsProvider, listMyProviders, getProviderStats, listOrders, getOrder, payOrder, checkOrder, cancelOrder |
zap.watchOnly | createWallet, listWallets, getWallet, deleteWallet, getAddresses, freshAddress, refresh, createPsbt |
zap.ai | listModels, getModel, getBalance, getRate, createTopup, checkTopup, listTopups, getUsage, getUsageSummary, getAutoTopup, saveAutoTopup, toggleAutoTopup, removeAutoTopup |
zap.ai.completions | create (non-streaming), stream (SSE async generator) |
zap.aiTools | list, quote, execute, jobs, waitForCompletion, history |
ZapAPI.webhooks | verify(rawBody, header, secret, tolerance?) |
Anything the SDK doesn't cover yet can be called directly over the REST API with the same API key — see the API Reference.