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_id and omit seller_user_id entirely; the escrow settles into your merchant wallet on completion, ready for withdrawal. Passing any other user as the seller is refused with 400.
  • Marketplace merchants — seller_user_id is 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 AmountAPI Amount (kobo)
₦10010000
₦1,000100000
₦10,0001000000
₦100,00010000000
₦1,000,000100000000

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

POST/api/v1/external/payment-links

Create a new payment link. Redirect the buyer to payment_url after creation.

Request example (single-vendor)

bash
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)

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)

bash
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)

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

json
{
  "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"
  }
}
Parameters
ParameterTypeRequiredDescription
buyer_user_idstring (UUID)YesThe buyer's customer ID
seller_user_idstring (UUID)NoMarketplace 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.
kindstringNoWhat 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_idstringNoYour 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.
titlestringYesShort description of the product/service
descriptionstringNoDetailed description
amountintegerYesBase amount in kobo (seller price, before fees)
currencystringNo (default: "NGN")Currency code
merchant_fee_percentagenumberNoMarketplace only — override commission % for this link (0–20)
merchant_fee_capintegerNoMarketplace only — override commission cap in kobo for this link
seller_bank_accountobjectNoOptional { 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.
metadataobjectNoCustom key-value data attached to the transaction
redirect_urlstringNoURL to redirect buyer after payment
callback_urlstringNoWebhook URL for this payment (overrides default)
GET/api/v1/external/payment-links/:reference

Retrieve 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

json
{
  "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"
  }
}
GET/api/v1/external/payment-links

List payment links with optional filtering and pagination.

Parameters
ParameterTypeRequiredDescription
statusstringNo (default: all)Filter: active, completed, expired, cancelled
pageintegerNo (default: 1)Page number
per_pageintegerNo (default: 20)Items per page (max 100)

Response

json
{
  "success": true,
  "data": [ ... ],
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 45,
    "total_pages": 3
  }
}
POST/api/v1/external/payment-links/:reference/cancel

Cancel an active (unpaid) payment link.