Skip to content
Beta · The spec may still change before general availability. The API domain api.hensa.example is a placeholder. See changes →

Webhooks

Hensa sends a JSON POST to your URL whenever something happens. Every webhook is signed – always verify before trusting the data.

  • bank_transaction.createdMoney arrived in a linked account (every transfer, including ones not tied to an order).data BankTransaction
  • payment_request.paidA payment request was paid in full – this is the event to fulfil the order on.data PaymentRequest
  • payment_request.partially_paidThe customer paid less than requested. Do not fulfil yet – ask for the remainder or handle it manually. Once fully paid you get `payment_request.paid`.data PaymentRequest
  • payment_request.expiredExpired unpaid. Release reserved stock or create a new one.data PaymentRequest
  • bank_connection.status_changedA bank connection failed or recovered. Pause bank-transfer checkout if needed.data BankConnectionStatus

Every event shares the same envelope:

{
"id": "evt_01JD7QB2S8E6H4N1P9C3V7K5TQ",
"type": "payment_request.paid",
"createdAt": "2026-11-03T08:03:12Z",
"data": {
"id": "prq_01JD7Q8M4K2X9V3T6B5N0R1W8Y",
"amount": 150000,
"currency": "VND",
"referenceCode": "LK7F3K9",
"status": "paid",
"externalOrderRef": "DH-1024",
"…": "…"
}
}
Field Meaning
id Event id evt_… – use it to de-duplicate
type Event type
createdAt When the event happened
data The object at that moment (see the API reference)

Accompanying header:

Hensa-Signature: t=1762156992,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
  • Return a 2xx within 10 seconds. Anything else (or a timeout) counts as a failure and is retried.
  • Heavy work (emails, courier calls…) → put it on a queue and return 200 right away.
  • No response body is needed.

Failed deliveries are retried on a fixed schedule – 8 attempts in total over about 45 hours:

AttemptWait after previousSince first attempt
1—0
21 min1 min
35 min6 min
430 min36 min
52 h2.6 h
66 h8.6 h
712 h20.6 h
824 h44.6 h

Hensa never silently switches off your webhook:

  • After 3 consecutive failures the endpoint becomes failing and Hensa emails the account owner and technical admins.
  • Only after 7 days of continuous failure is the endpoint paused (disabled) – with advance email.
  • Every event is kept for 30 days – you can redeliver any of them, including events that happened while the endpoint was paused.

The same event can arrive more than once (retries, manual redelivery, flaky networks). Record processed event.ids and skip repeats:

-- One small table; the UNIQUE constraint does the hard part
CREATE TABLE processed_hensa_events (event_id text PRIMARY KEY, processed_at timestamptz DEFAULT now());

Hensa does not guarantee ordering between events. Don’t assume bank_transaction.created always precedes payment_request.paid – always read the status in data (or fetch the latest via the API).

  • Dashboard › Developer › Webhooks › Deliveries → “Redeliver” any event.
  • Or the API: POST /webhook_deliveries/{id}/redeliver.
  • Send a test event: POST /webhook_endpoints/{id}/test or the “Send test event” button – instantly receive a signed sample to check your code.
  • Only public HTTPS is accepted. Private addresses (localhost, 10.x, 192.168.x…) are rejected.
  • A hard-to-guess path segment (e.g. /webhooks/hensa/8f3k…) is an extra layer – never a replacement for signature verification.