Webhook'lar

Webhook'lar

Add an endpoint under Developers → Webhooks and pick the events it should receive. Every delivery is an HTTPS POST with a JSON body, signed with the endpoint's secret. Use the dashboard's "Send test event" button to try your receiver before going live.

Where an endpoint may point

The address has to be one the open internet can reach. An endpoint on localhost, on a private network, or on one of the addresses a cloud provider answers its own metadata on is refused when you save it, and again before each delivery, because it is this platform's server that would be making the request rather than yours. Redirects are not followed for the same reason.

Developing locally? Put a tunnel in front of your machine and give us the tunnel's address, or use the delivery log to replay a delivery once your receiver is reachable.

Live and test mode

An endpoint belongs to the mode you were in when you added it, and stays in it. A payment taken in test mode is only ever announced to test endpoints, and a live payment only to live ones, so trying something out can never create a real order. Switch the dashboard to the other mode to see and add that mode's endpoints.

Every payload also carries a mode of live or test, so your receiver can tell real money from a rehearsal without looking anything up.

Event types

  • charge.created A charge was opened and its rate locked.
  • charge.payment_detected A transfer was seen on-chain and is gathering confirmations.
  • charge.paid The payment is confirmed. Fulfil the order.
  • charge.underpaid A confirmed payment fell short of your tolerance.
  • charge.late_payment Funds arrived after the charge expired or was canceled.
  • charge.refunded You recorded that funds were sent back to the customer.
  • charge.expired The rate lock ran out before the customer paid.
  • charge.canceled The charge was canceled from the dashboard or the API.
  • invoice.created An invoice was issued.
  • invoice.sent The invoice, or a reminder of it, was emailed to the customer.
  • invoice.viewed The customer opened the invoice for the first time.
  • invoice.paid One of the invoice's charges was paid, which settles it.
  • invoice.voided The invoice was withdrawn and can no longer be paid.

Charge events carry the charge under data.charge; invoice events carry the invoice under data.invoice (code, number, status, amount, currency, customer, hosted_url, charges). A paid invoice produces both charge.paid and invoice.paid: fulfil on one of them, not both.

Each payload carries an event_id. The same events are kept in the timeline of the charge or invoice and can be read back with GET /v1/events (filter with ?type=, ?charge= and ?invoice=) or GET /v1/events/{id}, which is handy for catching up after your endpoint was down.

Payload and headers

POST /your/endpoint
Content-Type: application/json
User-Agent: VeliraPay-Webhooks/1.0
X-VeliraPay-Event: charge.paid
X-VeliraPay-Delivery: 9d2e0b8a-…-5f3a
X-VeliraPay-Signature: t=1758067200,v1=4f1c…ab09

{
  "id": "9d2e0b8a-…-5f3a",
  "event_id": "9d2e0b89-…-77c1",
  "event": "charge.paid",
  "mode": "live",
  "created_at": "2026-09-17T00:05:12.000000Z",
  "data": {
    "charge": {
      "code": "2t4bcuqkpkoy",
      "status": "paid",
      "fiat_amount": "150.00",
      "fiat_currency": "USD",
      "asset": "BTC",
      "asset_amount": "0.002500000000000000",
      "received_amount": "0.002500000000000000",
      "customer_email": "[email protected]",
      "description": "Invoice #1042",
      "metadata": {"order_id": "1042"},
      "payment_link": null,
      "paid_at": "2026-09-17T00:05:10.000000Z",
      …
    }
  }
}

Next