Transfer $kic from your organisation's wallet to a Kicbak user's wallet, identified by email, username, or wallet ID.
Every request needs a bearer token in the Authorization header, and this endpoint only works for clients owned by an organisation. Exchange your client_key/client_secret at POST /auth/token; tokens last 24 hours.
| Field | Type | Required | Description |
|---|---|---|---|
| Content-Type | string | required | Must be application/json. |
| Authorization | string | required | Bearer token obtained from POST /api/third-party/v1/auth/token, e.g. "Bearer eyJhbGci...". |
| Field | Type | Required | Description |
|---|---|---|---|
| amount | integer | required | Whole $kic to transfer. Must be a positive integer. |
| recipientType | "EMAIL" | "USERNAME" | "WALLET_ID" | required | How recipientIdentifier resolves to a wallet. |
| recipientIdentifier | string | required | The recipient's email, username, or wallet public ID, depending on recipientType. |
| requestId | string | required | A unique string identifying this transfer, 8–200 characters. Send the same request again — same requestId, amount and recipient — and the original transfer is returned instead of the points moving twice, so a timed-out request is always safe to retry. Reusing a requestId for a DIFFERENT transfer is refused with 409. Unique to your organisation only; another partner's identical string does not affect you. |
If a request times out you cannot tell whether it went through. Send it again with the same requestId — if it already succeeded you get the original ref_id back and the points do not move again. A different amount or recipient under a used requestId is refused with 409 REQUEST_ID_REUSED and moves nothing.
curl -X POST https://kicbak.co/api/third-party/v1/kicbak/wallet/transfer \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"amount": 500,
"recipientType": "EMAIL",
"recipientIdentifier": "jane@example.com",
"requestId": "transfer-2026-08-04-0001"
}'Every response is JSON with a status field (1 success, 0 failure). Failures also carry a code your error handler can branch on — it never changes if we reword message.
{
"status": 1,
"message": "Transfer successful.",
"ref_id": 10482,
"debit_entry_id": 10482,
"credit_entry_id": 10483
}| Status | Code | Meaning |
|---|---|---|
| 400 | — | Malformed body, invalid amount, no wallet configured, insufficient balance, or the recipient wallet is not a user wallet. |
| 401 | UNAUTHORIZED | No readable credential, or a token that doesn't decode/validate. |
| 401 | TOKEN_EXPIRED | Token aged out. Re-exchange the same client_key/client_secret and retry. |
| 401 | CREDENTIAL_ROTATED | This token was minted under a secret that has since been rotated. Exchange your new secret. |
| 401 | CREDENTIAL_EXPIRED | The credential itself is past its own expiry — re-exchanging will not help; request a new credential. |
| 401 | CLIENT_INACTIVE | The client has been deactivated. |
| 403 | NOT_ORGANIZATION | Client is not organisation-owned — only organisation clients can call this endpoint. |
| 503 | SANDBOX_UNAVAILABLE | You are using a sandbox credential and the sandbox environment is temporarily unavailable on our side. Retry shortly; production credentials are unaffected. |
| 404 | — | recipientIdentifier did not resolve to a known user or wallet. |
| 409 | REQUEST_ID_REUSED | This requestId was already used for a different transfer. No points moved. Use a new requestId. |
| 429 | — | More than the configured hourly limit (default 30) from this client. |
| 500 | INTERNAL_ERROR | Unexpected server error. Safe to retry. |
Default 30 requests per hour per client. Transfers can only be made to user wallets — organisation-to-organisation transfers are not permitted, and the recipient must already have a Kicbak wallet.