Webhooks

Webhooks overview

Webhooks are how every async state change reaches your system. Treat them as the source of truth, never poll for status if you can avoid it.

Register an endpoint

POST/v1/webhooks/endpoints
{
  "url": "https://example.com/webhooks/xpend",
  "event_types": [
    "payment_intent.deposit_confirmed",
    "payment_intent.completed",
    "payout.completed",
    "payout.failed"
  ]
}
{
  "endpoint_id": "whe_01HXYZ...",
  "url": "https://example.com/webhooks/xpend",
  "environment": "test",
  "subscribed_event_types": [
    "payment_intent.deposit_confirmed",
    "payment_intent.completed",
    "payout.completed",
    "payout.failed"
  ],
  "signing_secret": "whsec_8a32f...",
  "signing_secret_last4": "2f9c"
}

signing_secret is shown once

Store it in your secret manager immediately. Lost secrets must be rotated via POST /v1/webhooks/endpoints/{id}/rotate-secret.

Event shape

{
  "id": "evt_01HXYZ...",
  "type": "payment_intent.completed",
  "api_version": "2026-04-01",
  "created_at": "2026-03-19T16:32:11Z",
  "merchant_id": "merchant_xyz",
  "environment": "test",
  "data": {
    "payment_intent": {
      "payment_intent_id": "pi_01JXYZ",
      "customer_id": "cust_9f2a1b",
      "metadata": {
        "order_id": "ORD-001"
      },
      "settled_amount": "10500000",
      "currency": "USDC",
      "chain": "ethereum",
      "credited_at": "2026-04-01T12:10:00.000Z",
      "fees": null
    }
  }
}

List supported types via GET /v1/webhooks/endpoints/supported-event-types.

Payment intent events

payment_intent.deposit_confirmed fires only when cumulative confirmed inbound passes funding policy and platform economic-minimum gates (same bar as routing). For standard intents, sub-threshold deposits may be observed on-chain but held until more funds arrive. For invoice_payment intents, routing waits for the full quoted amount while the checkout window is open; after expiry, partial confirmed inbound can proceed to settlement.

Use payment_intent.completed for fulfillment. On invoice intents, this still means funds were credited or settlement finished — not merely that the checkout timer expired.

Supported event types

  • payment_intent.created
  • payment_intent.deposit_confirmed
  • payment_intent.completed
  • payment_intent.credited is deprecated; prefer payment_intent.completed.
  • payout.created, payout.processing, payout.completed, and payout.failed.

Managing endpoints

GET/v1/webhooks/endpoints
PUT/v1/webhooks/endpoints/{endpointId}
POST/v1/webhooks/endpoints/{endpointId}/disable
POST/v1/webhooks/endpoints/{endpointId}/revoke
POST/v1/webhooks/endpoints/{endpointId}/rotate-secret

Inspecting deliveries

GET/v1/webhooks/deliveries
GET/v1/webhooks/deliveries/{deliveryId}
  • Each delivery records request/response, attempt count, and next retry time.
  • Use this to debug 4xx/5xx responses your endpoint returned.