NunuaPay API documentation
Base URL: https://pay.nunuanow.com/v1. A versioned REST API with scoped API keys, idempotent payment creation, server-side verification and signed outbound webhooks.
Authentication
- Dashboard / management endpoints use a Bearer JWT from
/v1/auth/loginor/v1/auth/register. - Integration endpoints use a scoped API key:
Authorization: Bearer nup_test_<lookup>_<secret>ornup_live_…. - Some read endpoints accept either credential.
Every API key carries scopes, an environment (test or live), optional branchRestrictions and methodRestrictions. The secret is returned once at creation or rotation and can never be retrieved again.
Businesses, branches and identifiers
A Business ID identifies your tenant. A Branch ID identifies a location inside exactly one business. Provider credentials are configured once per business and inherited by branches; method availability can be overridden per branch.
A branch-scoped API key includes the branch in branchRestrictions and must always send that branchId; the API rejects a missing or different branch.
Create a payment intent
curl -X POST https://pay.nunuanow.com/v1/payment-intents \\
-H "Authorization: Bearer nup_live_<lookup>_<secret>" \\
-H "Idempotency-Key: order-1001" \\
-H "Content-Type: application/json" \\
-d '{
"businessId": "<businessId>",
"branchId": "<branchId>",
"method": "card",
"amount": 100,
"currency": "KES",
"email": "customer@example.com",
"merchantRef": "ORDER-1001"
}'amount is an integer in minor units (whole shillings for KES). An optional provider pins routing when several providers offer the same method — still constrained to the key’s business, environment and configured routing.
Verify status
Never trust the browser return. POST /v1/payment-intents/:id/verify re-checks the transaction with the provider server-side. Only a succeeded status means the customer paid.
Idempotency
Payment and refund creation require an Idempotency-Key header ([A-Za-z0-9_\-:.]{8,128}). Repeating a key with an identical request returns the original resource; reusing it with a different body returns 409 idempotency_conflict.
Endpoints
Businesses & branches
GET /v1/businesses,POST /v1/businesses,GET|PATCH /v1/businesses/:idGET|POST /v1/businesses/:id/branches,GET|PATCH /v1/businesses/:id/branches/:branchIdGET /v1/businesses/:id/branches/:branchId/api-setup— identifiers, callback URLs, eligible methodsGET|POST /v1/businesses/:id/members
Providers & methods
GET|POST /v1/businesses/:id/providers,POST /v1/providers/:accountId/testGET|POST /v1/businesses/:id/payment-method-configsGET /v1/payment-methods?businessId=&branchId=¤cy=
Payment lifecycle
POST /v1/payment-intents,GET /v1/payment-intents,GET /v1/payment-intents/:idGET /v1/payment-intents/:id/attemptsPOST /v1/payment-intents/:id/cancel,POST /v1/payment-intents/:id/verify
Refunds
POST /v1/refunds,GET /v1/refunds,GET /v1/refunds/:id— where the provider supports refunds.
Webhooks
POST|GET /v1/webhook-endpoints,PATCH|DELETE /v1/webhook-endpoints/:id,POST /v1/webhook-endpoints/:id/test
Reports & reconciliation
GET /v1/transactions,GET /v1/transactions/exportGET /v1/reports/summary,GET /v1/reconciliation,POST /v1/reconciliation/run
Payment statuses
created, requires_action, processing, pending, succeeded, failed, cancelled, expired. Treat only succeeded as paid.
Error codes
- Auth
unauthenticated,invalid_api_key,api_key_revoked,api_key_expired,api_key_scope_denied- Request
validation_error,invalid_amount,unsupported_currency,invalid_idempotency_key,idempotency_conflict- Resource
not_found,conflict,invalid_state- Provider
provider_not_configured,provider_disabled,provider_unavailable,method_not_available,provider_error,provider_timeout- Lifecycle
payment_already_succeeded,payment_already_terminal,refund_exceeds_available,refund_not_supported- Security
invalid_signature,replay_detected,ssrf_blocked- System
rate_limited,internal
All errors share the envelope { "error": { "code", "message" } }.
Money
Amounts are integer minor units (KES shillings; USD/EUR/GBP cents). The API never uses floating point for money.
Test versus live
Test keys operate against provider test environments; live keys move real money. A live payment intent cannot be created with a test key. Use test credentials and provider sandboxes to run the full flow before going live.