Skip to main content
This is the map of the whole journey. It shows every endpoint you call, in the order you call it, and who the customer-id header points to at each step. Read it once and the individual API reference pages will fall into place.
Two actors run through every flow:
  • Seller — the merchant customer selling the item (the escrow is created on their behalf).
  • Buyer — the merchant customer funding the escrow.
You, the merchant, hold the secret key and act on behalf of both by setting the customer-id header to whichever one the current step concerns. The secret key (Authorization: Bearer sk_…) is on every request.

The journey at a glance

1

Set up (once)

Get your secret key, and optionally seed categories and look up a countryId.
2

Onboard your customers

Create the buyer and seller as merchant customers.
3

Create the deal

Create a standard escrow or a milestone escrow. You get a paymentToken and an id.
4

Fund it

The buyer pays into escrow. Funds are held, not released.
5

Deliver & release

The buyer confirms (or each milestone is confirmed), releasing funds net of fees.
6

Handle exceptions

Claims, disputes and refunds — only if the happy path doesn’t complete.

Phase 0 — Set up (once per merchant)

#CallEndpointNotes
0.1Get countriesGET /v1/misc/countriesReturns the countryId you need to create customers.
0.2List categories / Create categoryGET / POST /v1/escrow/category…Optional. Group your escrows into a catalog.
These don’t move money — do them once and cache the IDs.

Phase 1 — Onboard your customers

Almost every later call needs a customer-id, so you create the people first.
#CallEndpointcustomer-id
1.1Create merchant customer (seller)POST /v1/customer/create— (super-admin key)
1.2Create merchant customer (buyer)POST /v1/customer/create— (super-admin key)
1.3Update permissionsPUT /v1/customer/permissions/{customerId}
The seller needs canSell, the buyer needs canBuy. A buyer with canBuy disabled is rejected at payment time.
Keep the returned customer IDs — they become the customer-id header for the rest of the flow.

Phase 2 — The standard escrow flow

A single item, paid once, released once. This is the common case.
1

Create the escrow — as the seller

POST /v1/escrow/create with customer-id: <sellerId>. This is multipart/form-data (so you can attach up to 5 images).
curl -X POST https://staging.api.payluk.ng/v1/escrow/create \
  -H "Authorization: Bearer sk_test_…" \
  -H "customer-id: <sellerId>" \
  -F "amount=150000" \
  -F "purpose=MacBook Pro 14\"" \
  -F "whoPays=both" \
  -F "maxDelivery=3" \
  -F "deliveryTimeline=days" \
  -F "totalQuantity=1"
Returns id and paymentToken; state: AWAITING_PAYMENT, status: PENDING, settlementType: STANDARD. While in AWAITING_PAYMENT you can still edit or delete it.
Selling more than one unit off this single link? Set totalQuantity above 1 and read Multi-quantity escrows — the behaviour is different and is explained there.
2

Buyer resolves the link (optional)

Share the paymentToken. The buyer’s side can read the full escrow with GET /v1/escrow/verify/{paymentToken} to display amount, purpose and images before paying.
3

Make sure the buyer's wallet is funded

Paying with gateway: WALLET requires balance. Top it up first:Skip this step if you fund the escrow directly with a PAYSTACK/FLUTTERWAVE gateway.
4

Fund the escrow — as the buyer

POST /v1/payment/escrow with customer-id: <buyerId>.
curl -X POST https://staging.api.payluk.ng/v1/payment/escrow \
  -H "Authorization: Bearer sk_test_…" \
  -H "customer-id: <buyerId>" \
  -H "Content-Type: application/json" \
  -d '{
    "gateway": "WALLET",
    "transactionType": "ESCROW",
    "reference": "ESC_REF_98765",
    "escrowDetails": { "escrowId": "<escrowId>" }
  }'
The buyer is charged the escrow amount + their share of the fee. The escrow moves to state: OPENED / status: ONGOING and the delivery window starts.
With a non-wallet gateway this returns an authorizationUrl; after the buyer pays, call POST /v1/payment/verify to settle it into escrow.
5

Release the funds

The happy path — the buyer confirms delivery: POST /v1/escrow/confirm-payment/{escrowId} with customer-id: <buyerId>. The escrow closes as COMPLETED and the seller is paid net of fee.

If the happy path doesn’t happen

After the delivery window elapses, the seller calls GET /v1/escrow/claim-funds/{paymentToken} (customer-id: <sellerId>). Allowed only when OPENED and the window has passed. Closes as CLAIMED.
Either party opens POST /v1/escrow/submit-dispute/{paymentToken}. You, the merchant, then resolve it with POST /v1/escrow/dispute/resolve/{escrowId} — release to the seller or refund the buyer (REFUNDED).
The resolve and feeds routes are merchant-wide — do not send a customer-id header.

Phase 3 — The milestone escrow flow

Fund the whole project upfront; release it in stages as each milestone is confirmed. Same money rails, different create + release steps.
1

Create the milestone escrow — as the seller

POST /v1/escrow/milestone/create with customer-id: <sellerId>. Unlike standard create, this is JSON (no file upload).
{
  "amount": 1000000,
  "purpose": "Company website build",
  "whoPays": "buyer",
  "milestones": [
    { "title": "Design",      "amount": 300000, "dueDate": "2026-07-15" },
    { "title": "Development", "amount": 500000, "dueDate": "2026-08-15" },
    { "title": "Deployment",  "amount": 200000 }
  ]
}
At least 2 milestones.
Milestone amounts must sum to the escrow amount.
whoPays must be buyer on milestone escrows.
Returns settlementType: MILESTONE with a milestones[] array — each milestone has its own id and status: PENDING.
2

Fund it in full — as the buyer

Same endpoint as standard: POST /v1/payment/escrow with customer-id: <buyerId>. The buyer pays the entire amount once. Escrow → OPENED.
3

Confirm each milestone — as the buyer

Read the current milestones any time with GET /v1/escrow/milestone/{paymentToken}. As each deliverable lands, the buyer confirms it: POST /v1/escrow/milestone/confirm/{escrowId}/{milestoneId} with customer-id: <buyerId>. That milestone’s net share (its amount minus its pro-rata fee) is released and its status becomes RELEASED.
4

Auto-complete

When the final milestone is released, the escrow moves to CLOSED / COMPLETED automatically. No separate confirm-payment call.

Phase 4 — Monitor & reconcile (any time)

Don’t poll for status changes — register a callback URL and let Payluk push every escrow update to you. See Escrow webhooks.
CallEndpointUse
List escrow transactionsGET /v1/escrow/transactionsAll escrows and their states.
Get payment historyGET /v1/payment/historyMoney in/out per customer.
Get customer walletGET /v1/walletmainBalance vs escrowBalance.
List my disputes / feedsGET /v1/escrow/dispute/get, /feedsDispute activity.

Endpoint checklist

  1. POST /v1/customer/create — seller, then buyer (once)
  2. POST /v1/escrow/createcustomer-id: seller
  3. GET /v1/escrow/verify/{paymentToken} — buyer previews (optional)
  4. Fund wallet: POST /v1/payment/create-intentPOST /v1/payment/verify (or topup on staging)
  5. POST /v1/payment/escrowcustomer-id: buyer
  6. POST /v1/escrow/confirm-payment/{escrowId}customer-id: buyer
  • Exceptions: GET /v1/escrow/claim-funds/{paymentToken} · POST /v1/escrow/submit-dispute/{paymentToken} · POST /v1/escrow/dispute/resolve/{escrowId}
Selling multiple units off one link? That introduces escrow duplication — read Multi-quantity escrows next.