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

# Webhook Events

> All webhook event types and their payloads

Kyren Pay sends webhook notifications for the following event types.

## Event Types

| Event            | Description                                         | Trigger                                                                                           |
| ---------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `order.paid`     | A payment has been confirmed                        | Customer completes checkout                                                                       |
| `order.refunded` | A refund has been applied to an order               | A refund succeeds                                                                                 |
| `order.closed`   | An order has been closed without successful payment | Payment is canceled, fails, times out, authorization is released, or the gateway closes the order |

## Event Object Structure

All webhook events share the same top-level structure:

```json theme={null}
{
  "id": "evt_abc123",
  "type": "order.paid",
  "created_at": 1736932500000,
  "data": {
    // Event-specific data
  }
}
```

| Field        | Type    | Description                                     |
| ------------ | ------- | ----------------------------------------------- |
| `id`         | string  | Unique event identifier (use for deduplication) |
| `type`       | string  | The event type                                  |
| `created_at` | integer | Unix timestamp in milliseconds                  |
| `data`       | object  | Event-specific payload                          |

## order.paid

Sent when a customer successfully completes a payment.

```json theme={null}
{
  "id": "evt_abc123",
  "type": "order.paid",
  "created_at": 1736932500000,
  "data": {
    "order_id": "order_def456",
    "product_id": "prod_abc123",
    "customer_email": "customer@example.com",
    "amount": "9.99",
    "currency": "USD",
    "net_amount": "9.29",
    "paid_at": 1736932500000,
    "metadata": { "user_id": "u_123" }
  }
}
```

| Field            | Type           | Description                                                     |
| ---------------- | -------------- | --------------------------------------------------------------- |
| `order_id`       | string         | The order ID                                                    |
| `product_id`     | string         | The product that was purchased                                  |
| `customer_email` | string         | Customer's email address                                        |
| `amount`         | string         | Total payment amount                                            |
| `currency`       | string         | Three-letter currency code                                      |
| `net_amount`     | string         | Amount after fees                                               |
| `paid_at`        | integer        | When the payment was confirmed (Unix timestamp in milliseconds) |
| `metadata`       | object \| null | The metadata you passed when creating the checkout session      |

<Tip>
  This is the most important event. Use it to fulfill orders — for example, adding credits to a user's account.
</Tip>

## order.refunded

Sent when a refund has been applied to an order. Use this event to update entitlement, balance, or reconciliation records in your system.

See [Order refunds](/dashboard/order-refunds) for the dashboard refund request flow and refund status definitions.

```json theme={null}
{
  "id": "evt_refund123",
  "type": "order.refunded",
  "created_at": 1736932600000,
  "data": {
    "order_id": "order_def456",
    "refund_id": "refund_abc123",
    "refund_status": "PARTIAL",
    "amount": "2.50",
    "currency": "USD",
    "refunded_amount": "2.50",
    "original_amount": "9.99",
    "reason": "customer_request",
    "metadata": { "user_id": "u_123" }
  }
}
```

| Field             | Type           | Description                                                |
| ----------------- | -------------- | ---------------------------------------------------------- |
| `order_id`        | string         | The order ID                                               |
| `refund_id`       | string         | The refund ID                                              |
| `refund_status`   | string         | Order refund status, such as `PARTIAL` or `FULL`           |
| `amount`          | string         | Amount refunded by this event                              |
| `currency`        | string         | Three-letter currency code                                 |
| `refunded_amount` | string         | Total refunded amount on the order after this refund       |
| `original_amount` | string         | Original order amount                                      |
| `reason`          | string \| null | Refund reason                                              |
| `metadata`        | object \| null | The metadata you passed when creating the checkout session |

## order.closed

Sent when an order is closed without successful payment.

```json theme={null}
{
  "id": "evt_closed123",
  "type": "order.closed",
  "created_at": 1736932700000,
  "data": {
    "order_id": "order_def456",
    "product_id": "prod_abc123",
    "customer_email": "customer@example.com",
    "amount": "9.99",
    "currency": "USD",
    "closed_at": 1736932700000,
    "close_reason": "payment_timeout",
    "compat": { "epay_trade_status": "TRADE_CLOSED" },
    "metadata": { "user_id": "u_123" }
  }
}
```

| Field            | Type           | Description                                                                                                                    |
| ---------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `order_id`       | string         | The order ID                                                                                                                   |
| `product_id`     | string         | The product that was being purchased                                                                                           |
| `customer_email` | string         | Customer's email address                                                                                                       |
| `amount`         | string         | Total order amount                                                                                                             |
| `currency`       | string         | Three-letter currency code                                                                                                     |
| `closed_at`      | integer        | When the order was closed (Unix timestamp in milliseconds)                                                                     |
| `close_reason`   | string         | Closure reason, such as `payment_canceled`, `payment_failed`, `payment_timeout`, `authorization_released`, or `gateway_closed` |
| `compat`         | object         | Compatibility fields for legacy processors                                                                                     |
| `metadata`       | object \| null | The metadata you passed when creating the checkout session                                                                     |

## Handling Events

Here's how to handle webhook events in your endpoint:

```javascript theme={null}
app.post('/webhooks/kyren', (req, res) => {
  // ... verify signature first ...

  const event = JSON.parse(req.body.toString());

  switch (event.type) {
    case 'order.paid':
      handleOrderPaid(event.data);
      break;
    case 'order.refunded':
      handleOrderRefunded(event.data);
      break;
    case 'order.closed':
      handleOrderClosed(event.data);
      break;
    default:
      console.log(`Unhandled event: ${event.type}`);
  }

  res.status(200).send('OK');
});
```
