> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pcxpay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive real-time payment and platform events at your HTTPS endpoint

Webhooks let PCX push event notifications to your server the moment something changes — a payment completes, a virtual account receives a deposit, or a KYB check resolves. This eliminates the need to poll for status updates.

***

## Register your endpoint

Add your webhook URL to your organization using the Public service:

```bash theme={null}
curl -X POST https://prod-api.pcxpay.com/v1/public/webhooks \
  -H "X-Api-Key: your_api_key" \
  -H "Authorization: NONE" \
  -H "Content-Type: application/json" \
  -d '{ "endpoint_url": "https://yourapp.com/webhooks/pcx" }'
```

Your endpoint must be reachable over HTTPS. PCX starts delivering events immediately after registration. Multiple endpoints can be registered per organization.

***

## Event payload structure

Every event follows the same envelope:

```json theme={null}
{
  "event_id": "evt_xxxx",
  "event_type": "payment.completed",
  "org_id": "org_xxxx",
  "created_at": "2025-03-16T10:00:00Z",
  "data": { ... }
}
```

Use `event_id` to deduplicate — in rare retry scenarios, PCX may deliver the same event more than once. Process each `event_id` exactly once.

***

## Verify the signature

PCX signs every event with HMAC-SHA256 and sends the result in the `X-PCX-Signature` header. **Always verify this before processing the payload.**

```python theme={null}
import hmac
import hashlib

def is_valid(payload: bytes, signature_header: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature_header)
```

<Warning>
  Reject any webhook event where the signature does not match. Never process unverified payloads — they may be spoofed.
</Warning>

***

## Acknowledge receipt

Return a `2xx` HTTP status within **10 seconds**. PCX does not inspect the response body — any `2xx` counts as a successful acknowledgement.

If your processing logic takes longer than 10 seconds, acknowledge immediately and handle the event asynchronously:

```python theme={null}
@app.post("/webhooks/pcx")
async def handle_webhook(request):
    body = await request.body()
    is_valid(body, request.headers["X-PCX-Signature"], WEBHOOK_SECRET)
    background_tasks.add_task(process_event, body)
    return {"ok": True}  # Return 200 immediately
```

***

## Retry schedule

Failed deliveries — non-2xx responses or connection timeouts — are retried automatically with exponential backoff:

| Attempt | Delay          |
| ------- | -------------- |
| 1       | Immediate      |
| 2       | 1 minute       |
| 3       | 5 minutes      |
| 4       | 30 minutes     |
| 5+      | Up to 24 hours |

Events that exhaust all retries are moved to a Dead Letter Queue (DLQ). Contact support to replay DLQ events manually.

***

## Key event types

| Event                        | When it fires                                 |
| ---------------------------- | --------------------------------------------- |
| `payment.completed`          | Payment successfully delivered to beneficiary |
| `payment.failed`             | Payment rejected or failed at the provider    |
| `payment.processing`         | Payment submitted to the provider             |
| `virtual_account.deposit`    | Funds credited to a virtual account           |
| `virtual_account.withdrawal` | Funds debited from a virtual account          |
| `kyb.approved`               | Organization KYB verification passed          |
| `kyb.rejected`               | Organization KYB verification failed          |
| `transaction.updated`        | Transaction status changed                    |

See [Webhooks](/concepts/webhooks) in Concepts for the full event reference, including KYC events, virtual account freeze events, and the complete data payload schema per event type.

***

## Test your integration

Trigger a manual notification to all registered endpoints for your organization:

```bash theme={null}
curl -X POST https://prod-api.pcxpay.com/v1/public/webhooks/notify \
  -H "X-Api-Key: your_api_key" \
  -H "Authorization: NONE" \
  -H "Content-Type: application/json" \
  -d '{
    "org_id": "org_xxxx",
    "transaction_data": {
      "transaction_id": "txn_xxx",
      "status": "completed",
      "amount": 500.00,
      "currency": "USD",
      "updated_at": "2024-01-15T10:30:00Z"
    }
  }'
```

Use this during development to verify your endpoint receives and processes events correctly before going live.
