Client Reference
The ZendFiClient class is the core of the SDK. It provides methods for every ZendFi API operation with built-in retries, idempotency, and typed responses.
Constructor
import { ZendFiClient } from '@zendfi/sdk';
const zendfi = new ZendFiClient(options?: Partial<ZendFiConfig>);
Configuration Options
Your ZendFi API key. Falls back to ZENDFI_API_KEY env var.
baseURL
string
default:"https://api.zendfi.tech"
API base URL. Override for self-hosted or proxy setups.
environment
string
default:"auto-detected"
development, staging, or production. Auto-detected from NODE_ENV.
mode
string
default:"auto-detected"
test or live. Auto-detected from API key prefix.
Request timeout in milliseconds.
Number of retry attempts for failed requests (5xx and network errors).
Automatically generate idempotency keys for POST requests.
Log all requests and responses to the console.
Payments
createPayment
Creates a new payment and returns a checkout URL.
const payment = await zendfi.createPayment({
amount: 49.99,
description: 'Pro Plan',
currency: 'USD', // default
token: 'USDC', // default
customer_email: 'jane@example.com',
metadata: { order_id: '12345' },
split_recipients: [ // optional
{ recipient_wallet: '7xKXt...', percentage: 80 },
{ recipient_wallet: '8yLYu...', percentage: 20 },
],
});
console.log(payment.payment_url);
Payment object with id, status, payment_url, checkout_url, qr_code, expires_at, and more.
getPayment
Retrieves a payment by ID.
const payment = await zendfi.getPayment('pay_test_abc123');
console.log(payment.status); // 'pending' | 'confirmed' | 'failed' | 'expired'
Payment Links
createPaymentLink
Creates a shareable checkout URL.
const link = await zendfi.createPaymentLink({
amount: 99.00,
description: 'Premium License',
onramp: true, // enable fiat (NGN bank transfer) payment option
max_uses: 100,
expires_at: '2026-12-31T23:59:59Z',
collect_customer_info: true, // show customer details form on checkout
});
console.log(link.url); // convenience alias
console.log(link.hosted_page_url); // same URL
console.log(link.collect_customer_info); // true
With payment splits (wallet recipients)
Automatically distribute payments across multiple wallets:
const link = await zendfi.createPaymentLink({
amount: 100.00,
description: 'Marketplace Purchase – 3-way split',
split_recipients: [
{ recipient_wallet: '7xKXt...', percentage: 60, recipient_name: 'Seller' },
{ recipient_wallet: '8yLYu...', percentage: 30, recipient_name: 'Platform' },
{ recipient_wallet: '9zMZv...', percentage: 10, recipient_name: 'Referrer' },
],
});
console.log(link.url);
console.log(link.split_recipients); // array with full split details
With payment splits (bank account recipients)
Enable NGN bank deposits via PAJ bank account integration:
const link = await zendfi.createPaymentLink({
amount: 500.00,
description: 'Contractor Payment – Direct to Bank',
onramp: true,
split_recipients: [
{
recipient_type: 'bank_account',
percentage: 70,
recipient_account_name: 'John Okonkwo',
recipient_bank_account: '0123456789',
recipient_bank_id: 'GTB', // bank identifier (id/code/name)
recipient_email: 'john@contractor.com',
},
{
recipient_type: 'wallet',
recipient_wallet: '7xKXt...',
percentage: 30,
recipient_name: 'Agent',
},
],
});
console.log(link.url);
// When paid:
// – 70% (₦...) USDC → PAJ → John's GTB account
// – 30% direct blockchain transfer to agent wallet
For bank recipients, the SDK accepts recipient_bank_id plus aliases recipient_bank, bank_identifier, and bank_code.
For wallet recipients, you can target a sub-account directly with sub_account_id (or alias recipient_sub_account) instead of recipient_wallet.
const link = await zendfi.createPaymentLink({
amount: 100,
description: 'Fund operations sub-account',
split_recipients: [
{
recipient_type: 'wallet',
sub_account_id: 'sa_7b1w9j2k4m8p',
// percentage optional for a single recipient (defaults to 100%)
},
],
});
With a pre-filled customer object
When you supply a customer object the checkout page skips the email / info
collection step entirely and shows a single “Continue to Pay” button instead.
The customer data is passed directly into the payment flow (including the onramp
initiation). The link is automatically locked to max_uses = 1.
const link = await zendfi.createPaymentLink({
amount: 49.00,
description: 'Order #1042 – Wireless Headphones',
onramp: true,
payer_service_charge: true,
// Pre-fill customer — checkout skips the info form, max_uses forced to 1
customer: {
email: 'ada@example.com',
name: 'Ada Lovelace',
phone: '+2348012345678',
billing_city: 'Lagos',
billing_country: 'NG',
},
});
console.log(link.max_uses); // 1 (forced automatically)
console.log(link.customer_data); // { email: 'ada@example.com', name: 'Ada Lovelace', … }
console.log(link.url);
const link = await zendfi.getPaymentLink('link_code_here');
listPaymentLinks
const links = await zendfi.listPaymentLinks();
Sub Accounts
Sub-account methods are documented in detail on SDK Sub Accounts.
createSubAccount
const sub = await zendfi.createSubAccount({
label: 'user_paschal_001',
spend_limit_usdc: 500,
access_mode: 'delegated',
yield_enabled: false,
});
listSubAccounts
const subs = await zendfi.listSubAccounts();
getSubAccount / getSubAccountBalance
const sub = await zendfi.getSubAccount('sa_xxxxx');
const balance = await zendfi.getSubAccountBalance(sub.id);
mintSubAccountDelegationToken
const token = await zendfi.mintSubAccountDelegationToken(sub.id, {
scope: 'withdraw_only',
spend_limit_usdc: 100,
expires_in_seconds: 900,
single_use: true,
});
freezeSubAccount / closeSubAccount
await zendfi.freezeSubAccount(sub.id, { reason: 'fraud-review' });
await zendfi.closeSubAccount(sub.id);
drainSubAccount / withdrawFromSubAccount
await zendfi.drainSubAccount(sub.id, {
token: 'Usdc',
amount: 25,
mode: 'live',
passkey_signature,
});
await zendfi.withdrawFromSubAccount(sub.id, {
to_address: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU',
amount: 10,
token: 'Usdc',
delegation_token: token.delegation_token,
passkey_signature,
});
Subscriptions
createSubscriptionPlan
Defines a recurring billing plan.
const plan = await zendfi.createSubscriptionPlan({
name: 'Pro Plan',
amount: 29.99,
interval: 'monthly',
trial_days: 14,
});
getSubscriptionPlan
const plan = await zendfi.getSubscriptionPlan('plan_abc123');
createSubscription
Subscribes a customer to a plan.
const sub = await zendfi.createSubscription({
plan_id: 'plan_abc123',
customer_email: 'customer@example.com',
customer_wallet: '7xKXt...',
});
getSubscription
const sub = await zendfi.getSubscription('sub_abc123');
cancelSubscription
const canceled = await zendfi.cancelSubscription('sub_abc123');
Installment Plans
createInstallmentPlan
Splits a purchase into scheduled payments.
const plan = await zendfi.createInstallmentPlan({
customer_wallet: '7xKXt...',
total_amount: 1200.00,
installment_count: 4,
payment_frequency_days: 30,
description: 'Quarterly payments',
});
getInstallmentPlan
const plan = await zendfi.getInstallmentPlan('plan_test_abc123');
listInstallmentPlans
const plans = await zendfi.listInstallmentPlans({ limit: 20, offset: 0 });
listCustomerInstallmentPlans
const plans = await zendfi.listCustomerInstallmentPlans('7xKXt...');
cancelInstallmentPlan
const result = await zendfi.cancelInstallmentPlan('plan_test_abc123');
Invoices
createInvoice
Creates a professional invoice.
const invoice = await zendfi.createInvoice({
customer_email: 'client@company.com',
customer_name: 'Acme Corp',
amount: 2500.00,
description: 'March consulting',
line_items: [
{ description: 'Strategy session', quantity: 5, unit_price: 500 },
],
due_date: '2026-03-15T00:00:00Z',
});
console.log(invoice.line_items); // always returned (not just on create)
console.log(invoice.status); // 'draft'
console.log(invoice.sent_at); // null until sendInvoice() is called
With onramp enabled
When onramp: true, the payment link generated by sendInvoice() will use the
PAJ onramp flow (NGN bank transfer → USDC settlement) instead of a direct
crypto payment.
const invoice = await zendfi.createInvoice({
customer_email: 'client@company.com',
customer_name: 'Acme Corp',
amount: 25.00,
description: 'Product purchase – March 2026',
onramp: true, // payment link will use NGN bank transfer
amount_ngn: 42500, // exact NGN amount (bypasses live FX re-quote)
payer_service_charge: true, // PAJ fee added on top, billed to the customer
payment_link_max_uses: 1, // default for invoices; set higher if needed
collect_customer_info: false,
due_date: '2026-03-15T00:00:00Z',
});
// When ready, send it — the generated payment link will have onramp=true
const result = await zendfi.sendInvoice(invoice.id);
console.log(result.payment_url); // https://checkout.zendfi.tech/checkout/…
console.log(result.onramp); // true
console.log(result.max_uses); // 1
getInvoice
const invoice = await zendfi.getInvoice('inv_test_abc123');
listInvoices
const invoices = await zendfi.listInvoices();
sendInvoice
Sends the invoice to the customer via email.
const result = await zendfi.sendInvoice('inv_test_abc123');
console.log(result.payment_url); // URL included in the email
console.log(result.sent_to); // verified recipient
Webhooks
verifyWebhook
Verifies the HMAC-SHA256 signature of an incoming webhook.
const isValid = zendfi.verifyWebhook({
payload: rawBody, // string or object
signature: req.headers['x-zendfi-signature'],
secret: process.env.ZENDFI_WEBHOOK_SECRET!,
});
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
Always pass the raw request body as the payload, not the parsed JSON. Parsing and re-serializing can alter whitespace and break the signature.
Retry and Idempotency
The client automatically retries failed requests:
- 5xx errors: Retried up to
retries times with exponential backoff (2n seconds)
- Network errors: Retried with the same backoff logic
- 4xx errors: Never retried (these indicate client-side issues)
Idempotency keys are generated automatically for all POST requests when idempotencyEnabled is true (the default). This prevents duplicate charges from retried requests.
Debug Mode
Enable debug logging to see all requests and responses:
const zendfi = new ZendFiClient({
apiKey: 'zfi_test_...',
debug: true,
});
Output:
[ZendFi] POST /api/v1/payments
[ZendFi] Request: {"amount":49.99,"description":"Pro Plan"}
[ZendFi] 200 OK (142ms)
[ZendFi] Response: {"id":"pay_test_abc123","status":"pending",...}