TeronaPay

Wallets API

Manage the wallets under your account and move funds between them. All endpoints require API-key auth and are scoped to the authenticated account.

The Wallet object

{
  "id": "wlt_3xQ8d2Kpath7",
  "number": "47514771",
  "name": "Operations",
  "currency": "KES",
  "is_default": false,
  "status": "active",
  "available": 12500.00,
  "pending": 0.00,
  "created_at": "2026-06-05T10:00:00Z"
}
FieldTypeNotes
idstringWallet id, prefixed wlt_. Accepted as wallet_id anywhere
numberstring8-digit account number. A human-friendly alias — accepted as wallet_id anywhere the id is, and the two never collide
namestringHuman label, unique per account
currencystringISO-4217. Fixed at creation
is_defaultboolThe wallet used when a request omits wallet_id
statusenumactive / closed
availablenumberSpendable balance, decimal major units
pendingnumberReserved (in-flight payouts), decimal major units
created_attimestamp

POST /v1/wallets

Create a wallet.

Request

{
  "name": "Operations",
  "currency": "KES"
}
FieldTypeRequiredNotes
namestring1–64 chars, unique per account
currencystringISO-4217; defaults to the account's default_currency
curl -X POST https://api.teronapay.com/v1/wallets \
  -u "$KEY:$SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Operations", "currency": "KES" }'

Response 201: a Wallet object (available and pending start at 0).

Errors

  • 400 invalid_request — missing/oversized name, unsupported currency
  • 409 conflict — a wallet with that name already exists on the account
  • 422 precondition_failed — per-account wallet limit reached

GET /v1/wallets

List the account's wallets. Returns the default wallet first, then by created_at.

{
  "data": [ /* Wallet objects, balances included */ ]
}

Query parameters: currency (filter), status (active / closed).

GET /v1/wallets/{id}

Retrieve one wallet, including live available / pending balances.

Errors

  • 404 not_found — unknown id or not on this account

GET /v1/wallets/{id}/balance

Balance-only shortcut (cheaper than the full object when you poll). The {id} path segment accepts the wlt_… id or the 8-digit number.

{ "wallet_id": "wlt_3xQ8d2Kpath7", "number": "47514771", "currency": "KES", "available": 12500.00, "pending": 0.00 }

PATCH /v1/wallets/{id}

Update mutable fields. Currency is immutable.

Request

{ "name": "Operations (KE)", "is_default": true }
FieldTypeNotes
namestringRename
is_defaultboolPromote to default. The previous default is demoted. Must match the account's default_currency

DELETE /v1/wallets/{id}

Close a wallet. Response 204.

Errors

  • 422 precondition_failed — wallet is the default, or holds a non-zero available/pending balance. Move funds out first (transfer or payout).

Transfers

Move funds between two wallets of the same account. Ledger-only and synchronous — no external rails, no fee, no callback. A transfer either succeeds atomically or fails inline.

The Transfer object

{
  "id": "trf_7Yk2mZpQ9aLr",
  "from_wallet_id": "wlt_DEFAULT00001",
  "to_wallet_id": "wlt_3xQ8d2Kpath7",
  "amount": 500.00,
  "currency": "KES",
  "status": "succeeded",
  "reference": "float-topup-may",
  "created_at": "2026-06-05T10:05:00Z"
}
FieldTypeNotes
idstringTransfer id, prefixed trf_
from_wallet_idstringDebited wallet
to_wallet_idstringCredited wallet
amountnumberDecimal major units, positive
currencystringMust match both wallets
statusenumAlways succeeded (transfers resolve synchronously)
referencestringOptional merchant note

POST /v1/transfers

Request

{
  "from_wallet_id": "wlt_DEFAULT00001",
  "to_wallet_id": "wlt_3xQ8d2Kpath7",
  "amount": 500.00,
  "currency": "KES",
  "reference": "float-topup-may"
}
FieldTypeRequiredNotes
from_wallet_idstringSource wallet — wlt_… id or 8-digit number
to_wallet_idstringDestination wallet (must differ from source) — wlt_… id or 8-digit number
amountnumberDecimal major units, positive
currencystringDefaults to the wallets' currency; rejected if it differs
referencestringFree-text note

Idempotency: send Idempotency-Key: <key> to dedupe retries.

Response 201: a Transfer object with status: "succeeded".

Errors

  • 400 invalid_request — same source and destination, non-positive amount
  • 404 not_found — a wallet id doesn't exist on this account
  • 422 precondition_failed — insufficient available in the source wallet, or the two wallets have different currencies

GET /v1/transfers

List transfers. Query parameters: wallet_id (either side), since, before, limit. Same paging shape as GET /v1/payments.

GET /v1/transfers/{id}

Retrieve one transfer.

Status codes summary

CodeMeaning
200OK — GET succeeded
201Created — wallet or transfer created
204No content — wallet closed
400Bad request
404Wallet/transfer not found
409Duplicate wallet name
422Precondition failed (limit reached, non-empty wallet, insufficient balance, currency mismatch)
429Rate limit exceeded
API: Wallets · TeronaPay docs