Billium
Webhooks

Event catalog

All webhook events emitted by Billium, with full payload shapes.

Billium delivers 15 event types across two categories: invoice events, which track the high-level lifecycle of a charge, and payment events, which track the on-chain transactions that settle that charge.

Each event is classified as durable or best-effort — this determines the delivery guarantee. Durable events are retried until delivered (at-least-once); best-effort events are dispatched once and may be dropped if Billium experiences a failure in-flight. See Delivery guarantees for the full model.

As a rule of thumb: drive your business logic off durable events (invoice.paid, invoice.expired, etc.) and treat best-effort events (invoice.updated, payment.created, payment.updated) as UI hints only.

Invoice events

invoice.created

Fired when a new invoice is created.

{
  "event": "invoice.created",
  "id": "evt_...",
  "data": {
    "invoiceId": "inv_...",
    "merchantId": "mer_...",
    "amount": 49.99,
    "cryptocurrency": "USDT",
    "network": "ethereum",
    "status": "AWAITING_PAYMENT",
    "paymentAddress": "0xAbCdEf...",
    "expectedCryptoAmount": "49.952341",
    "expiresAt": "2025-03-15T05:00:00.000Z",
    "metadata": {}
  },
  "timestamp": "2025-03-15T04:00:00.000Z"
}

invoice.updated

Fired whenever the invoice status changes. Includes the new status.

{
  "event": "invoice.updated",
  "id": "evt_...",
  "data": {
    "invoiceId": "inv_...",
    "merchantId": "mer_...",
    "status": "PENDING_CONFIRMATION",
    "metadata": {}
  },
  "timestamp": "2025-03-15T04:08:00.000Z"
}

invoice.paid

Fired when the invoice is fully confirmed on-chain and the received amount matches exactly.

{
  "event": "invoice.paid",
  "id": "evt_...",
  "data": {
    "invoiceId": "inv_...",
    "merchantId": "mer_...",
    "paymentId": "pay_...",
    "amount": 49.99,
    "cryptocurrency": "USDT",
    "network": "ethereum",
    "receivedCryptoAmount": "49.952341",
    "txHash": "0xabcdef1234...",
    "metadata": {}
  },
  "timestamp": "2025-03-15T04:12:00.000Z"
}

This is the event you care about most — use it to fulfill orders, unlock content, or update your database.


invoice.underpaid

Fired when the transaction is confirmed but the received amount is less than expected.

{
  "event": "invoice.underpaid",
  "id": "evt_...",
  "data": {
    "invoiceId": "inv_...",
    "merchantId": "mer_...",
    "paymentId": "pay_...",
    "expectedCryptoAmount": "49.952341",
    "receivedCryptoAmount": "40.000000",
    "txHash": "0xabcdef1234...",
    "metadata": {}
  },
  "timestamp": "2025-03-15T04:12:00.000Z"
}

invoice.overpaid

Fired when the transaction is confirmed but the received amount is more than expected.

{
  "event": "invoice.overpaid",
  "id": "evt_...",
  "data": {
    "invoiceId": "inv_...",
    "merchantId": "mer_...",
    "paymentId": "pay_...",
    "expectedCryptoAmount": "49.952341",
    "receivedCryptoAmount": "75.000000",
    "txHash": "0xabcdef1234...",
    "metadata": {}
  },
  "timestamp": "2025-03-15T04:12:00.000Z"
}

invoice.expired

Fired when an invoice reaches its expiresAt time without a confirmed payment.

{
  "event": "invoice.expired",
  "id": "evt_...",
  "data": {
    "invoiceId": "inv_...",
    "merchantId": "mer_...",
    "metadata": {}
  },
  "timestamp": "2026-04-12T05:00:00.000Z"
}

invoice.cancelled

Fired when a merchant cancels an invoice via POST /invoices/{id}/cancel or the dashboard. Only invoices in non-terminal states can be cancelled.

{
  "event": "invoice.cancelled",
  "id": "evt_...",
  "data": {
    "invoiceId": "inv_...",
    "merchantId": "mer_...",
    "metadata": {}
  },
  "timestamp": "2026-04-12T04:30:00.000Z"
}

Payment events

payment.detected

Fired as soon as a matching transaction is seen on-chain, before it accumulates enough confirmations. Useful for showing the customer a "payment detected, waiting for confirmation" message.

{
  "event": "payment.detected",
  "id": "evt_...",
  "data": {
    "paymentId": "pay_...",
    "invoiceId": "inv_...",
    "merchantId": "mer_...",
    "txHash": "0xabcdef1234...",
    "receivedCryptoAmount": "49.952341",
    "confirmations": 1,
    "requiredConfirmations": 12
  },
  "timestamp": "2025-03-15T04:08:00.000Z"
}

payment.confirmed

Fired each time the confirmation count increases, until the required threshold is reached.

{
  "event": "payment.confirmed",
  "id": "evt_...",
  "data": {
    "paymentId": "pay_...",
    "invoiceId": "inv_...",
    "merchantId": "mer_...",
    "txHash": "0xabcdef1234...",
    "confirmations": 6,
    "requiredConfirmations": 12
  },
  "timestamp": "2026-04-12T04:10:00.000Z"
}

payment.paid

Fired when a single payment settles with the exact expected amount. This is the per-payment counterpart of invoice.paid — subscribe to it if you need transaction-level granularity (e.g. an invoice that's paid by multiple transactions). Most integrations only need invoice.paid.

{
  "event": "payment.paid",
  "id": "evt_...",
  "data": {
    "paymentId": "pay_...",
    "invoiceId": "inv_...",
    "merchantId": "mer_...",
    "txHash": "0xabcdef1234...",
    "amount": "49.952341",
    "currency": "USDT"
  },
  "timestamp": "2026-04-12T04:12:00.000Z"
}

payment.underpaid

Fired when a single payment confirms with less than the expected amount. The invoice may still reach invoice.paid later if further payments arrive — always check the invoice status before acting on a payment.underpaid.


payment.overpaid

Fired when a single payment confirms with more than the expected amount. As with payment.underpaid, this is a per-payment signal — use invoice.overpaid for the aggregate view.


payment.expired

Fired when a detected payment never reaches the required confirmation count before the invoice's expiry window closes.

{
  "event": "payment.expired",
  "id": "evt_...",
  "data": {
    "paymentId": "pay_...",
    "invoiceId": "inv_...",
    "merchantId": "mer_..."
  },
  "timestamp": "2026-04-12T05:00:00.000Z"
}

payment.created

Fired when a new payment row is inserted (customer initiated the checkout). Best-effort — use it for UI hints, not for critical logic.


payment.updated

Fired on low-level payment field changes — received amount, confirmation count, expiry, tx hash. Fires frequently and is best-effort. Most integrations should prefer payment.confirmed (durable) for confirmation updates.


Event summary

EventTriggerDeliveryTerminal?
invoice.createdInvoice created via APIBest-effortNo
invoice.updatedAny status changeBest-effortNo
invoice.paidConfirmed, exact amountDurableYes
invoice.underpaidConfirmed, short amountDurableYes
invoice.overpaidConfirmed, excess amountDurableYes
invoice.expiredDeadline passed without paymentDurableYes
invoice.cancelledMerchant cancelled via API or dashboardDurableYes
payment.createdNew payment row insertedBest-effortNo
payment.updatedPayment row updatedBest-effortNo
payment.detectedTx seen on-chainDurableNo
payment.confirmedEach new confirmationDurableNo
payment.paidPayment reached terminal paid stateDurableYes
payment.underpaidPayment confirmed under expectedDurableYes
payment.overpaidPayment confirmed over expectedDurableYes
payment.expiredPayment abandoned before confirmingDurableYes

Subscribing to all events in a category

When creating a webhook, pass the wildcard event invoice.* or payment.* to subscribe to every event in that category without listing them individually.

On this page