Environments and key types
The three kinds of API key - test, sandbox and production - what each does with money, and the limits that apply to each.
Every PayDirect API key has a key type: DEVELOPMENT (a test key), SANDBOX or
PRODUCTION. The type decides what happens to money when you use the key. All three share one
base URL and one set of endpoints, so the same deployed code runs with any of them - swapping
credentials is how you move between them.
The three key types
DEVELOPMENT (test key) |
SANDBOX |
PRODUCTION |
|
|---|---|---|---|
| Public key prefix | pk_test_… |
pk_live_… |
pk_live_… |
| Environment | TEST | LIVE | LIVE |
| Money | Never real. Every flow returns a mocked result | Real | Real |
| Amount per transaction | Any amount, any currency | Up to GHS 1.00, GHS only | Up to GHS 1.00 until your Integrator is verified, then no cap |
| Use it for | Building and testing your integration | A small real transaction end to end, before launch | Your customers |
The type is fixed when a key is issued, and it fixes the prefix. You cannot change a key between
DEVELOPMENT and SANDBOX/PRODUCTION - issue a new key instead. See
API keys. A key's type is shown as keyType when you
list your keys with GET /integrators/{id}/apikeys.
Warning
Test and live traffic share one webhook URL and one callback URL - there is no split. Every webhook payload carries an
environmentfield (TESTorLIVE), and your receiver must branch on it, or a test payment will look like a real one. See Webhooks overview.
Test keys: everything is mocked
With a DEVELOPMENT key, no payment provider is ever contacted, in any flow: collections, hosted
checkout, bill payments, payouts, withdrawals and transfers. Each returns an immediate SUCCESS
with a provider reference starting SIMULATED-, and the rest behaves as it would for real - the
transaction record, your test ledger balance, webhooks, idempotency, scopes and signing.
Because nothing real moves, test keys have no per-transaction cap: use whatever amounts and currencies your tests need. Your test ledger balance still applies, though. A payout or withdrawal needs a test balance to draw on, which mocked collections provide.
Because mocked outcomes are immediate, you will not see a PENDING result, a slow approval or a
provider decline with a test key. Your code for those paths - waiting for the webhook, calling
reconcile, handling FAILED - still has to be right. Review it carefully, and use a SANDBOX key
to watch one real transaction before launch.
Sandbox keys: real money, GHS 1.00
A SANDBOX key moves real money through the real providers, limited to GHS 1.00 per
transaction and to GHS only. It is how you see your integration handle a real payer, a real
wallet prompt and a real settlement, for a cost of a few cedis. A request over the limit is
refused with 403 before anything happens:
{
"code": 403,
"status": "error",
"message": "SANDBOX keys are limited to GHS 1.00 per transaction."
}
The limit applies to every sandbox key, and does not change when your Integrator is verified.
Production keys
A PRODUCTION key moves real money for your customers. Until PayDirect has verified your
Integrator, its production keys share the sandbox limit of GHS 1.00 per transaction; after
verification there is no per-transaction cap from PayDirect beyond your configured limits. See
Onboarding and verification.
Payments made from a signed-in dashboard session follow the same rule as a production key: they are real, and limited to GHS 1.00 per transaction until your Integrator is verified.
What is the same for every key
| All key types | |
|---|---|
| Endpoints, request and response shapes | Identical |
| Idempotency, scopes, signing | Identical |
| Rate limit | One budget per Integrator, shared by all your keys |
| Webhook retry window | Test keys: hourly, up to 10 hours. Sandbox and production: every 3 minutes for 4 tries, then hourly, up to 72 hours |
Test and live balances and transactions are kept fully separate: a mocked collection can never fund a real payout.
A key whose type and prefix do not agree is refused with 403 "This API key's type does not match its environment. Contact support.". You should never see it; if you do, contact
support.
Working with test keys
Exercise all of it before you go live:
- Collections -
POST /transactions/collect, then reconcile the transaction and confirm your records agree. - Payouts -
POST /transactions/disburseto both a bank account and a wallet. - Withdrawals -
POST /transactions/withdraw, once a settlement account is registered. - Webhooks - point
webhookUrlat a tunnel (ngrok, localtunnel) and verify signatures against real deliveries, not hand-made ones. - Failure handling - force a
400with a bad payload, reuse anIdempotency-Key, let a request expire. The unhappy paths are the ones that bite in production.
Then run one small real transaction with a sandbox key, and work through the go-live checklist.
Restricting where keys can be used
You can limit test and live traffic to a list of source addresses, so a leaked key is useless from anywhere else. See IP allowlisting.
Next
API keys and credentials - issuing, scoping, rotating and revoking.