POST an offer_completed event to a URL you own. Use it when your server needs to react to a completion: grant a reward, credit a balance, kick off an email. You host one HTTPS endpoint; nothing to poll.
This is the final stage of the shared flow, and it fires on verification (the advertiser’s conversion postback), not on the user tapping or claiming; a claim that never converts produces no webhook. The payload contract (headers, body fields, signature spec, delivery guarantees, URL requirements) is the offer_completed webhook reference; this guide is the wiring.
Set it up
Configuration lives in the dashboard under Settings for the app you want events for; there is no publishable-key API for webhook config.1
Enter your webhook URL
Paste the HTTPS URL of your endpoint (for example
https://your-server.com/webhooks/encore) and save. One webhook per app: to point somewhere else, edit the existing URL. It must be HTTPS, publicly resolvable, and redirect-free (URL requirements).2
Copy your signing secret
Creating the webhook generates a
whsec_... signing secret. Store it as a server-side secret (an environment variable, never in client code). Rotating it invalidates the old one immediately: update your server first, or you’ll reject deliveries in the gap.3
Send a test delivery
Send Test POSTs a sample event to your live URL with a real signature from your current secret, exercising your verification path end to end. The sample’s
transactionId and userId are not real records, so make sure your handler tolerates an unknown user gracefully instead of throwing.4
Enable or pause
The status toggle turns delivery on and off. Because there are no retries, events that occur while paused are lost, not queued.
Verify the signature
Never trust an unverified delivery; the signature proves the request came from Encore. Recompute HMAC-SHA256 over<X-Webhook-Timestamp>.<raw_body> with your whsec_... secret and compare hex digests (signature spec).
offer_completed webhook reference.
Design for the guarantees
The guarantees are intentionally thin (delivered once, 5-second timeout, no retries, no ordering, no event id; full table). Three consequences worth designing for:- Return
2xxfast, work asynchronously. Acknowledge as soon as the signature checks out, then hand the work to a queue. Doing database writes or third-party calls before responding is the most common way publishers hit the timeout and lose events they had already received. - Handle duplicates idempotently. Key on
transactionId: record processed ids and make a second delivery for the same id a no-op. Without that, a repeat can double-grant a reward. - Reconcile anything financial. A dropped delivery is unrecoverable, so never treat this webhook as your ledger. Reconcile periodically against your Encore dashboard analytics and treat the webhook as a low-latency hint that a completion happened.
Testing locally
The SSRF guard refuses private addresses, sohttp://localhost:3000 can never be a webhook URL, including for test deliveries. Expose your local server through a tunnel with a public HTTPS hostname (ngrok, Cloudflare Tunnel, Tailscale Funnel), point the webhook at the tunnel URL, and use Send Test. On the demo campaign, a test-conversion call from your own integration simulates the advertiser postback, so you can exercise the full flow without a live advertiser.
Troubleshooting
Signature never matches
Signature never matches
Almost always a body-bytes problem. Verify against the raw request body, not
JSON.stringify(req.body). Also confirm you’re joining timestamp and body with a literal dot, hex-encoding (not base64), using the header timestamp rather than your own clock, and using the current secret if you recently rotated it.Send Test reports a timeout or connection error
Send Test reports a timeout or connection error
A timeout means your endpoint took longer than 5 seconds: return
2xx as soon as the signature verifies. A connection error means the URL isn’t HTTPS, the host isn’t publicly resolvable (use a tunnel), or the path redirects (redirects are rejected, not followed).No events arriving in production
No events arriving in production
Confirm the status toggle is enabled, and remember the event fires on verified completions, driven by the advertiser, not by claims. Run Send Test first to isolate reachability from event volume.
A completion appears in the dashboard but no webhook arrived
A completion appears in the dashboard but no webhook arrived
Expected under the no-retry model: the delivery was attempted, failed (endpoint down, non-2xx, timeout, TLS error), and was dropped. Deliveries are not replayable; reconcile from dashboard analytics.
Related
- offer_completed webhook reference: the payload contract, signature spec, and delivery guarantees.
- Rewarded Actions guide: the flagship flow whose completions this webhook reports.