# 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.copperx.io/integrate-payments/webhooks-and-events.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
