Payment Links
Payment links are how you initiate a transaction through Kashia. Create a payment link, then redirect the buyer to pay. When payment is confirmed, an escrow is automatically created.
amount is the base amount (what the seller charges). The buyer pays fees.total_amount (base + platform + marketplace fees). See Fees for details.
Amounts are 64-bit integers (long) in the smallest currency unit — kobo for NGN, cents for USD. Never decimals.
Who is the seller?
- Single-vendor merchants — you are the seller. Pass only
buyer_user_idand omitseller_user_identirely; the escrow settles into your merchant wallet on completion, ready for withdrawal. Passing any other user as the seller is refused with400. - Marketplace merchants —
seller_user_idis required (create the seller via Customersfirst). On settlement, Kashia automatically pays the seller's amount to their default verified bank account; your marketplace fee is credited to your merchant wallet.
Amount format
| Display Amount | API Amount (kobo) |
|---|---|
| ₦100 | 10000 |
| ₦1,000 | 100000 |
| ₦10,000 | 1000000 |
| ₦100,000 | 10000000 |
| ₦1,000,000 | 100000000 |
Seller payout account
For marketplace merchants, pass seller_bank_account when creating the link (or register one via Bank Accounts). When the escrow settles, Kashia automatically pays the seller's seller_amount — in full, with no withdrawal fee — to their default verified bank account and emits payout.* webhooks. If no verified account is on file, the funds stay safely in the seller's Kashia wallet; once an account is added, Kashia support retries the payout.
Goods or services?
Every payment link has a kind — "goods" or "services"— which the escrow inherits when paid. It changes what the hosted pages say and what the deliver step collects (tracking details for goods; a completion summary and work evidence for services) — never how the money moves. Omit it and your account's default offering applies; accounts set to Bothmust state it per link. All webhook payloads carry the escrow's kind.
Order IDs & idempotency
Pass your own transaction identifier as order_id when creating the link. It is an idempotency key: within one environment, each order_id can be used exactly once, ever — creating a second link with it is refused with 400and the existing link's reference, even if that link was cancelled or expired. Give every order its own identifier. The order_id flows onto the escrow at payment, appears in every escrow.* webhook, and resolves the escrow via GET /external/escrows/lookup— your system never needs to store Kashia's ids.
Legacy spelling: order_id inside metadata is also honored — it is promoted onto the real field automatically (an explicit top-level order_id wins if both are sent). Prefer the top-level field in new code.
Endpoints
/api/v1/external/payment-linksCreate a new payment link. Redirect the buyer to payment_url after creation.
Request example (single-vendor)
curl -X POST https://vault-api.kashiahq.com/api/v1/external/payment-links \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"buyer_user_id": "550e8400-e29b-41d4-a716-446655440000",
"title": "iPhone 15 Pro Max",
"description": "256GB, Blue Titanium, Brand New",
"amount": 150000000,
"currency": "NGN",
"order_id": "ORD-12345",
"metadata": {
"product_id": "PROD-678"
},
"redirect_url": "https://yourstore.com/order/confirmed"
}'Request body (JSON)
{
"buyer_user_id": "550e8400-e29b-41d4-a716-446655440000",
"title": "iPhone 15 Pro Max",
"description": "256GB, Blue Titanium, Brand New",
"amount": 150000000,
"currency": "NGN",
"order_id": "ORD-12345",
"metadata": {
"product_id": "PROD-678"
},
"redirect_url": "https://yourstore.com/order/confirmed"
}Request example (marketplace)
curl -X POST https://vault-api.kashiahq.com/api/v1/external/payment-links \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"buyer_user_id": "buyer-uuid",
"seller_user_id": "seller-uuid",
"title": "Web Design Service",
"description": "Landing page and brand kit",
"amount": 50000000,
"currency": "NGN",
"merchant_fee_percentage": 10.0,
"merchant_fee_cap": 20000000,
"seller_bank_account": {
"bank_code": "058",
"account_number": "0123456789"
},
"order_id": "ORD-12345",
"metadata": {
"product_id": "PROD-678"
},
"redirect_url": "https://yourstore.com/order/confirmed"
}'Request body (JSON)
{
"buyer_user_id": "buyer-uuid",
"seller_user_id": "seller-uuid",
"title": "Web Design Service",
"description": "Landing page and brand kit",
"amount": 50000000,
"currency": "NGN",
"merchant_fee_percentage": 10.0,
"merchant_fee_cap": 20000000,
"seller_bank_account": {
"bank_code": "058",
"account_number": "0123456789"
},
"order_id": "ORD-12345",
"metadata": {
"product_id": "PROD-678"
},
"redirect_url": "https://yourstore.com/order/confirmed"
}Response
{
"success": true,
"data": {
"id": "...",
"reference": "PL-a1b2c3d4",
"order_id": "ORD-12345",
"merchant_name": "Your Store",
"payment_url": "https://vault.kashiahq.com/pay/PL-a1b2c3d4",
"title": "iPhone 15 Pro Max",
"amount": 150000000,
"currency": "NGN",
"fees": {
"platform_fee": 2250000,
"merchant_fee": 0,
"total_fees": 2250000,
"total_amount": 152250000
},
"status": "active",
"environment": "live",
"seller_bank_account": {
"id": "...",
"bank_code": "058",
"bank_name": "GTBank",
"account_number": "0123456789",
"account_name": "ADAOBI OKAFOR",
"is_default": true,
"is_verified": true,
"name_source": "bank_confirmed",
"currency": "NGN"
},
"buyer": {
"id": "...",
"email": "john@example.com",
"first_name": "John",
"last_name": "Doe"
},
"seller": {
"id": "...",
"email": "seller@example.com",
"first_name": "...",
"last_name": "..."
},
"expires_at": null,
"created_at": "2025-01-15T10:30:00Z"
}
}| Parameter | Type | Required | Description |
|---|---|---|---|
buyer_user_id | string (UUID) | Yes | The buyer's customer ID |
seller_user_id | string (UUID) | No | Marketplace only — the seller's customer ID (required). Single-vendor merchants must omit it: you are the seller, and passing any other user is refused with 400. |
kind | string | No | What this transaction protects: "goods" (shipped items, buyer confirms receipt) or "services" (completed work, client confirms completion). Omitted inherits your account default; accounts set to Both must pass it on every link. |
order_id | string | No | Your own transaction identifier (max 64 chars). Idempotency key: usable exactly once per environment, ever — a duplicate is refused with 400 even if the earlier link was cancelled. Use it to look the escrow up later via GET /external/escrows/lookup. |
title | string | Yes | Short description of the product/service |
description | string | No | Detailed description |
amount | integer | Yes | Base amount in kobo (seller price, before fees) |
currency | string | No (default: "NGN") | Currency code |
merchant_fee_percentage | number | No | Marketplace only — override commission % for this link (0–20) |
merchant_fee_cap | integer | No | Marketplace only — override commission cap in kobo for this link |
seller_bank_account | object | No | Optional { bank_code, account_number }. Verified by bank name enquiry and saved as the seller's DEFAULT payout account — settlement payouts go there automatically. A failed enquiry fails the request with 400 and creates nothing. Ignored on test-key requests. |
metadata | object | No | Custom key-value data attached to the transaction |
redirect_url | string | No | URL to redirect buyer after payment |
callback_url | string | No | Webhook URL for this payment (overrides default) |
/api/v1/external/payment-links/:referenceRetrieve a payment link by reference. Once the link is paid, the response additionally carries escrow_id — the escrow it created. (seller_bank_account is echoed only on the create response.)
Response
{
"success": true,
"data": {
"id": "...",
"reference": "PL-a1b2c3d4",
"order_id": "ORD-12345",
"merchant_name": "Your Store",
"payment_url": "https://vault.kashiahq.com/pay/PL-a1b2c3d4",
"title": "iPhone 15 Pro Max",
"amount": 150000000,
"currency": "NGN",
"fees": {
"platform_fee": 2250000,
"merchant_fee": 0,
"total_fees": 2250000,
"total_amount": 152250000
},
"status": "active",
"environment": "live",
"seller_bank_account": {
"id": "...",
"bank_code": "058",
"bank_name": "GTBank",
"account_number": "0123456789",
"account_name": "ADAOBI OKAFOR",
"is_default": true,
"is_verified": true,
"name_source": "bank_confirmed",
"currency": "NGN"
},
"buyer": {
"id": "...",
"email": "john@example.com",
"first_name": "John",
"last_name": "Doe"
},
"seller": {
"id": "...",
"email": "seller@example.com",
"first_name": "...",
"last_name": "..."
},
"expires_at": null,
"created_at": "2025-01-15T10:30:00Z"
}
}/api/v1/external/payment-linksList payment links with optional filtering and pagination.
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No (default: all) | Filter: active, completed, expired, cancelled |
page | integer | No (default: 1) | Page number |
per_page | integer | No (default: 20) | Items per page (max 100) |
Response
{
"success": true,
"data": [ ... ],
"meta": {
"page": 1,
"per_page": 20,
"total": 45,
"total_pages": 3
}
}/api/v1/external/payment-links/:reference/cancelCancel an active (unpaid) payment link.