Skip to main content
An outbound webhook is an endpoint you implement: this page is the contract of the request Encore sends when one of your users’ offer completions is verified, the response Encore expects back, and the delivery semantics.
For wiring it up (dashboard setup, a runnable signature-verification handler, idempotency, and reconciliation), see the Receive Completion Events guide. For the inbound direction (you forward your own billing’s subscription events into Encore), see Webhook Ingestion: an unrelated flow with different headers and signing.

The request Encore sends

POSTed to the URL you configure at the moment a transaction is marked verified (driven by the advertiser’s conversion postback, not by the user tapping or claiming). It is the only event that credits a user. If you turn on payout updates, Encore can also send offer_payout_booked to the same URL, which never credits one.

Headers

Body

That’s the whole body. The body is exactly the top-level fields in the table, with no app id, creative id, or event id. Only reward is an object; every other field is a plain value. actionId appears only when it has a value, so a handler must not require it. A handler written against a nested req.body.data won’t work; read the fields off the top level. The usr_... shape above is only a placeholder from the test-delivery sample; Encore mints no user ids and adds no prefix.

Signature

The signature base string is the timestamp header and the raw request body, joined by a single dot:
HMAC-SHA256 that string with your whsec_... signing secret and hex-encode it. The result must equal X-Webhook-Signature. Verify against the exact bytes Encore sent, not a re-serialization; a runnable handler is in the guide.

The response Encore expects

Return any 2xx within 5 seconds. Anything else fails that attempt and Encore retries it.

Delivery is at-least-once

A conversion may arrive more than once, including a delivery your system already processed. A timeout or a missing response tells us nothing about whether your side completed the work, so we retry. Two things protect you, and they cover opposite failures:
  1. Required: credit the user once per transactionId. Take it from the signed request body. Every retry carries the same value, and so does every paid action of a multi-step offer. To record each payment rather than each credit, key on transactionId plus actionId, or on transactionId alone when actionId is absent; retries of one delivery repeat both. Do not key on X-Webhook-Delivery-Id or X-Webhook-Attempt. Both sit outside the signature base string and are not authenticated.
  2. Recommended: return 2xx only once everything the delivery records is durably committed, not on receipt. That is the user’s credit and, if you keep one, the payment record for its actionId. On a later paid action the credit is already a no-op, so the payment record is the only work left to commit. If you acknowledge first and write asynchronously, a failure in that async step is invisible to us: we will not retry, because you already returned 2xx, and the credit or the payment is silently lost.

URL requirements