# Overview # Webhook Overview PEXX uses webhooks to notify your server about asynchronous events — for example, when a transaction's status changes. Instead of polling our APIs, you provide an HTTPS endpoint and Pexx delivers a signed JSON payload to it as soon as the event happens. This page covers everything you need to integrate: how your endpoint is set up, the request format, how to verify signatures, and how to handle retries. *** ## 1. Quick Start 1. Stand up an HTTPS endpoint on your side that accepts `POST` requests and returns `2xx` quickly. 2. Send your endpoint URL to your Pexx contact for both `Sandbox` and `Production`. The URL and the signing secret (`whsec_...`) are configured **manually** by Pexx and shared back to you over a secure channel — store the secret securely. 3. On every incoming request, **verify the signature** (see §4) and process the event **idempotently** by `X-Webhook-Event-Id`. 4. Reply with `2xx` within 15 seconds. > Per-event-type subscription (choosing which event types your endpoint receives) is **still under development**. For now, your endpoint will receive every event type PEXX emits for your merchant. *** ## 2. Endpoint Requirements | Requirement | Detail | | ---------------------- | ------------------------------------------------------------------------------------------ | | Protocol | **HTTPS only.** Plain `http://` URLs are not accepted. | | Method | `POST` | | Response time | Reply with a `2xx` status within **15 seconds** (otherwise the call is treated as failed). | | Idempotency | The same event ID may be delivered more than once. Multiple processings must be safe. | | Signature verification | Required for production traffic. See §4. | > Tip: do not perform long-running work inside the webhook handler. Persist or enqueue the event first, return `2xx`, and process it asynchronously. *** ## 3. Request Format Pexx delivers events as an `HTTP POST` with a JSON body. ### 3.1 Headers | Header | Description | | ---------------------- | ---------------------------------------------------------------------------------------------------------------- | | `Content-Type` | Always `application/json`. | | `X-Webhook-Event-Id` | Unique event ID (UUID). Use this for **idempotent processing**. | | `X-Webhook-Event-Type` | Event type, e.g. `transaction.updated`. | | `X-Webhook-Timestamp` | Unix timestamp (in **milliseconds**) when Pexx initiated this request. Used for signature and replay protection. | | `X-Webhook-Signature` | `sha256=` — HMAC-SHA256 signature of the request (see §4). | ### 3.2 Body ```json { "id": "9c4f8a72-3e71-4f4a-bc2a-1f0d8b8e1a91", "group": "transaction", "type": "transaction.updated", "occurred_at_ms": 1745793600000, "data": { // Event payload — schema depends on the event type. // See the per-event reference for details. } } ``` | Field | Type | Description | | ---------------- | ------ | ------------------------------------------------------------------------- | | `id` | string | The event ID. Same value as the `X-Webhook-Event-Id` header. | | `group` | string | Event group, lowercase (e.g. `transaction`). | | `type` | string | Event type (e.g. `transaction.updated`). | | `occurred_at_ms` | number | When the **business** event happened (UTC, milliseconds since epoch). | | `data` | object | Event-specific payload. See the per-event reference for the exact schema. | *** ## 4. Signature Verification To protect your endpoint from spoofed traffic, every request is signed with HMAC-SHA256 using the secret Pexx shared with you. **Verify the signature on every incoming request.** ### 4.1 Algorithm ```text signed_payload = X-Webhook-Timestamp + "." + expected_sig = HMAC_SHA256(secret, signed_payload) // lowercase hex ``` Compare `expected_sig` against the value after `sha256=` in `X-Webhook-Signature` using a **constant-time comparison**. If they match, the request is authentic. ### 4.2 Replay Protection * Reject any request whose `X-Webhook-Timestamp` differs from your server time by more than a small window (we recommend 5 minutes). * Track `X-Webhook-Event-Id` in your own datastore and skip duplicates. ### 4.3 Verification Example (Node.js) ```javascript const crypto = require('crypto'); function verifyPexxWebhook(rawBody, headers, secret) { const timestamp = headers['x-webhook-timestamp']; const signature = (headers['x-webhook-signature'] || '').replace(/^sha256=/, ''); const expected = crypto .createHmac('sha256', secret) .update(`${timestamp}.${rawBody}`) .digest('hex'); const a = Buffer.from(expected, 'hex'); const b = Buffer.from(signature, 'hex'); return a.length === b.length && crypto.timingSafeEqual(a, b); } ``` > ⚠️ Verification must use the **raw, unmodified request body** as it was received over the wire. Any re-serialization or reformatting will produce a different signature. *** ## 5. Retries If your endpoint does not return `2xx` within 15 seconds (or returns a non-`2xx` status, or the network call fails), Pexx will automatically retry with **exponential backoff**: | Attempt | Wait before next retry | | ------- | ---------------------- | | 1 → 2 | 30 s | | 2 → 3 | 60 s | | 3 → 4 | 2 min | | 4 → 5 | 4 min | | 5 → 6 | 8 min | | 6 → 7 | 16 min | | 7 → 8 | 32 min | * An event will be retried up to **8 times** in total. After that it is permanently marked as failed and will not be retried. * Successful delivery on any attempt stops the retries. > **Ordering is not guaranteed.** Retries and backoff may cause an older event to arrive after a newer one. Use `occurred_at_ms` and the state fields inside the event payload to determine the correct order on your side. *** ## 6. Best Practices * **Always verify the signature.** Treat any request that fails verification as untrusted and respond with `401`. * **Be idempotent.** Deduplicate by `X-Webhook-Event-Id`; the same event may legitimately arrive more than once. * **Respond fast.** Acknowledge with `2xx` immediately; do the heavy work asynchronously. * **Don't trust ordering.** Use `occurred_at_ms` and resource state fields, not arrival order. * **Keep your secret safe.** Store it in your secrets manager, never commit it to source control. Contact Pexx to rotate it if you suspect it has leaked. * **Handle redirects/auth challenges yourself.** Pexx will not follow `3xx` redirects or respond to `401/403` challenges. *** ## 7. Integration Checklist * [ ] Stand up an HTTPS endpoint that returns `2xx` within 15 seconds. * [ ] Send your **Sandbox** endpoint URL to Pexx and securely store the `whsec_...` secret you receive back. * [ ] Verify `X-Webhook-Signature` and `X-Webhook-Timestamp` on every request. * [ ] Deduplicate by `X-Webhook-Event-Id` to ensure idempotency. * [ ] Trigger a test transaction in Sandbox and confirm your endpoint receives and processes the event. * [ ] Repeat the registration in **Production** and validate again.