Cowdidocs
API ReferenceTransactions

Preview a transaction

POST
/v1/transactions/preview

Price a transaction before creating it. Set type to SEND_MONEY, BUY_GOODS, PAY_BILL, POCHI, or PAYEE to match the intended create operation. Requires an authenticated, active user. Every loan or credit account returns loan_options. The top-level loan field is deprecated in its favor. For FLEX_CREDIT, send tenure to price that option alone, or omit it to price every available tenure. fee, total, and loan are then absent, so read loan_options instead. Omit tenure for other account types. Returns 404 when account_id, or a PAYEE request's payee_account_id, does not belong to the caller. A POCHI preview, or a PAYEE preview of a POCHI payee account, refuses POCHI_NOT_SUPPORTED_FOR_ACCOUNT, POCHI_NOT_AVAILABLE, and POCHI_AMOUNT_OUT_OF_RANGE.

AuthorizationBearer <token>

Create a client with POST /v1/oauth/clients, then exchange its credentials for an access token. AUTHORIZATION_CODE clients act on behalf of a signed-in user and always use PKCE; CLIENT_CREDENTIALS clients act as themselves. A client may only be granted scopes its creator already holds — read the grantable list from GET /v1/oauth/clients/scopes.

In: header

Request Body

application/json

Request to preview an M-Pesa buy-goods payment.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/transactions/preview" \  -H "Content-Type: application/json" \  -d '{    "type": "BUY_GOODS",    "account_id": "550e8400-e29b-41d4-a716-446655440000",    "amount": {      "value": 50000,      "currency": "KES"    },    "till_number": "123456",    "recipient_name": "ABC Store"  }'
{  "type": "SEND_MONEY",  "amount": 30000,  "fee": 1100,  "total": 31100,  "channel": "MPESA",  "currency": "KES",  "can_proceed": true,  "validation": {    "account_valid": true,    "account_name": "John D****** O******",    "name_match": "PARTIAL_MATCH",    "validation_message": "The name 'John Doe' partially matches the account holder 'John D****** O******'. Please confirm this is the correct recipient.",    "requires_confirmation": true  }}
{  "code": "BAD_REQUEST",  "description": "Bad Request"}
{  "code": "UNAUTHORISED",  "description": "Unauthorised"}
{  "code": "FORBIDDEN",  "description": "Forbidden"}
{  "type": "SEND_MONEY",  "amount": 30000,  "fee": 1100,  "total": 31100,  "channel": "MPESA",  "currency": "KES",  "can_proceed": true,  "validation": {    "account_valid": true,    "account_name": "John D****** O******",    "name_match": "PARTIAL_MATCH",    "validation_message": "The name 'John Doe' partially matches the account holder 'John D****** O******'. Please confirm this is the correct recipient.",    "requires_confirmation": true  }}
{  "code": "BAD_REQUEST",  "description": "Invalid input"}
{  "code": "RATE_LIMIT_EXCEEDED",  "description": "Too Many Requests"}
{  "code": "INTERNAL_ERROR",  "description": "Internal Server Error"}

Create a payee transaction POST

Create a transaction to one of the caller's saved payee accounts. The server resolves the destination from `payee_account_id` and routes the payment the same way as the matching M-Pesa surface. Requires an authenticated, active user. For a `FLEX_CREDIT` account, get `GET /v1/accounts/flex/{account_id}/tenure/options` and send the chosen option's `tenure`. Omit `tenure` for other account types. A repeated request with the same `Idempotency-Key` header replays the first response instead of creating a second payment, and reusing the key with a different request body returns 409. Returns 404 when `account_id` or `payee_account_id` does not belong to the caller, and 422 when the payee is blocked. Returns 409 `LOAN_REPAYMENT_PAYBILL_NOT_SUPPORTED` when the payee account is a Cowdi loan repayment destination. A payment to a `POCHI` payee account is a Pochi la Biashara payment. It follows the rules of `POST /v1/transactions/mpesa/pochi`.

Transfer between the caller's accounts POST

Transfer money between two of the caller's own accounts. The current version supports one pair: a current account pays a lending account. Requires an authenticated, active user. The custodian sends the caller an OTP, and the transaction waits in `PENDING_OTP_CONFIRMATION` until the caller confirms it with `POST /v1/transactions/{id}/confirm`. Both accounts record the transfer, with the counterparty account, in their transaction details. A repeated request with the same `Idempotency-Key` header replays the first response instead of creating a second transfer, and reusing the key with a different request body returns 409. Returns 404 when `from_account_id` or `to_account_id` does not belong to the caller, and 400 when the accounts are not a current-to-lending pair, use different currencies, or the amount exceeds the destination's outstanding balance.