Test Mode And Sandbox

Build and test the full payment lifecycle without moving real money.

Every Kyshi integration should be built in test mode first. Test mode runs the same endpoints, the same validation, and the same webhooks as live mode — it just does not move real money.

How Mode Is Chosen

Mode is resolved from the secret key you authenticate with. There is no mode header and no mode parameter.

Key prefixMode
sk_test_Test
sk_live_Live

If you send a test key, you are in test mode for that request. Sending a live key puts that request in live mode, regardless of anything else in the payload.

Data Is Isolated

Customers, transactions, virtual accounts, beneficiaries, plans, subscriptions, and references created in test mode are not visible in live mode, and vice versa.

This means a reference you used in test mode is free to reuse in live mode, and that a test-mode ID will return 404 if you query it with a live key. A 404 on an object you are certain exists is almost always a mode mismatch.

Webhooks are isolated too. Configure your test webhook URL and your live webhook URL separately in your business settings.

Simulating Inbound Payments

The hardest part of testing a collections integration is that you cannot make a real bank transfer on demand. Kyshi gives you endpoints that stand in for the customer.

Simulate A Virtual Account Credit

Credits a test virtual account as though a customer had sent a bank transfer. This fires the same webhooks a real credit would.

POST {{host}}/v1/wallets/credit
{
  "accountNumber": "1234567890",
  "amount": "5000"
}

Rejected in live mode. See Simulate Credit for the full response.

Use this to test the cases that are otherwise hard to reach:

To testSend
COMPLETEDExactly the expected amount
PARTIALLess than the expected amount
OVERPAIDMore than the expected amount

Underpayment and overpayment are normal in production, because the customer chooses the amount in their own banking app. Test both before going live. See Statuses And Lifecycles.

Simulate Subscription Billing

Subscription bugs usually only appear on the second or third billing cycle, which is a long wait in real time. This endpoint moves a subscription through its lifecycle immediately.

POST {{host}}/v1/subscriptions/{id}/simulate
EventUse forSimulates
pre_charge_reminderCardThe reminder sent before a renewal.
due_chargeCard or manualA renewal falling due now.
payment_failedCardA failed renewal and the recovery flow.
retry_dueCard or manualA retry falling due now.
retry_failed_cancelCardA final failed retry that can cancel the subscription.
manual_invoice_dueManualAn invoice becoming due, with its checkout URL.
manual_overdue_reminderManualAn invoice going overdue.

Manual subscriptions are those created with bank_transfer, bank, or mobile_money — anything that cannot be auto-charged. See Simulate Subscription.

Test-mode only.

A Suggested Test Pass

Before you ask for live access, confirm you handle all of these:

  1. A successful collection, end to end, with value released exactly once.
  2. The same webhook delivered twice — your system must not double-fulfil.
  3. A PARTIAL virtual account credit.
  4. An OVERPAID virtual account credit.
  5. A failed payment, with the customer able to retry.
  6. A subscription renewal that fails and recovers.
  7. A subscription that fails all retries and cancels.
  8. A webhook your endpoint returns 5xx for, then recovers and reconciles via the API.

Items 2 and 8 are the ones most integrations skip and most often get wrong in production.

Moving To Live

When your test pass is complete, see the going-live checklist on Environment. At minimum: swap to the live key, configure the live webhook URL, and confirm your enabled currencies are right for live mode.


Did this page help you?