> For the complete documentation index, see [llms.txt](https://docs.copperx.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.copperx.io/integrate-payments/webhooks-and-events.md).

# Webhooks & Events

#### ✅ What is a Webhook?

A **webhook** is a POST request sent from Copperx to your server’s endpoint whenever a specific event occurs. It carries a JSON payload describing the event and its associated data.

* **Why Use Webhooks?**\
  Instead of continuously calling the API to check for payment status updates, Copperx sends real-time events to your backend. This reduces overhead and ensures your app stays in sync.
* **Typical Flow:**
  1. User completes a payment (e.g., Checkout Session).
  2. Copperx sends a webhook event (`checkout_session.completed`) to your endpoint.
  3. Your backend verifies the webhook and updates the user’s plan or access.

***

#### 🔐 Webhook Security & Signature Verification

For security, Copperx signs every webhook payload using an **HMAC-SHA256 signature**.

* **Headers in the Webhook Request:**

  | Header                | Description                                  |
  | --------------------- | -------------------------------------------- |
  | `x-webhook-signature` | HMAC-SHA256 signature of the raw payload     |
  | `x-webhook-token`     | Your **webhook secret key** (from dashboard) |
* **How to Verify the Signature:**
  1. Retrieve the `x-webhook-signature` header from the request.
  2. Compute your own HMAC-SHA256 hash of the raw request body using your **webhook secret key**.
  3. Compare your hash with the `x-webhook-signature`. If they match, the webhook is valid.

You can find your **webhook secret key** in the Copperx dashboard settings.

***

#### 🔗 Common Webhook Events

Below are the **most commonly used events** in Copperx’s payment flows:

| Event                            | Description                                         |
| -------------------------------- | --------------------------------------------------- |
| `checkout_session.completed`     | User successfully completed the checkout.           |
| `checkout_session.expired`       | Checkout session expired before payment completion. |
| `checkout_session.canceled`      | User canceled the checkout session.                 |
| `invoice.paid`                   | An invoice was paid successfully.                   |
| `invoice.finalized`              | An invoice was finalized (ready to be paid).        |
| `invoice.payment_failed`         | Payment for an invoice failed.                      |
| `customer.subscription.created`  | A new subscription was created.                     |
| `customer.subscription.started`  | Subscription started.                               |
| `customer.subscription.deleted`  | Subscription was canceled/deleted.                  |
| `customer.subscription.past_due` | Subscription payment is past due.                   |
| `customer.subscription.unpaid`   | Subscription payment is unpaid.                     |
| `payment_intent.succeeded`       | PaymentIntent was successfully completed.           |
| `payment_intent.failed`          | PaymentIntent failed.                               |

***

#### 📨 Example Webhook Payload: Checkout Session Completed

When a checkout session is completed, here’s an example of the JSON payload you will receive:

```json
{
  "id": "00b6d1b6-93ce-4d6d-86b4-a7ab8fe14d87",
  "apiVersion": "2023-01-11",
  "created": 1748329965626,
  "object": "checkoutSession",
  "type": "checkout_session.completed",
  "data": {
    "object": {
      "id": "fe6b7b01-1c4b-4b35-a5a5-a23b0a089cc6",
      "createdAt": "2025-05-27T07:12:19.165Z",
      "updatedAt": "2025-05-27T07:12:45.582Z",
      "mode": "payment",
      "paymentMethodTypes": ["wallet"],
      "paymentSetting": {
        "allowedChains": [
          { "chainId": 137 },
          { "chainId": 8453 },
          { "chainId": 42161 }
        ],
        "paymentMethodTypes": ["wallet"],
        "preferredChainId": 137,
        "allowSwap": true
      },
      "currency": "usdc",
      "amountSubtotal": "10000000",
      "amountTotal": "10100000",
      "status": "complete",
      "paymentStatus": "paid",
      "paymentLinkId": "769a890b-5664-49f8-8f68-c9b688bcf60f",
      "url": "https://buy.copperx.tech/payment/checkout-session/fe6b7b01-1c4b-4b35-a5a5-a23b0a089cc6",
      "lineItems": {
        "object": "list",
        "data": [
          {
            "description": null,
            "quantity": 1,
            "price": {
              "id": "ebc48921-0171-4f37-9070-2906d0da2f51",
              "currency": "usdc",
              "productId": "b1d2476f-b5cf-47d7-99ee-3d8123ae9ab5",
              "type": "one_time",
              "unitAmount": "10000000",
              "product": {
                "id": "b1d2476f-b5cf-47d7-99ee-3d8123ae9ab5",
                "name": "Demo test",
                "isActive": true
              },
              "isActive": true
            },
            "amountTotal": "10000000",
            "currency": "usdc"
          }
        ]
      },
      "addresses": [/* ... multiple supported crypto assets and addresses ... */],
      "paymentIntent": {
        "id": "3b7bc615-a04d-49e1-98c4-8612f785484d",
        "amount": "10100000",
        "amountReceived": "10100000",
        "currency": "usdc",
        "status": "requires_payment_method",
        "paymentMethod": {
          "id": "88cbd77a-84f1-422a-8ac6-fa6f498142b1",
          "asset": {
            "id": "13056880-798b-4bd4-a555-c1c71de017fa",
            "name": "USDC.e",
            "chainId": 137,
            "address": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
            "currency": "usdc"
          },
          "type": "wallet",
          "accountAddress": "0xd2b59f3a9575a90a44e5627cda4ed98ecb5e5d20",
          "options": {
            "frontend": {
              "transactionHash": "0x54d164f9807060445614ece882603fd4b2a7858ac1394451cf1f07715a183def"
            },
            "wallet": {
              "assetId": "13056880-798b-4bd4-a555-c1c71de017fa",
              "transactionHash": "0x54d164f9807060445614ece882603fd4b2a7858ac1394451cf1f07715a183def",
              "contractAddress": "0x146db67792092eaa92a51961946b50f89277a975"
            }
          }
        }
      },
      "amountDetails": {
        "amountTotal": "10100000",
        "amountSubtotal": "10000000",
        "amountFee": "100000",
        "currency": "usdc",
        "feePercentage": 1
      },
      "amountNet": "10000000"
    }
  }
}

```

***

#### 💡 How to Use This Event

When you receive this event:\
✅ Verify the **signature** (for security).\
✅ Check the **paymentStatus** (`paid`).\
✅ Activate the user’s plan, send a confirmation email, or update their account.

#### ⚙️ Best Practices for Webhooks

✅ Always verify the signature to avoid spoofed requests.\
✅ Respond quickly to webhook POST requests with a **`200` OK** to acknowledge receipt.\
✅ Retry Behavior

* **Max Attempts:** 10
* **Initial Delay:** 30 seconds
* **Backoff Strategy:** Exponential (delay doubles with each retry)
* **No Delay Cap:** Delay continues to double up to the 10th attempt
* **Total Retry Window:** \~**4 hours 16 minutes** from initial delivery attempt

***

### 📊 Retry Schedule

| Attempt | Delay (seconds) | Delay (minutes) | Cumulative Time |
| ------- | --------------- | --------------- | --------------- |
| 1st     | 30              | 0.5 min         | 0:00:30         |
| 2nd     | 60              | 1 min           | 0:01:30         |
| 3rd     | 120             | 2 min           | 0:03:30         |
| 4th     | 240             | 4 min           | 0:07:30         |
| 5th     | 480             | 8 min           | 0:15:30         |
| 6th     | 960             | 16 min          | 0:31:30         |
| 7th     | 1920            | 32 min          | 1:03:30         |
| 8th     | 3840            | 64 min          | 2:07:30         |
| 9th     | 7680            | 128 min         | 4:15:30         |
| 10th    | 15,360          | 256 min         | 8:31:30\*       |

**Notes:**

* A maximum of **10 retry attempts** will be made.
* If all retries fail, the webhook delivery is marked as **unsuccessful**.

✅ Log webhook events for debugging and auditing.\
✅ Use HTTPS for secure communication.
