Skip to content

Webhooks

Overview

Webhooks push event notifications to your server when asynchronous state changes occur. Rather than polling the API to check payment status, configure a webhook endpoint and let ElasticPay notify you.

Webhook endpoints are configured by ElasticPay — there is no self-serve webhook screen in the dashboard. To add or change one, contact support with:

  • the endpoint URL (publicly reachable, HTTPS, not localhost)
  • the event types you want to receive
  • whether it is for your sandbox or live account

Sandbox and live accounts have separate subscriptions, each with its own signing secret. Sandbox events are only delivered to sandbox endpoints, and live events only to live endpoints. Secret rotation also goes through support.

Once events are flowing, the delivery status of an individual event is shown in the Events panel on the payment intent, payment plan, or customer detail page.

Event envelope

Every webhook event has the same structure:

{
"version": "1",
"event_id": "evt_0abc123def456ghi789jkl",
"event_type": "payment_intent.succeeded",
"biller_id": "biller_abc123",
"livemode": false,
"emitted_at": "2025-01-15T10:05:00Z",
"data": {
"id": "pi_0abc123def456ghi789jkl012mn",
"status": "succeeded",
"amount": 5000,
"currency": "AUD"
}
}
FieldDescription
versionEnvelope schema version
event_idUnique event identifier — use for deduplication
event_typeEvent name (see table below)
biller_idAccount that generated the event
livemodetrue for live events, false for sandbox
emitted_atISO 8601 timestamp
dataThe resource at the time of the event

Event types

EventDescription
payment_intent.succeededPayment completed successfully
payment_intent.failedPayment declined or failed
payment_intent.processingSubmitted to PSP, awaiting result
payment_intent.canceledPayment intent was canceled
payment_intent.refundedFull refund succeeded
payment_intent.partially_refundedPartial refund succeeded
payment_intent.refund_failedRefund attempt failed
setup_intent.succeededPayment method saved successfully
setup_intent.failedSetup intent failed
test_clock.createdSandbox test clock created
test_clock.advancingTest clock advance accepted (includes target_time)
test_clock.readyTest clock advance completed (includes effects_manifest)
test_clock.deletedTest clock deleted, manually or at expiry

Verifying signatures

Every webhook request includes an X-Webhook-Signature header. Verify it to confirm the request came from ElasticPay.

The signature is computed as v1=HMAC-SHA256(secret, "<timestamp>.<raw_body>") where <timestamp> is the value of the X-Webhook-Timestamp header.

HeaderDescription
X-Webhook-Signaturev1=<hex HMAC-SHA256>
X-Webhook-TimestampISO 8601 timestamp of when the event was signed
X-Webhook-Key-IdIdentifies which signing key was used

Reject requests where the timestamp is more than 5 minutes from your server clock to prevent replay attacks.

Terminal window
# Webhook requests include these headers:
#
# X-Webhook-Signature v1=<hex HMAC-SHA256>
# X-Webhook-Timestamp ISO 8601 timestamp
# X-Webhook-Key-Id signing key identifier
#
# The signature is computed as:
# HMAC-SHA256(secret, "<timestamp>.<raw_body>")
#
# Reject requests where the timestamp is more than 5 minutes
# from your server clock to prevent replay attacks.

Best practices

  • Respond 200 immediately. Process events asynchronously — do not make slow API calls inside the handler.
  • Make handlers idempotent. Events may be delivered more than once. Use event_id to deduplicate.
  • Verify livemode. Match the flag to your environment to avoid processing test events in production.
  • Expect retries. If your endpoint returns a non-2xx status, ElasticPay retries delivery with exponential backoff.