Webhooks

Overview & Delivery

Webhook delivery model, registration, event envelope, and the 6-attempt retry schedule.

Webhooks deliver event notifications to your server via HTTP POST. Use them for server-side order fulfillment instead of polling for payment status.

Registering endpoints

Webhook endpoints are configured in the merchant dashboard — there is no public API for webhook CRUD. Go to Settings → Webhooks to add endpoints and subscribe to events.

Open dashboard webhooks

Go to Settings → Webhooks in the halfin dashboard.

Add your endpoint URL

Paste your HTTPS endpoint that will receive events.

Subscribe to events

Select the event types your integration needs. Use invoice.paid for payment confirmation; balance.credited when fulfillment depends on spendable balance.

Event envelope

Every webhook POST body follows this structure:

{
  "event_id": "00000000-0000-0000-0000-000000000001",
  "event": "invoice.paid",
  "created_at": "2024-01-01T12:00:00Z",
  "data": { ... }
}

New canonical deliveries include a stable event_id UUID for merchant-side idempotency. It is optional in invoice, late-payment, payout, and refund schemas only because retries of legacy stored payloads may omit it. balance.credited always includes event_id.

Headers

Every webhook request includes:

X-Halfin-Signature: t=1735689900,v1=5d41402abc4b2a76b9719d...
X-Halfin-Event: invoice.paid
Content-Type: application/json

Always verify the X-Halfin-Signature header before processing the event. See Signature Verification.

Delivery semantics

  • Method: POST with a JSON body
  • At-least-once: the same event may be delivered more than once. De-duplicate by event_id when present. For a legacy payload without it, fall back to the event type, created_at, and the event-specific resource ID in data.
  • Success: any 2xx response is treated as successful delivery
  • Timeout: your endpoint should respond within 15 seconds

Retry schedule

On non-2xx responses, halfin makes up to 6 total delivery attempts (the initial attempt plus 5 retries) with fixed backoff intervals:

AttemptDelay before retry
1 (initial)
230 seconds
32 minutes
410 minutes
51 hour
66 hours

After the 6th failed attempt the delivery is marked failed. No further retries occur.

Webhook delivery is at-least-once. Prefer the stable event_id UUID as the idempotency key. Only legacy stored payloads may require the fallback key described above.

Event types

See Event Types for all 13 subscribable events with payload field descriptions.