# Integrate NuePay API v1

Build against https://api.nuepay.net/v1. Read the linked contract and relevant guides before writing code. If behavior is unspecified, flag the gap instead of inventing an endpoint or parameter.

## Implementation rules

- Call NuePay from your server with `Authorization: Bearer <secret>`. Keep credentials in environment variables; never embed them in browser code, prompts, logs or source control.
- A named key identifies its application and merchant. Do not send an application nickname or account number for authentication. Up to ten applications share the merchant wallet; there are no separate application wallets or spending scopes.
- Start with `np_test_` credentials and simulated funds. The same API base accepts test and live keys. Verify `/application` and the expected `mode` before testing. Only switch to real payments when explicitly authorized by the merchant.
- Send UGX amounts as decimal strings, never binary floating-point calculations. Collections credit the requested principal; the payer covers the quoted total, rounded up to whole UGX. Disbursements require whole UGX amounts and debit principal plus fee. Get `/quotes` before displaying charges; quotes do not move or reserve funds.
- Bind the displayed total: send `expected_payer_charge` for collections or `expected_wallet_debit` for disbursements. A `409 quote_changed` rejects creation before any payment starts; refresh the quote and obtain approval again. Accepted retries retain their saved pricing and exact payload. Limits and fee ranges can change; never hardcode them.
- Persist a UUID and the exact payment payload before calling `/collections` or `/disbursements`. Send that UUID in `Idempotency-Key`. After a timeout or retry, reuse both; never issue a fresh UUID to retry an uncertain payment. A changed payload with the same UUID returns `409`.
- `202` means accepted. Only `success` confirms payment. `process` is unresolved; an HTTP or browser timeout is not proof of failure. Collections unconfirmed at NuePay's 120-second deadline return `failed/payment_timeout`; poll every 15 seconds. A confirmed failure permits a fresh customer attempt with a new UUID. A delayed notification of an earlier success must still be reconciled and fulfilled once; never discard a confirmed receipt or release an interrupted invoice-posting reservation because a browser timer expired. An actual NuePay `failed/payment_timeout` result remains a failed checkout; contradictory late funds need separate reconciliation.
- For bounded collection initiation, send optional `request_expires_at` in exact UTC `YYYY-MM-DDTHH:MM:SSZ` form and preserve it on retries. New requests at or after that acceptance deadline return `410 request_expired` without creation; existing identical requests still return the original payment. It does not extend approval time. Recover a lost response with `GET /collections/{requestId}` using the saved idempotency UUID, application and environment. This never sends a phone prompt. A 404 after the saved acceptance deadline confirms that request can no longer create a collection. Use `/transactions/{reference}` once the reference is known.
- Handle non-2xx responses using `code`, `message` and `errors`. Log the HTTP `X-Request-Id` for investigation; `data.request_id` identifies the original payment UUID, not the current HTTP attempt. Honor rate limits and use bounded backoff.
- Configure each application's HTTPS notification URL in Keys & Webhooks. Use View Signing Secret beside the same key to copy its whsec_ code. The API key requests payments; the signing secret verifies updates. Verify `X-NuePay-Signature` using HMAC-SHA256 over `timestamp + "." + raw_body`, compare in constant time, and reject timestamps more than five minutes from your synchronized clock.
- After signature verification, match body `id` to `X-NuePay-Event-Id`, check `livemode`, durably deduplicate event IDs, and apply only newer payment `version` values. Save events before returning 2xx. Deliveries may repeat or arrive out of order; fulfil a real order only on a live success.

## Acceptance checks

- Test collection and disbursement success, failure and timeout with the sandbox simulation endpoint. Sandbox never moves real funds; sandbox webhook delivery is opt-in.
- Test identical retries, changed-payload conflicts and a network timeout after dispatch. Verify that neither charges nor fulfilment repeat.
- Test invalid signatures, stale timestamps, duplicate/out-of-order webhooks, test/live isolation and insufficient wallet funds.
- Store payment reference, application ID, merchant reference, idempotency UUID, status and version. Reconcile any unresolved payment before showing a final outcome.

## Read next

- [Quickstart](https://docs.nuepay.net/quickstart/index.md)
- [All endpoints](https://docs.nuepay.net/api/index.md)
- [Retries and statuses](https://docs.nuepay.net/reliability/index.md)
- [Webhook verification](https://docs.nuepay.net/webhooks/index.md)
- [Going live](https://docs.nuepay.net/going-live/index.md)

## Source

API v1. Behavior reviewed against platform commit `4293996be449bc8ae20e83b911a069b1662f3442` on 2026-10-25.
Docs index: https://docs.nuepay.net/llms.txt. Contract: https://docs.nuepay.net/openapi.json.

---

# Recover a collection

Find an accepted collection using its saved request UUID.

Canonical page: https://docs.nuepay.net/api/collection-lookup/

`GET https://api.nuepay.net/v1/collections/{requestId}`

Lost the collection response? Use the UUID you saved in `Idempotency-Key`. This lookup never sends a phone prompt.

## Request

Replace the example UUID with your original payment UUID. Use the same application's secret and environment.

```bash
curl -X GET https://api.nuepay.net/v1/collections/00000000-0000-4000-8000-000000000001 \
  -H "Authorization: Bearer $NUEPAY_TEST_KEY"
```

 

## Parameters

| Name | Location | Details |
| --- | --- | --- |
| `requestId` | path | The original UUID supplied in Idempotency-Key when creating the collection. |

## Use the result

**200:** the same payment resource as [transaction status](https://docs.nuepay.net/api/transactions/index.md). Only `success` confirms payment. **404:** no accepted collection matches this application, environment and UUID; no payment is created by this lookup.

Set `request_expires_at` when [creating the collection](https://docs.nuepay.net/api/collections/index.md) to prevent an old request arriving after your checkout deadline. Keep that value unchanged on retries. Once that deadline has passed, a 404 confirms that request can no longer create a collection. Disbursements are excluded.

### All response fields

| Field | Type | Details |
| --- | --- | --- |
| `reference` (required) | string | NuePay payment reference. Use it to read this payment. |
| `project_id` (required) | string · uuid or null | Application ID; retained alongside application_id in the payment resource. |
| `application_id` (required) | string · uuid or null | The application identified by the secret used to create this payment. |
| `application_name` (required) | string or null | Application nickname recorded for this payment. It is not an authentication parameter. |
| `request_id` (required) | string · uuid or null | Original payment idempotency UUID, not the current HTTP X-Request-Id. |
| `idempotency_key` (required) | string · uuid or null | The original Idempotency-Key UUID. |
| `merchant_reference` (required) | string or null | Your optional order reference. It does not provide idempotency. |
| `status_reason` (required) | string or null | Additional reason, including payment_timeout when an unconfirmed collection reaches its two-minute deadline, awaiting_confirmation or review_required; null when absent. |
| `amount` (required) | string | Requested principal, as a UGX decimal string. |
| `fee` (required) | string | Quoted fee, as a UGX decimal string. |
| `gross_amount` (required) | string | Principal plus fee, as a UGX decimal string. |
| `currency` (required) | string | — |
| `payer_charge` (required) | string or null | Total charged to the collection payer; null for disbursements. |
| `wallet_credit` (required) | string or null | Collection principal credited to the wallet; null for disbursements. |
| `recipient_amount` (required) | string or null | Amount sent to the disbursement recipient; null for collections. |
| `wallet_debit` (required) | string or null | Disbursement principal plus fee; null for collections. |
| `created_at` (required) | string · date-time or null | Creation timestamp in ISO 8601 format. |
| `updated_at` (required) | string · date-time or null | Last update timestamp in ISO 8601 format. |
| `expires_at` (required) | string · date-time or null | Collection confirmation deadline in ISO 8601, 120 seconds after created_at; null for disbursements. |
| `type` (required) | string | `collection`, `disbursement` |
| `status` (required) | string | process is unresolved; only success confirms payment. Unconfirmed collections become failed after 120 seconds. An HTTP timeout alone does not establish the outcome. |
| `status_label` (required) | string | Display label. Use status for program logic. |
| `mode` (required) | string | test uses simulated funds; live uses real funds. |
| `livemode` (required) | boolean | True only for real payments. Check it before fulfilling a live order. |
| `refunded` (required) | boolean | Whether a failed disbursement has been refunded to the wallet. |
| `version` (required) | integer | Monotonically increasing payment state version. Ignore older webhook versions. |

### HTTP status codes

| Status | Meaning |
| --- | --- |
| `200` | Current collection state. |
| `401` | Invalid credentials. |
| `403` | Access unavailable or operation paused. |
| `404` | Resource not found in this application and environment. |
| `409` | Idempotency conflict, quote changed, insufficient funds, or payout setup required. |
| `422` | Invalid input; inspect errors and code. |
| `429` | Rate limit exceeded. |
| `503` | Live provider unavailable; accepted payments can still be read or retried. |

[Error codes and recovery →](https://docs.nuepay.net/errors/index.md)
