Skip to main content
Kyren Pay sends webhook notifications for the following event types.

Event Types

EventDescriptionTrigger
order.paidA payment has been confirmedCustomer completes checkout
order.refundedA refund has been applied to an orderA refund succeeds
order.closedAn order has been closed without successful paymentPayment 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:
{
  "id": "evt_abc123",
  "type": "order.paid",
  "created_at": 1736932500000,
  "data": {
    // Event-specific data
  }
}
FieldTypeDescription
idstringUnique event identifier (use for deduplication)
typestringThe event type
created_atintegerUnix timestamp in milliseconds
dataobjectEvent-specific payload

order.paid

Sent when a customer successfully completes a payment.
{
  "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" }
  }
}
FieldTypeDescription
order_idstringThe order ID
product_idstringThe product that was purchased
customer_emailstringCustomer’s email address
amountstringTotal payment amount
currencystringThree-letter currency code
net_amountstringAmount after fees
paid_atintegerWhen the payment was confirmed (Unix timestamp in milliseconds)
metadataobject | nullThe metadata you passed when creating the checkout session
This is the most important event. Use it to fulfill orders — for example, adding credits to a user’s account.

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 for the dashboard refund request flow and refund status definitions.
{
  "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" }
  }
}
FieldTypeDescription
order_idstringThe order ID
refund_idstringThe refund ID
refund_statusstringOrder refund status, such as PARTIAL or FULL
amountstringAmount refunded by this event
currencystringThree-letter currency code
refunded_amountstringTotal refunded amount on the order after this refund
original_amountstringOriginal order amount
reasonstring | nullRefund reason
metadataobject | nullThe metadata you passed when creating the checkout session

order.closed

Sent when an order is closed without successful payment.
{
  "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" }
  }
}
FieldTypeDescription
order_idstringThe order ID
product_idstringThe product that was being purchased
customer_emailstringCustomer’s email address
amountstringTotal order amount
currencystringThree-letter currency code
closed_atintegerWhen the order was closed (Unix timestamp in milliseconds)
close_reasonstringClosure reason, such as payment_canceled, payment_failed, payment_timeout, authorization_released, or gateway_closed
compatobjectCompatibility fields for legacy processors
metadataobject | nullThe metadata you passed when creating the checkout session

Handling Events

Here’s how to handle webhook events in your endpoint:
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');
});