Skip to main content
ElasticFunnels can fire an outbound HTTP POST to a URL you control whenever a merchant event happens - a purchase, a refund, a subscription renewal, a chargeback, and so on. The payload is the same regardless of which gateway (Stripe, NMI, PayPal, ClickBank, BuyGoods, Digistore24, Klarna, Shop Pay, Shopify Checkout) actually processed the transaction, so you only have to integrate once. This is intentionally simpler than building a full automation graph in the Automations module. Use postback URLs when you just need to forward a clean, predictable JSON event to your own backend, your data warehouse, an internal CRM, or a third-party partner.
Postback URLs do not replace the Automations module - they are complementary. Automations are great for branching logic, retries with conditions, and connecting many third-party services. Postback URLs are great for “fire-and-forget” event forwarding.

Where to configure

In your brand:
  1. Go to SettingsMerchants → pick a merchant → Edit.
  2. Open the Postback URLs tab.
  3. For each event you care about, set:
    • Postback URL - https://your-app.example.com/webhooks/elasticfunnels
    • Authorization - a token we send as Authorization: Bearer <token>, so you can gate the endpoint at a reverse proxy before it ever reaches your app. Use Generate token for a random one, or paste your own. Leave it blank and we generate one for you when you save - the field is never actually empty, so expect the header on every request.
    • Enabled - must be checked for the URL to fire.
  4. Use the Send test button next to each URL to deliver a sample payload right away.
  5. The Recent deliveries section shows status, HTTP code, attempts, the exact payload that was sent, and lets you retry failed deliveries manually.
The same tab also exposes the Signing secret that we use to sign every outbound request. Reveal it once when you wire up your verifier, and you can rotate it at any time.

Supported events

You can configure a different URL per event, or use the same URL for every event - your handler can branch on the event field and on the X-EF-Event header.

Request format

Every request looks like this:

Verifying the signature

Reject any request whose recomputed signature does not match X-EF-Signature. Always compare with a constant-time function (hash_equals in PHP, crypto.timingSafeEqual in Node, etc.). It is also a good idea to reject requests whose X-EF-Timestamp is more than a few minutes in the past or future.

Response handling

  • Return any 2xx status to acknowledge the delivery. The body is ignored (but stored in the deliveries log for debugging - keep it under a few KB).
  • Any non-2xx response, timeout, or connection error counts as a failure.
  • Failed deliveries are retried automatically with exponential backoff: 1m → 5m → 30m → 2h → 12h (up to 5 attempts). After the final failure the delivery is marked dead and stops retrying. You can re-queue it manually from the Postback URLs tab.
  • Each delivery has a unique X-EF-Delivery-Id. Use it to dedupe on your end.

Standardized payload

All events share the same envelope. Fields that don’t apply to a given event are present but null. The full schema:

Passing your own tracking values

Six fields in tracking are yours to populate - vtid and subid through subid5. Put any of them on the landing URL or on the buy link as a query parameter and we capture, store, and echo the value back on every postback for that order:
  • vtid - “vendor tracking ID”. A single general-purpose slot, independent of gateway and of any affiliate network. Best choice for a third-party tracker or session key that you need reflected back. Keep values under 512 characters: the postback echoes the value in full, but the click record truncates at 512, so a longer value will not match between the two when you reconcile. This matters most for base64/encoded composite keys, which get long quickly.
  • subid .. subid5 - five slots conventionally used for affiliate-network sub-tracking (RedTrack, Voluum, and similar).
All six are also filterable and exportable from the Conversions report, so you can reconcile against the postbacks you received.
Upsell and rebill conversions inherit these values from the original purchase, so a subscription’s renewals carry the same vtid as the sale that created it.

Event-specific notes

  • refund / chargeback - amounts.total is negative for the refunded portion, and order.original_conversion_code references the original purchase row.
  • subscription_renewal_failed - order.subscription.retry_count and order.subscription.next_charge_at reflect the dunning schedule, and meta.payment_status will typically be failed with a populated order.subscription.decline_reason.
  • subscription_cancel - order.subscription.status is cancelled and order.subscription.cancel_reason is filled when available.
  • shipped - fires the first time a tracking number is set on an order. shipping.tracking_number, shipping.tracking_url, and shipping.carrier are populated. We dedupe per order so you only get one shipped event.
  • abandon - order.status is abandoned and customer may be partially populated depending on how far the visitor got in checkout.
  • test - Postbacks fired from the Send test button or originating from a sandbox merchant set test: true. Use it to gate side effects in your handler.

Example: minimal Express.js handler

Operational details

  • Deliveries are processed asynchronously so they do not block the gateway request that triggered them.
  • Failed deliveries are retried automatically after the configured backoff window has elapsed.
  • The Postback URLs tab shows recent deliveries, including the URL, payload, headers, response body, attempts, and final status.
  • The signing secret is stored encrypted at rest. Rotating it invalidates all previous signatures immediately - only do it if you can update your verifier in lockstep.