BINKDocs
HelpDashboard

Headless / Custom API

/docs/global/integrations/headless-apiGlobal

Build a fully custom payment experience using the BINKPAY REST API directly. This reference documents every endpoint you need to accept payments, issue refunds, manage invoices, and automate treasury operations — with examples in cURL and Node.js using the native fetch API.

Authentication

All API requests must include an Authorization header with a Bearer token. BINKPAY issues two types of secret API keys:

Key typePrefixWhen to use
Live secret keysk_live_…Production — processes real payments.
Test secret keysk_test_…Development and CI — no money moves. Use test card 4242 4242 4242 4242.

Retrieve your keys from Dashboard → Developer → API Keys. Rotate a compromised key immediately from the same page.

Keep your secret key private

Never expose sk_live_ or sk_test_ keys in client-side code, public repositories, or logs. Use environment variables and a secrets manager in production. Authorization header

bash
curl https://api.binkpay.net/v1/payments \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json"

Base URLs

EnvironmentBase URLCurrencies
Globalhttps://api.binkpay.netUSD, AED, EUR, GBP, SAR
Egypthttps://api.binkpay.egEGP only

Environment currency restriction

The Egypt environment (api.binkpay.eg) only accepts EGP and Egypt-specific payment methods (Vodafone Cash, InstaPay, Fawry, ValU, MeezaPay). The Global environment (api.binkpay.net) does not accept EGP.

Rate limits & idempotency

Rate limits

Each API key is limited to 100 requests per second. Exceeding this limit returns a 429 Too Many Requests response. The Retry-After header in the response contains the number of seconds to wait before retrying.

HTTP/1.1 429 Too Many Requests

Retry-After: 1

X-RateLimit-Limit: 100

X-RateLimit-Remaining: 0

X-RateLimit-Reset: 1730734270

Idempotency

All POST requests that create or mutate resources accept an Idempotency-Key header. Send a UUID v4 as the key. If the request fails due to a network error and you retry with the same key, BINKPAY returns the original response without creating a duplicate resource. Keys expire after 24 hours.

Idempotent payment creation

bash
curl -X POST https://api.binkpay.net/v1/payments \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json" \
 -H "Idempotency-Key: 7f6a1b3c-4e2d-41a8-b9f0-3c5e8d2a7f1b" \
 -d '{
 "amount": 4999,
 "currency": "USD",
 "payment_method": "card",
 "return_url": "https://shop.example.com/checkout/return"
 }'

Payments

Base path: /v1/payments

POST/v1/payments

Initiates a payment. For card and digital wallet payments (apple_pay, google_pay), BINKPAY returns a checkout_url to redirect the customer to the hosted payment page. For Egypt wallet methods (vodafone_cash, instapay, fawry, valu, meezapay), the customer receives an OTP or payment code on their device. All amounts are in the lowest denomination (piastres for EGP, cents for USD, fils for AED).

bash
curl -X POST https://api.binkpay.net/v1/payments \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json" \
 -H "Idempotency-Key: 7f6a1b3c-4e2d-41a8-b9f0-3c5e8d2a7f1b" \
 -d '{
 "amount": 4999,
 "currency": "USD",
 "payment_method": "card",
 "customer_id": "cus_01HX8FZRCPK7YN2D1L0JQWMAE",
 "return_url": "https://shop.example.com/checkout/return",
 "metadata": {
 "order_id": "ORD-2025-00441",
 "product_sku": "SKU-7734"
 }
 }'

Response

json
{
 "id": "pay_01HX9K2QVPM7AZ3E2M1JTXNBD",
 "object": "payment",
 "amount": 4999,
 "amount_captured": 0,
 "currency": "USD",
 "status": "requires_action",
 "payment_method": "card",
 "customer_id": "cus_01HX8FZRCPK7YN2D1L0JQWMAE",
 "description": null,
 "metadata": {
 "order_id": "ORD-2025-00441",
 "product_sku": "SKU-7734"
 },
 "return_url": "https://shop.example.com/checkout/return",
 "checkout_url": "https://checkout.binkpay.net/c/pay_01HX9K2QVPM7AZ3E2M1JTXNBD",
 "livemode": true,
 "created_at": "2025-11-04T14:31:55Z",
 "updated_at": "2025-11-04T14:31:55Z"
}
GET/v1/payments/:id
bash
curl https://api.binkpay.net/v1/payments/pay_01HX9K2QVPM7AZ3E2M1JTXNBD \
 -H "Authorization: Bearer sk_live_51HxK9..."

Response

json
{
 "id": "pay_01HX9K2QVPM7AZ3E2M1JTXNBD",
 "object": "payment",
 "amount": 4999,
 "amount_captured": 4999,
 "currency": "USD",
 "status": "succeeded",
 "payment_method": "card",
 "customer_id": "cus_01HX8FZRCPK7YN2D1L0JQWMAE",
 "billing_details": {
 "name": "Alex Mercer",
 "email": "alex@example.com",
 "phone": "+12125550100",
 "address": {
 "line1": "123 Main St",
 "city": "New York",
 "state": "NY",
 "postal_code": "10001",
 "country": "US"
 }
 },
 "metadata": {
 "order_id": "ORD-2025-00441",
 "product_sku": "SKU-7734"
 },
 "return_url": "https://shop.example.com/checkout/return",
 "livemode": true,
 "captured_at": "2025-11-04T14:32:09Z",
 "created_at": "2025-11-04T14:31:55Z",
 "updated_at": "2025-11-04T14:32:10Z"
}
POST/v1/payments/:id/capture

Capture an authorized payment

Captures a payment that is in authorized status. You may capture a partial amount by providing an amount less than the authorized amount. The uncaptured remainder is automatically released.

bash
curl -X POST https://api.binkpay.net/v1/payments/pay_01HX9K2QVPM7AZ3E2M1JTXNBD/capture \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json" \
 -d '{
 "amount": 4999
 }'
POST/v1/payments/:id/cancel

Cancels a payment that has not yet been captured. If the payment has already been captured, use the Refunds API instead.

bash
curl -X POST https://api.binkpay.net/v1/payments/pay_01HX9K2QVPM7AZ3E2M1JTXNBD/cancel \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json"

Refunds

Base path: /v1/refunds

POST/v1/refunds

Creates a refund for a captured payment. To issue a full refund omit the amount field. To issue a partial refund supply an amount less than payment.amount_captured. The reason field accepts duplicate, fraudulent, or requested_by_customer.

bash
curl -X POST https://api.binkpay.net/v1/refunds \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json" \
 -H "Idempotency-Key: c3a7e9f1-2b4d-4e6a-8c0f-5d1b7e3a9c2e" \
 -d '{
 "payment_id": "pay_01HX9K2QVPM7AZ3E2M1JTXNBD",
 "amount": 2000,
 "reason": "requested_by_customer"
 }'
GET/v1/refunds/:id
bash
curl https://api.binkpay.net/v1/refunds/ref_01HXB3NCPQ5YZ7WD2K0MJRTAE \
 -H "Authorization: Bearer sk_live_51HxK9..."

Invoices

Base path: /v1/invoices

POST/v1/invoices

Creates a draft invoice. Amounts per line item are in the smallest denomination. The invoice total is calculated as sum(amount × quantity) across all line items. Call /send to email the invoice to the customer.

bash
curl -X POST https://api.binkpay.net/v1/invoices \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json" \
 -d '{
 "customer_id": "cus_01HX8FZRCPK7YN2D1L0JQWMAE",
 "currency": "USD",
 "due_date": "2025-11-30",
 "line_items": [
 {
 "description": "Pro subscription — November 2025",
 "amount": 2900,
 "quantity": 1
 },
 {
 "description": "Additional workspace seat",
 "amount": 900,
 "quantity": 3
 }
 ],
 "metadata": {
 "billing_period": "2025-11"
 }
 }'
GET/v1/invoices/:id
bash
curl https://api.binkpay.net/v1/invoices/inv_01HXBF3KCPM8YN4E2L1JRWTAE \
 -H "Authorization: Bearer sk_live_51HxK9..."
POST/v1/invoices/:id/send

Emails the invoice PDF to the customer's email address on record. Also transitions the invoice status from draft to sent.

bash
curl -X POST https://api.binkpay.net/v1/invoices/inv_01HXBF3KCPM8YN4E2L1JRWTAE/send \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json"
POST/v1/invoices/:id/pay

Marks an invoice as paid outside of BINKPAY (e.g. cash, bank transfer). Use this to keep your invoice records accurate when payment was collected offline.

bash
curl -X POST https://api.binkpay.net/v1/invoices/inv_01HXBF3KCPM8YN4E2L1JRWTAE/pay \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json" \
 -d '{ "payment_method": "cash" }'

Customers

Base path: /v1/customers

POST/v1/customers

Creates a customer object. Attach a customer_id to payments and invoices to associate them with this customer and enable saved payment methods.

bash
curl -X POST https://api.binkpay.net/v1/customers \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json" \
 -d '{
 "email": "alex@example.com",
 "first_name": "Alex",
 "last_name": "Mercer",
 "phone": "+12125550100",
 "metadata": {
 "plan": "pro",
 "internal_id": "user_8821"
 }
 }'
GET/v1/customers/:id
bash
curl https://api.binkpay.net/v1/customers/cus_01HX8FZRCPK7YN2D1L0JQWMAE \
 -H "Authorization: Bearer sk_live_51HxK9..."
GET/v1/customers

Query parameters: limit (1–100, default 20), page (default 1), email (exact match filter).

bash
curl "https://api.binkpay.net/v1/customers?limit=20&page=1&email=alex%40example.com" \
 -H "Authorization: Bearer sk_live_51HxK9..."

Webhooks

Base path: /v1/webhooks

POST/v1/webhooks

Register a webhook endpoint

Registers a URL to receive signed event payloads. The response includes a secret (prefixed whsec_) that you use to verify incoming signatures. This secret is only returned once — store it securely.

Available events: payment.succeeded, payment.failed, refund.created, dispute.created, checkout.completed, invoice.paid, payout.completed.

bash
curl -X POST https://api.binkpay.net/v1/webhooks \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json" \
 -d '{
 "url": "https://app.example.com/webhooks/binkpay",
 "events": [
 "payment.succeeded",
 "payment.failed",
 "refund.created",
 "dispute.created",
 "checkout.completed",
 "invoice.paid",
 "payout.completed"
 ],
 "description": "Production webhook — payments and payouts"
 }'
GET/v1/webhooks

List webhook endpointsbash

bash
curl https://api.binkpay.net/v1/webhooks \
 -H "Authorization: Bearer sk_live_51HxK9..."
DELETE/v1/webhooks/:id

Delete a webhook endpointbash

bash
curl -X DELETE https://api.binkpay.net/v1/webhooks/wh_01HXCK4NCPQ5YZ7WD2K0MJRTAE \
 -H "Authorization: Bearer sk_live_51HxK9..."
POST/v1/webhooks/:id/test

Delivers a sample event payload to the registered URL so you can verify your handler before going live.

bash
curl -X POST https://api.binkpay.net/v1/webhooks/wh_01HXCK4NCPQ5YZ7WD2K0MJRTAE/test \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json" \
 -d '{ "event": "payment.succeeded" }'

Webhook signature verification

Every webhook delivery from BINKPAY includes an X-BinkPay-Signature header containing an HMAC-SHA256 hex digest of the raw request body, keyed with your webhook secret (whsec_…). Always verify this signature before processing the event.

Always verify signatures

Skipping signature verification exposes your application to spoofed events. An attacker could craft a fake payment.succeeded payload and trigger order fulfillment without a real payment.middleware/binkpay-webhook.ts

text
import crypto from 'node:crypto' ;

/**
 * Express middleware that verifies the BINKPAY webhook signature.
 * Mount BEFORE any body-parsing middleware so that req.body
 * is the raw Buffer.
 */ export function binkpayWebhookMiddleware(webhookSecret) { return (req, res, next) => { const signature = req.headers[ 'x-binkpay-signature'];
 if (!signature) {
 return res.status( 400).json({ error: 'Missing X-BinkPay-Signature header' });
 }

 // req.body must be the raw Buffer — use express.raw({ type: '*/*' })
 const rawBody = req.body;
 if (!Buffer.isBuffer(rawBody)) { return res.status( 400).json({ error: 'Raw body required for signature verification' });
 }

 const expected = crypto
 .createHmac( 'sha256' , webhookSecret)
 .update(rawBody)
 .digest( 'hex');

 const isValid = crypto.timingSafeEqual(
 Buffer. from(expected, 'hex'),
 Buffer. from(signature, 'hex')
 );

 if (!isValid) {
 return res.status( 403).json({ error: 'Invalid webhook signature' });
 }

 // Attach the parsed event to the request for downstream handlers req.binkpayEvent = JSON.parse(rawBody.toString( 'utf8'));
 next();
 };
}

// Usage in Express
import express from 'express';
const app = express();

app.post( '/webhooks/binkpay',
 express.raw({ type: '*/*' }),
 binkpayWebhookMiddleware(process.env.BINKPAY_WEBHOOK_SECRET),
 (req, res) => { const event = req.binkpayEvent;

 switch (event.type) {
 case 'payment.succeeded':
 // Fulfill order
 break;
 case 'refund.created':
 // Update order status
 break;
 case 'dispute.created':
 // Alert your support team break;
 }

 res.sendStatus( 200);
 }
);

Disputes

Base path: /v1/disputes

A dispute (chargeback) is created when a customer asks their card issuer to reverse a payment. You have a limited window — typically 7–14 days depending on the card network — to submit evidence. Monitor the dispute.created webhook event to be notified immediately.

GET/v1/disputes

Query parameters: status (needs_response | under_review | won | lost), limit, page.

bash
curl "https://api.binkpay.net/v1/disputes?status=needs_response" \
 -H "Authorization: Bearer sk_live_51HxK9..."
GET/v1/disputes/:id
bash
curl https://api.binkpay.net/v1/disputes/dis_01HXDP5NCPQ5YZ7WD2K0MJRTAE \
 -H "Authorization: Bearer sk_live_51HxK9..."
POST/v1/disputes/:id/evidence

Submits evidence to counter the dispute. Once submitted, the status transitions to under_review. Evidence can only be submitted once per dispute — make sure all information is accurate before calling this endpoint.

bash
curl -X POST https://api.binkpay.net/v1/disputes/dis_01HXDP5NCPQ5YZ7WD2K0MJRTAE/evidence \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json" \
 -d '{
 "customer_email_address": "alex@example.com",
 "customer_purchase_ip": "203.0.113.42",
 "product_description": "Pro subscription — November 2025",
 "receipt": "file_01HXE...",
 "shipping_tracking_number": null,
 "additional_evidence": "Customer confirmed via email on 2025-11-01 that they authorised this payment."
 }'

Treasury

Base path: /v1/treasury

GET/v1/treasury/balance

Returns available and pending balances per currency. available funds can be paid out immediately. pending funds are from recent payments still in the settlement window.

bash
curl https://api.binkpay.net/v1/treasury/balance \
 -H "Authorization: Bearer sk_live_51HxK9..."
GET/v1/treasury/transactions

List treasury transactions

Query parameters: currency, type (payment | refund | payout | fee), limit, page, from / to (ISO 8601 date strings).

bash
curl "https://api.binkpay.net/v1/treasury/transactions?currency=USD&limit=50" \
 -H "Authorization: Bearer sk_live_51HxK9..."
POST/v1/treasury/payouts

Initiates a bank transfer of available funds. Payouts typically settle within 1–3 business days. The payout.completed webhook fires when the transfer settles. Use an Idempotency-Key to prevent duplicate payouts.

bash
curl -X POST https://api.binkpay.net/v1/treasury/payouts \
 -H "Authorization: Bearer sk_live_51HxK9..." \
 -H "Content-Type: application/json" \
 -H "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
 -d '{
 "amount": 500000,
 "currency": "USD",
 "destination": {
 "type": "bank_account",
 "account_number": "000123456789",
 "routing_number": "110000000",
 "account_holder_name": "Acme Corp LLC"
 },
 "description": "Weekly settlement — week 44"
 }'

Error handling

BINKPAY uses standard HTTP status codes. Every error response includes a JSON body with a machine-readable code, a human-readable message, and a request_id to quote when contacting support.

StatusMeaningAction
400Bad RequestFix the request body — a required field is missing or malformed.
401UnauthorizedCheck your Authorization header and API key.
403ForbiddenThe API key does not have permission for this operation.
404Not FoundThe resource ID does not exist in this environment.
409ConflictAn idempotent request was replayed with a different body.
422Unprocessable EntityRequest body is valid JSON but fails business rules (e.g. EGP on global env).
429Too Many RequestsRespect the Retry-After header and back off.
500Internal Server ErrorAn unexpected server error occurred. Retry with exponential backoff and contact support if persistent.

Error response shape

Error response

Error codes

CodeDescription
payment_declinedThe card issuer declined the payment. Ask the customer to use a different payment method.
insufficient_fundsThe card or wallet does not have enough funds to cover the amount.
invalid_cardThe card number, expiry, or CVV is incorrect.
card_expiredThe card expiry date has passed.
do_not_honorGeneric decline from the issuer. Customer should contact their bank.
currency_not_supportedThe requested currency is not supported in this environment.
amount_too_smallThe amount is below the minimum allowed for this currency.
amount_too_largeThe amount exceeds the maximum allowed for this currency or account tier.
duplicate_idempotency_keyAn idempotent request was replayed with a different request body.
webhook_secret_not_foundThe webhook secret could not be found for signature verification.

SDKs & libraries

The official BINKPAY Node.js SDK wraps the REST API with TypeScript types, automatic retries with exponential backoff, idempotency key management, webhook signature verification helpers, and auto-pagination for list endpoints.

Install

bash
npm install @binkpay/node

Usage

text
import Binkpay from '@binkpay/node';

const binkpay = new Binkpay({
 apiKey: process.env.BINKPAY_SECRET_KEY, // sk_live_... or sk_test_...
 // baseUrl: 'https://api.binkpay.eg', // uncomment for Egypt environment
});

// Create a payment
const payment = await binkpay.payments.create({
 amount: 4999,
 currency: 'USD',
 payment_method: 'card',
 customer_id: 'cus_01HX8FZRCPK7YN2D1L0JQWMAE',
 return_url: 'https://shop.example.com/checkout/return' ,
 metadata: { order_id: 'ORD-2025-00441' },
});

// Retrieve a payment
const retrieved = await binkpay.payments.retrieve( 'pay_01HX9K2QVPM7AZ3E2M1JTXNBD');

// Create a refund
const refund = await binkpay.refunds.create({
 payment_id: payment.id,
 amount: 2000,
 reason: 'requested_by_customer',
});

// List customers
const customers = await binkpay.customers.list({ limit: 20, page: 1 });

// Iterate pages automatically
for await ( const customer of binkpay.customers.listAutoPaging({ limit: 100 })) {
 console.log(customer.email);
}

OpenAPI specification

A machine-readable OpenAPI 3.1 spec is available at https://api.binkpay.net/v1/openapi.json. Use it to generate client SDKs in any language with tools like openapi-generator

or to import the collection directly into Postman or Insomnia.

Need help? Contact BINKPAY supportAPI status pageFull documentation

PreviousRate Limits & IdempotencyRead more →

Was this page helpful?