The KanaCash Partner API allows approved partners to integrate KanaCash wallet services into their own platforms and applications. Once onboarded, your platform can perform two core operations:
How It Works
Your platform authenticates using a client ID and client secret provided by KanaCash. These credentials are exchanged for a short-lived access token, which you include in all subsequent API requests.
Your partner account holds a separate balance in LRD and USD. For disbursements, your balance must be funded in advance. Contact KanaCash operations to top up your partner balance via bank transfer.
Download the Postman collection to get all endpoints pre-configured with the correct URLs, request bodies, and automatic token handling. No manual setup required.
Download Postman CollectionSetup in 3 steps
.json file. The collection appears in your workspace with all folders and requests ready.clientId and clientSecret with the values provided by KanaCash.accessToken variable and applied to all subsequent requests.referenceId from collection and disbursement requests, so status check requests are pre-filled automatically.
All Partner API endpoints are prefixed with the base URL for the environment you are targeting. Never use plain HTTP as requests will be rejected.
client_id will not work on the production endpoint and vice versa. The Postman collection defaults to production — change the baseUrl variable to the sandbox URL when testing.
Example full URL (production):
POST https://core.kanacash.com/api/v1/partner/v1/collection/request
Example full URL (sandbox):
POST https://sandbox.kanacash.com/api/v1/partner/v1/collection/request
The KanaCash Partner API uses OAuth 2.0 Client Credentials. You exchange your client_id and client_secret for a Bearer token, then include that token in every API request.
Step 1 — Get an access token
Call the token endpoint with your credentials. You will receive an access token that is valid for 1 hour.
POST /api/v1/partner/auth/token Content-Type: application/json { "client_id": "your-client-id", "client_secret": "your-client-secret" }
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}Step 2 — Include the token in every request
Add the access token to the Authorization header of all subsequent requests.
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...client_secret is shown only once when your account is created. Store it securely. If it is lost, contact KanaCash to generate a new one. Never expose it in client-side code or public repositories.
Token expiry
Tokens expire after 1 hour (expires_in: 3600). Your application should request a new token before the current one expires to avoid interruptions. A 401 response with code TOKEN_EXPIRED means you need to re-authenticate.
All request bodies must be JSON. All responses are JSON.
| Requirement | Value |
|---|---|
| Request body format | JSON |
| Content-Type header | application/json |
| Response format | JSON |
| Currency values | LRD or USD |
| Amount precision | Up to 2 decimal places (e.g. 100.50) |
| Phone number format | Liberian format — with or without country code (e.g. 0881234567 or +231881234567) |
Successful response structure
{
"success": true,
"data": { /* response payload */ }
}Error response structure
{
"success": false,
"message": "Human-readable description of the error",
"code": "MACHINE_READABLE_CODE"
}Each partner account has a default rate limit of 60 requests per minute. If your integration requires a higher limit, contact KanaCash to request an increase.
When you exceed the rate limit, the API returns HTTP 429 Too Many Requests. Your application should implement exponential backoff and retry after a short delay.
externalId to prevent duplicate processing. If you reuse an externalId that was already processed, the API returns 409 Conflict.
Authenticates your partner application and returns a short-lived access token. This token must be included in all subsequent API requests.
Request Body
| Field | Type | Description |
|---|---|---|
| client_idrequired | string (UUID) | Your partner client ID, provided by KanaCash on account creation.Format: 3b48755c-88dd-487b-8fed-f0437d044a39 |
| client_secretrequired | string | Your partner client secret. Shown once on account creation — store securely and never expose in client-side code. |
Response
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NmYxNzg...",
"token_type": "Bearer",
"expires_in": 3600
}Looks up a KanaCash user by their phone number. Use this before initiating a collection or disbursement to verify the account exists, is active, and supports the currency you want to transact in.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| phonerequired | string | The user's phone number. Accepts Liberian format with or without the country code (e.g. 0881234567 or +231881234567). |
Response
{
"success": true,
"data": {
"name": "John Doe",
"phone": "+231881234567",
"accountStatus": "active",
"kycStatus": "approved",
"currencies": ["LRD", "USD"]
}
}accountStatus is active and the required currency is present in the currencies array.
Returns the current balance of your partner wallet in both LRD and USD. You must have sufficient balance in the requested currency before initiating a disbursement.
Response
{
"success": true,
"data": {
"LRD": 150000.00,
"USD": 500.00
}
}Returns a fee breakdown and validates the recipient before you send a collection request. No funds are moved and no request is created. This is also where KanaCash runs full pre-validation: account status, wallet status, per-transaction/daily/weekly/monthly/yearly limits, transaction-count caps, and available balance. If any check fails, the quote is rejected outright (see error codes below) instead of being issued with a warning.
Request Body
| Field | Type | Description |
|---|---|---|
| phonerequired | string | The user's phone number.Accepts any Liberian format: 0881234567 or +231881234567 |
| amountrequired | number | The amount you intend to collect. |
| currencyrequired | string | LRD or USD |
Response
{
"success": true,
"data": {
"recipient": {
"name": "John Doe",
"phone": "+231881234567",
"accountStatus": "active"
},
"amount": 500.00,
"currency": "LRD",
"fee": 5.00,
"totalDebit": 505.00,
"sufficientBalance": true,
"approvalWindowMinutes": 10,
"note": "L$505.00 will be debited from John Doe's wallet. They have 10 minutes to approve."
}
}sufficientBalance is always true when a quote is returned successfully — insufficient balance now causes the quote call itself to fail with PKC_4001 instead of being returned as a warning. The field is kept for backward compatibility.
Confirms a collection request using the quoteId from Step 1 (Quote Collection). The fee is locked at the price shown in the quote. The user receives an in-app push notification and must approve or decline within 10 minutes.
quoteId. Quotes expire after 10 minutes and are single-use — a used quote cannot be reused even if the request fails. Limits and balance are re-checked at confirm time as well (to guard against race conditions between quote and confirm, e.g. another transaction consuming the user's limit in between) — since Quote Collection now runs the same checks upfront, this should rarely reject a quote that already succeeded.
Request Body
| Field | Type | Description |
|---|---|---|
| quoteIdrequired | string (UUID) | The quoteId returned by collection/quote. Locks in the fee calculated at quote time.Valid for 10 minutes. Single-use — consumed on this call. |
| externalIdrequired | string | Your unique reference for this transaction (max 100 chars).Used for idempotency. Use your internal order or payment ID. |
| noteoptional | string | Message shown to the user in their app (max 200 chars).Example: Payment for Order #00123 |
Example Request
{
"quoteId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"externalId": "ORDER-2026-00123",
"note": "Payment for Order #00123"
}Response
{
"success": true,
"data": {
"referenceId": "PKC-1751117862345-A1B2C3D4",
"status": "pending_approval",
"amount": 500.00,
"currency": "LRD",
"fee": 5.00,
"totalDebit": 505.00,
"expiresAt": "2026-06-28T12:20:00.000Z",
"message": "Request sent to user for in-app approval"
}
}Returns the current status of a collection request. Use this to poll for updates if you are not using webhooks. The referenceId is returned when you initiate a collection.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| referenceIdrequired | string | The reference ID returned when the collection was initiated. |
Response
{
"success": true,
"data": {
"referenceId": "PKC-1751117862345-A1B2C3D4",
"externalId": "ORDER-2026-00123",
"status": "completed",
"amount": 500.00,
"currency": "LRD",
"fee": 5.00,
"totalDebit": 505.00,
"transactionId": {
"transactionId": "TXN-A1B2C3D4E5F67890",
"status": "completed",
"completedAt": "2026-06-28T12:15:43.000Z"
},
"createdAt": "2026-06-28T12:10:02.000Z"
}
}Returns the exact fee, total cost to your partner balance, and validates the recipient before executing a disbursement. No funds are moved. This is also where KanaCash runs full pre-validation: recipient account/wallet status, per-transaction/daily/weekly/monthly/yearly limits, transaction-count caps, and available partner balance. If any check fails, the quote is rejected outright (see error codes below) instead of being issued with a warning. Use this to confirm the breakdown before calling disbursement/transfer.
Request Body
| Field | Type | Description |
|---|---|---|
| phonerequired | string | The recipient's phone number. |
| amountrequired | number | The amount the user will receive. |
| currencyrequired | string | LRD or USD |
Response
{
"success": true,
"data": {
"recipient": {
"name": "Jane Smith",
"phone": "+231771234567",
"accountStatus": "active",
"hasWallet": true
},
"amount": 200.00,
"currency": "USD",
"fee": 2.00,
"totalDebit": 202.00,
"partnerBalance": 1000.00,
"sufficientBalance": true,
"note": "$202.00 will be debited from your partner balance ($200.00 to user + $2.00 fee). Your remaining balance will be $798.00."
}
}sufficientBalance is always true when a quote is returned successfully — insufficient partner balance now causes the quote call itself to fail with PKC_4002 instead of being returned as a warning. The field is kept for backward compatibility.
Executes a disbursement using the quoteId from Step 1 (Quote Disbursement). The fee is locked at the quote price. Completes immediately — the user's wallet is credited and the response includes the final transaction ID.
Request Body
| Field | Type | Description |
|---|---|---|
| quoteIdrequired | string (UUID) | The quoteId returned by disbursement/quote. Locks in the fee and recipient details.Valid for 10 minutes. Single-use — consumed on this call. |
| externalIdrequired | string | Your unique reference for this payout (max 100 chars).Used for idempotency. Duplicate externalId returns 409. |
| noteoptional | string | Description shown in the user's transaction history (max 200 chars).Example: Commission payment June 2026 |
Example Request
{
"quoteId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"externalId": "PAYOUT-2026-00456",
"note": "Agent commission for June 2026"
}Response
{
"success": true,
"data": {
"referenceId": "PKD-1751117900456-B2C3D4E5",
"status": "completed",
"transactionId": "TXN-F1E2D3C4B5A67890",
"amount": 200.00,
"currency": "USD",
"fee": 2.00,
"totalDebit": 202.00
}
}PKC_7004 (nothing happened) or a normal success/error response is on its way. It is always safe to retry with the same externalId after a timeout — the idempotency check (PKC_6001) only matches completed or in-progress transactions, so a retry after an aborted timeout is treated as a fresh attempt. We recommend setting your client-side timeout to at least 30 seconds so our budget is exhausted first.
Returns the details of a disbursement by its reference ID. Useful for reconciliation and verifying the final state of a transaction.
Response
{
"success": true,
"data": {
"referenceId": "PKD-1751117900456-B2C3D4E5",
"transactionId": "TXN-F1E2D3C4B5A67890",
"status": "completed",
"amount": 200.00,
"currency": "USD",
"fee": 2.00,
"totalDebit": 202.00,
"netAmount": 200.00,
"createdAt": "2026-06-28T12:15:00.000Z",
"completedAt": "2026-06-28T12:15:01.000Z"
}
}When the status of a collection or disbursement changes, KanaCash sends an HTTP POST request to your registered webhook URL. Webhooks are the recommended way to receive updates rather than polling the status endpoint.
Registering a Webhook URL
Contact KanaCash to register your webhook URL when setting up your partner account. Your endpoint must:
- Accept HTTP
POSTrequests - Respond with HTTP
200within 10 seconds - Be accessible over HTTPS
Webhook Payload
{
"event": "collection.completed",
"referenceId": "PKC-1751117862345-A1B2C3D4",
"externalId": "ORDER-2026-00123",
"amount": 500.00,
"currency": "LRD",
"fee": 5.00,
"status": "completed",
"transactionId": "TXN-A1B2C3D4E5F67890",
"timestamp": "2026-06-28T12:15:43.000Z"
}Webhook Event Types
| Event | Triggered When |
|---|---|
collection.completed | The user approved the collection request and funds were transferred. |
collection.declined | The user declined the collection request. No funds moved. |
collection.expired | The 10-minute approval window passed with no user response. |
disbursement.completed | The disbursement was processed successfully and funds reached the user. |
Verifying Webhook Signatures
Every webhook includes an X-KanaCash-Signature header. This is an HMAC-SHA256 signature of the request body, computed using your webhook secret. Always verify this signature before processing the webhook.
X-KanaCash-Partner-Id: your-client-id X-KanaCash-Signature: sha256=a4b7c2d1e8f3... Content-Type: application/json
Signature Verification (Node.js example)
const crypto = require('crypto'); function verifyWebhook(rawBody, signature, webhookSecret) { const expected = 'sha256=' + crypto .createHmac('sha256', webhookSecret) .update(rawBody) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); }
Retry Policy
If your endpoint does not respond with HTTP 200 within 10 seconds, KanaCash retries the webhook up to 3 times with increasing delays. After 3 failed attempts, no further retries are made. You can always check the current status using the status endpoints.
referenceId to detect and safely ignore duplicate deliveries.
POST /partner/auth/token with your client ID and secret. Token is valid for 1 hour.POST /partner/v1/collection/quote with phone, amount, and currency. You receive a quoteId (valid 10 min), the calculated fee, total debit, and whether the user has sufficient balance. Check sufficientBalance before proceeding.POST /partner/v1/collection/request with the quoteId and your externalId. The fee is locked at the quote price. You receive a referenceId and status pending_approval.collection.completed, collection.declined, or collection.expired. Alternatively poll GET /partner/v1/collection/:referenceId.GET /partner/v1/balance.POST /partner/auth/token to obtain a valid Bearer token. Token is valid for 1 hour.POST /partner/v1/disbursement/quote with phone, amount, and currency. You receive a quoteId (valid 10 min), the calculated fee, your total cost, and whether your partner balance is sufficient. Check sufficientBalance — if false, top up first.POST /partner/v1/disbursement/transfer with the quoteId and your externalId. The fee is locked at the quote price. Completes immediately — you receive the transaction ID and status completed.disbursement.completed webhook is sent to your registered URL with the full transaction details. The user also receives an in-app notification that funds have arrived.code and message.All error responses follow this format:
{
"success": false,
"code": "PKC_3001",
"message": "No KanaCash account found for this phone number."
}code field for programmatic error handling. The message field is human-readable and may change. Build logic on code.
PKC_1XXX — Authentication
| HTTP | Code | Description |
|---|---|---|
| 401 | PKC_1001 | client_id or client_secret missing or malformed. |
| 401 | PKC_1002 | Credentials do not match any partner account. |
| 401 | PKC_1003 | Access token has expired. Call /auth/token to get a new one. |
| 401 | PKC_1004 | Token is malformed, tampered, or issued by a different system. |
| 401 | PKC_1005 | Authorization header missing or not in Bearer <token> format. |
| 403 | PKC_1006 | Partner account is suspended. Contact KanaCash. |
| 403 | PKC_1007 | Partner account is pending activation. Contact KanaCash. |
| 403 | PKC_1008 | Account does not have permission for this operation (collection or disbursement). |
| 403 | PKC_1009 | Partner account is not active. |
PKC_2XXX — Request Validation
| HTTP | Code | Description |
|---|---|---|
| 400 | PKC_2001 | Invalid Liberian phone number. Accepted: 0881234567, 0771234567, +231881234567. |
| 400 | PKC_2002 | Amount must be a positive number greater than zero. |
| 400 | PKC_2003 | Amount is below the minimum per transaction: $1 USD or L$50 LRD. |
| 400 | PKC_2004 | Amount exceeds the maximum per transaction: $10,000 USD or L$1,850,000 LRD. |
| 400 | PKC_2005 | Invalid currency. Must be LRD or USD. |
| 400 | PKC_2006 | externalId exceeds maximum length of 100 characters. |
| 400 | PKC_2007 | note exceeds maximum length of 200 characters. |
| 400 | PKC_2008 | One or more required fields are missing from the request body. |
| 400 | PKC_2009 | quoteId is not a valid UUID format. |
| 400 | PKC_2010 | Requested currency is not supported for this operation. |
PKC_3XXX — User / Account
| HTTP | Code | Description |
|---|---|---|
| 404 | PKC_3001 | No KanaCash account found for the provided phone number. |
| 403 | PKC_3002 | User account is not active (suspended or closed). The user must contact KanaCash. |
| 404 | PKC_3003 | User does not have an active wallet in the requested currency. |
| 403 | PKC_3004 | User's wallet is frozen. The user must contact KanaCash support. |
| 403 | PKC_3005 | User's KYC is not approved. The user must complete identity verification. |
PKC_4XXX — Balance / Limits
| HTTP | Code | Description |
|---|---|---|
| 400 | PKC_4001 | User does not have sufficient balance for this collection (amount + fee). Returned at quote time — the quote is not created. |
| 400 | PKC_4002 | Partner balance insufficient. Top up your partner wallet before disbursing. Returned at quote time — the quote is not created. |
| 400 | PKC_4003 | Amount exceeds the user's per-transaction limit for their account tier. Returned at quote time. |
| 400 | PKC_4004 | User's daily transaction limit for partner collection/disbursement has been reached. Returned at quote time. |
| 400 | PKC_4005 | User's monthly transaction limit for partner collection/disbursement has been reached. Returned at quote time. |
| 400 | PKC_4006 | A transaction limit has been exceeded (generic). Retry after the limit period resets. |
| 400 | PKC_4007 | User's weekly transaction limit for partner collection/disbursement has been reached. Returned at quote time. |
| 400 | PKC_4008 | User's yearly transaction limit for partner collection/disbursement has been reached. Returned at quote time. |
| 400 | PKC_4009 | User has reached the maximum number of partner transactions allowed per day. |
| 429 | PKC_4010 | User has reached the maximum number of partner transactions allowed per hour. Retry after the hour resets. |
| 403 | PKC_4011 | The user's account limits are not configured. Contact KanaCash support — this indicates an account provisioning issue, not a limit being exceeded. |
| 403 | PKC_4012 | No limit configuration exists for this transaction type/currency combination. Contact KanaCash support. |
PKC_5XXX — Quote
| HTTP | Code | Description |
|---|---|---|
| 400 | PKC_5001 | Quote not found or expired (10-minute TTL). Request a new quote and confirm promptly. |
| 400 | PKC_5002 | Quote has already been used. Each quote is single-use. Request a new quote. |
| 400 | PKC_5003 | Quote type does not match the endpoint (collection quote used on disbursement endpoint or vice versa). |
| 403 | PKC_5004 | Quote belongs to a different partner account. You can only confirm your own quotes. |
PKC_6XXX — Transaction
| HTTP | Code | Description |
|---|---|---|
| 409 | PKC_6001 | externalId already used for a completed or in-progress transaction. |
| 404 | PKC_6002 | Collection request not found for the given referenceId. |
| 410 | PKC_6003 | Collection request expired — the user did not respond within 10 minutes. No funds moved. |
| 409 | PKC_6004 | Collection request has already been approved or declined and cannot be changed. |
| 403 | PKC_6005 | Not authorized to perform this action on this resource. |
PKC_7XXX — System / Rate
| HTTP | Code | Description |
|---|---|---|
| 429 | PKC_7001 | Rate limit exceeded. Default is 60 requests per minute. Wait 60 seconds and retry with exponential backoff. |
| 500 | PKC_7002 | Internal server error. Please retry. If this persists, contact KanaCash support with the timestamp. |
| 503 | PKC_7003 | KanaCash service is temporarily unavailable (e.g. database connectivity issue). Retry shortly — no quote or transaction was created. |
| 504 | PKC_7004 | Disbursement Transfer exceeded KanaCash's internal 25-second processing budget. The transfer was not posted. Safe to retry immediately with the same externalId. |
KanaCash applies per-user transaction limits to partner collection and disbursement. These limits are configured by KanaCash administrators per user tier (Basic, Silver, Gold, Platinum) and control how much a single user can have collected from or disbursed to their wallet by all partners combined.
How limits work
As of this version, all limit checks run at quote time (collection/quote and disbursement/quote), not just the per-transaction cap. A quote that succeeds means the transaction is expected to clear these checks at confirm time too.
| Limit type | Checked at | What happens if exceeded |
|---|---|---|
| Per-transaction maximum | Quote time | PKC_4003 returned in the quote response. The quote is not created. |
| Daily limit | Quote time | PKC_4004 returned in the quote response. The quote is not created. |
| Weekly limit | Quote time | PKC_4007 returned in the quote response. The quote is not created. |
| Monthly limit | Quote time | PKC_4005 returned in the quote response. The quote is not created. |
| Yearly limit | Quote time | PKC_4008 returned in the quote response. The quote is not created. |
| Max transactions per day / hour | Quote time | PKC_4009 / PKC_4010 returned in the quote response. The quote is not created. |
collection/request approval) or transferring (disbursement/transfer) does. This means you can safely request multiple quotes without affecting the user's daily/monthly allowance. Confirm/transfer still re-runs the same checks as a final safeguard against race conditions (e.g. two transactions consuming the same limit window concurrently), so in rare cases a transaction can still be rejected after a successful quote — treat the quote as a strong signal, not an absolute guarantee.
Collection Request Statuses
| Status | Description |
|---|---|
pending_approval | The request was created and the user has been notified. Waiting for the user to approve or decline. |
completed | The user approved the request and funds were successfully transferred to your partner account. |
declined | The user actively declined the request. No funds were moved. |
expired | The 10-minute window passed with no response from the user. No funds were moved. |
failed | An error occurred during processing after the user approved. Contact KanaCash support with the reference ID. |
Transaction Statuses
| Status | Description |
|---|---|
completed | The transaction was processed successfully and funds have been moved. |
failed | The transaction failed. The operation was reversed and no funds were permanently moved. |
pending | The transaction is being processed. |