---
name: nopan-webhooks
description: Apply this guide when configuring or handling Nopan webhooks — exposing a callback endpoint, registering it in the portal, choosing which events to subscribe to, verifying the signature on incoming notifications, returning the right status code, or debugging missed deliveries and retries. Relevant for any asynchronous payment notification flow from Nopan.
---

# Nopan webhooks

Webhooks are how Nopan pushes lifecycle events (payment captured, refunded, canceled, etc.) to your system in real time,
so you don't have to poll `/payments/{id}/status`.

> Authoritative
>
pages: [Webhooks Overview](https://docs.nopan.com/guides/webhooks/webhook-overview), [Webhook Setup](https://docs.nopan.com/guides/webhooks/webhook-setup), [Webhook Types](https://docs.nopan.com/guides/webhooks/webhook-types).

> **Match existing patterns** — if the project already runs a webhook pipeline for other providers (verify → enqueue → dedupe → reconcile), Nopan webhooks should plug into it: same controller shape, same queue, same dedupe store, same reconciliation step. Do not build a parallel pipeline. The Nopan-specific bits — signature verification, the 2xx-fast contract, the 24h/10-retry window — slot into the existing stages; they don't replace them. See [`nopan-integration`](https://docs.nopan.com/skills/nopan-integration.md) Step 0.

## Delivery contract

- **Transport:** HTTPS POST with a JSON body.
- **Acknowledge:** Your endpoint must return a **2xx** status — `202 Accepted` is the recommended choice.
- **Anything else** (non-2xx, timeout, TLS failure) is treated as a delivery failure and triggers a retry.
- **Retries:** Up to **10 retries within 24 hours** with backoff. After 24h, the delivery is dropped.

## Endpoint design rules

1. **Acknowledge first, process second.** Return `202` immediately. Do business logic asynchronously (queue/worker).
   This keeps you under Nopan's delivery timeout and avoids retries triggered by slow downstreams.
2. **Be idempotent.** A retry sends the **same** event. Use the event's identifier (and/or the related `transactionId` +
   state) as a dedupe key — never double-fulfill an order on retry.
3. **Verify the signature before trusting the payload.** Reject any request that fails verification with `401`. See [
   `nopan-authentication`](https://docs.nopan.com/skills/nopan-authentication.md) for the signing model; webhooks carry
   a signature header you must validate against the registered key material.
4. **Tolerate event reordering and gaps.** Don't assume events arrive in lifecycle order. Reconcile against the current
   payment state when in doubt (`GET /payments/{transactionId}/status`).
5. **Don't validate business state from the webhook alone for high-stakes actions** (e.g. shipping, refunding, releasing
   access). Treat the webhook as a trigger; confirm with a status fetch when the action is irreversible.

## Configuration

Done in the **Nopan Portal**, not via API:

- Log in to **Nopan portal** (sandbox: `https://portal.nopan.com/sandbox`; production:
  `https://portal.nopan.com/production`)
- Click "Add new webhook" button.
- Add your callback URL (sandbox: `https://portal.nopan.com/sandbox/webhooks`; production:
  `https://portal.nopan.com/production/webhooks`).
- Subscribe only to the event types you care about — fewer subscriptions = less noise and lower load.
- For setup detail: [Webhook Setup](https://docs.nopan.com/guides/webhooks/webhook-setup).

## Event types

The catalog covers the payment lifecycle (initiation, finalize, capture, refund). **Do not embed the list here** — it
evolves.
Fetch [Webhook Types](https://docs.nopan.com/guides/webhooks/webhook-types) when you need the current names and payload
shapes.

When mapping events to your domain:

- Treat `PAYMENT_INITIATE` as update on initiate payment.
- Treat `PAYMENT_FINALIZE` as payment finalize update. Used to receive user confirmation, expiration, etc
- Treat `PAYMENT_CAPTURE` as payment capture update. Used to receive confirmations, amount captured, etc
- Treat `PAYMENT_REFUND` as payment refund update. Used to receive confirmations, amount refunded, etc

## Verifying signatures

Every webhook arrives with a signature header. Details on the verification flow, the key material to use, and the
canonicalization rules live at [Response Validation](https://docs.nopan.com/guides/authentication/response-validation)
and the related signature pages — fetch them before implementing verification rather than assuming an algorithm.

Always verify **before** parsing or acting on the body. A failed verification means:

- The request is forged, or
- Your key/secret is misconfigured, or
- The body was modified by a proxy.

Return `401 Unauthorized` and do not retry processing.

## Debugging missed or duplicate deliveries

| Symptom                            | Likely cause                                                         | What to check                                                                                                                        |
|------------------------------------|----------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| Never received an event            | Endpoint not reachable from internet, TLS error, wrong URL in portal | Curl the endpoint from outside the perimeter; check portal config                                                                    |
| Received once but duplicated later | Returned non-2xx the first time → retry hit later                    | Always return 2xx after verification; check application logs                                                                         |
| Received many duplicates           | Slow ack → retried during backoff window                             | Ack with 202 immediately, defer processing                                                                                           |
| Signature verification fails       | Wrong key, mismatched algorithm, body modified by middleware         | Verify the raw body bytes before any framework parses them                                                                           |
| Stopped receiving after 24h        | Past the retry window                                                | Reconcile with `GET /payments/{transactionId}/status` and `GET /payments/{transactionId}/events`; fix the endpoint for future events |

## Decision rules

- **Building a new endpoint** → expose HTTPS, return `202` early, verify signature, queue the work.
- **Choosing webhooks vs polling** → webhooks by default; poll only as a backstop for critical operations after a gap.
- **Reacting to `PAYMENT_CAPTURE`** → safe to fulfill (after dedupe) for most schemes; verify the amount and currency
  match the order.
- **Reacting to `PAYMENT_REFUND`** → update internal ledger and notify the shopper; do **not** initiate another refund
  in response.
- **Endpoint outage suspected** → check the portal's delivery history before assuming an event was never sent.

## Related guides

- Signature verification model → [`nopan-authentication`](https://docs.nopan.com/skills/nopan-authentication.md).
- Mapping events to lifecycle states → [`nopan-payments`](https://docs.nopan.com/skills/nopan-payments.md).
- Testing webhook delivery in sandbox → [`nopan-testing`](https://docs.nopan.com/skills/nopan-testing.md).