How The Kyshi API Works
Conventions that apply to every endpoint — envelopes, references, listing, and async behaviour.
These conventions hold across the whole API. Learning them once saves reading them on every endpoint page.
One Response Envelope
Every response uses the same shape, success or failure.
{
"status": true,
"message": "Success",
"code": 200,
"data": {}
}| Field | Meaning |
|---|---|
status | true on success, false on failure. |
message | Human-readable summary. Log it; do not branch on it. |
code | Mirrors the HTTP status code. |
data | The payload. {} on failure. |
Branch on the HTTP status code and on status. Never branch on message — the wording can change without notice.
const res = await fetch(url, { headers: { 'x-api-key': key } });
const body = await res.json();
if (!res.ok || !body.status) {
throw new KyshiError(res.status, body.message);
}
return body.data;Versioning
Every public endpoint is namespaced under /v1:
https://api.kyshi.co/v1/transactions/initializeThe base URL is the same in test and live mode. Your key decides which one you are in. See Test Mode And Sandbox.
References Are Your Idea, Not Ours
Most creation endpoints accept a reference that you supply. Use your own order or invoice ID.
This matters more than it looks. Your reference is how you:
- verify a payment without storing a Kyshi ID
- reconcile a settlement batch back to your orders
- recognise a webhook for something you already processed
{
"email": "[email protected]",
"amount": 1000,
"localCurrency": "NGN",
"reference": "ORDER-10001"
}Then verify by the same reference:
GET {{host}}/v1/transactions/verify/ORDER-10001References must be unique within a mode. Because test and live data are isolated, a reference used in test mode is free to reuse in live mode.
If you do not supply one, Kyshi generates it and returns it. Store whatever comes back.
Payments Are Asynchronous
A 201 means Kyshi accepted your request, not that the customer has paid. Almost nothing about a payment is decided at the moment you create it.
| You call | You get back | The money arrives |
|---|---|---|
| Initialize transaction | A checkout URL | When the customer completes checkout |
| Charge (bank transfer) | Payment instructions | When the customer makes the transfer |
| Create virtual account | Account details | Whenever the customer chooses to send |
| Initiate transfer | An accepted payout | When the rail completes it |
Design for the gap. Never release goods, credit, or access on the response to the creation call.
There are two ways to learn the outcome:
Webhooks tell you as soon as something changes. Fast, but a delivery can be missed. See Webhook.
Verification endpoints are authoritative. Slower, but definitive.
Use both: act on the webhook, confirm with verification before giving value, and reconcile periodically with the list endpoints to catch anything missed.
Listing And Filtering
List endpoints share a common set of query parameters.
| Parameter | Default | Notes |
|---|---|---|
page | 1 | Page number, starting at 1. |
limit | 100 | Maximum 100. |
order | DESC | ASC or DESC. |
sort | — | Field to sort by. |
query | — | Free-text search. |
GET {{host}}/v1/transactions/history?page=1&limit=50&order=DESCBecause the default order is newest first, paging through a list while new records are arriving can shift rows between pages. For reconciliation, filter to a closed time range rather than walking pages of live data.
See Pagination And Filtering for the full detail.
Amounts
Amounts are in major units. 1000 means one thousand Naira. See Countries And Currencies.
Errors
Failures use the same envelope with status: false and an HTTP status code that tells you what kind of problem it is. 4xx means fix the request or the configuration; 5xx means Kyshi. See Errors.
Next
- Authentication — keys and modes
- Getting Started — choose a product flow
- Test Mode And Sandbox — build without moving money
Updated about 20 hours ago
