Webhooks
Webhooks are how NamibraPay tells your server that money moved — the moment a collection settles, a payout lands or a subscription renews. Configure one URL per environment; we sign every delivery.
Configure a webhook endpoint
Register an HTTPS URL per environment from the dashboard, or with the API. Sandbox events only ever go to your test URL and live events only to your live URL.
/api/v1/merchant/webhooks/configAuthenticates with your API keys.
Body parameters
| Parameter | Description | |
|---|---|---|
url | required | stringHTTPS endpoint that will receive POST requests. |
environment | required | stringtest or live — which environment delivers to this URL. |
curl -X POST "https://api.namibrapay.com/api/v1/merchant/webhooks/config" \
-H "X-API-Key: pk_test_xxxxxxxxxxxxxxxx" \
-H "X-API-Secret: sk_test_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "url": "https://api.yourapp.com/webhooks/namibrapay", "environment": "test" }'Each configured environment gets a signing secret used to compute delivery signatures. You can rotate it at any time with POST /merchant/webhooks/config/{environment}/regenerate-secret.
Events
| Event | Fired when |
|---|---|
| collection.initiated | A direct charge or checkout payment has been created and is awaiting the payer. |
| collection.processing | The provider accepted the charge; awaiting final confirmation. |
| collection.success | The collection settled — money is in your balance. Fulfil the order. |
| collection.failed | The payer declined or the charge failed. |
| payout.initiated | A payout was created and is pending. |
| payout.processing | The rail accepted the payout; funds are in flight. |
| payout.completed | The payout landed in the recipient account. |
| payout.failed | The payout was rejected; funds returned to your balance. |
| refund.initiated | A refund request was submitted. |
| refund.completed | The refund reached the customer. |
| dispute.created | A customer opened a dispute on a transaction. |
| dispute.resolved | The dispute was resolved by support. |
| kyc.submitted | Your compliance application was submitted. |
| kyc.approved | Your compliance application was approved — live keys unlock. |
| kyc.rejected | Your compliance application was rejected. |
| subscription.charged | A scheduled subscription debit succeeded. |
| subscription.payment_failed | A scheduled subscription debit failed. |
Payload
Every delivery is a POST with a JSON body and two headers: Content-Type: application/json and X-Webhook-Signature.
{
"event": "collection.success",
"timestamp": "2026-10-26T09:14:41.512Z",
"data": {
"reference": "NPR-k21jf9ds",
"merchantReference": "ORDER-67890",
"amount": 1000,
"currency": "GHS",
"channel": "mtn_momo",
"status": "success"
}
}Verifying signatures
The X-Webhook-Signature header is an HMAC-SHA256 hex digest of the raw request body, keyed with your environment's signing secret. Verify it before processing anything.
Use the raw body
express.raw(), Flask request.get_data()).# Test your endpoint's verification with a replay from the logs API:
curl -X POST "https://api.namibrapay.com/api/v1/merchant/webhooks/test" \
-H "X-API-Key: pk_test_xxxxxxxxxxxxxxxx" \
-H "X-API-Secret: sk_test_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "event": "collection.success" }'Retries & timeouts
- We wait up to 10 seconds for a response. Anything slower (or any non-2xx status) counts as a failure.
- Failed deliveries are retried up to 5 times with exponential backoff starting at 5 seconds.
- Respond 200 as fast as possible — do heavy work asynchronously, not inside the handler.
Best practices
- Verify every signature before trusting a payload.
- Make handlers idempotent — retries mean you may receive the same event more than once. Key your side effects on
data.reference. - Don't rely on event order. A late reconciliation can deliver
collection.successafter you gave up on a timeout — handle state transitions, not just messages. - Confirm with the API for high-value flows. When a webhook triggers money movement, call the verify endpoint as a second check.
Logs & manual retries
Every delivery — successful or not — is recorded with its status code, response snippet and retry count. Inspect logs from the dashboard or the API, and replay any delivery:
/api/v1/merchant/webhooks/logsAuthenticates with your API keys.
/api/v1/merchant/webhooks/logs/{id}/retryAuthenticates with your API keys.
/api/v1/merchant/webhooks/testAuthenticates with your API keys.
GET /api/v1/merchant/webhooks/logs
{
"success": true,
"data": [
{
"id": "wl_9fj21kda",
"event": "collection.success",
"url": "https://api.yourapp.com/webhooks/namibrapay",
"httpStatus": 200,
"success": true,
"retryCount": 0,
"createdAt": "2026-10-26T09:14:42.100Z"
}
]
}