Webhooks — Overview

Webhooks

Webhooks are how Rewarded Media notifies your server about things that happened to your members — they hit a reward threshold, they got flagged for fraud, and so on. Your server receives a real-time HTTP callback with transaction details so you can credit the user, scrub a payout, or update internal state.

Webhooks are fully configurable in the Rewarded Media dashboard: URL, HTTP method, custom headers, body template, signing secret, and which events this webhook subscribes to. At send time we render the body and headers by interpolating template variables (macros) into whatever you've configured.

Supported events — subscribe each webhook to the ones you care about. Branch on {{event}}:

reward_unlocked Fires once when a member hits the promotion's cumulative reward threshold — the signal to credit the full reward.
fraud_flagged Fires when a member trips a block-severity fraud rule — payout is zeroed on our side; scrub your own crediting.

Prerequisites

  • An HTTPS endpoint that returns a 2xx status code
  • A shared secret (configured per webhook) used to generate the HMAC signature
  • Respond within the timeout budget (see Delivery & Retry Behavior below)

Template Variables

All macros use double curly-brace syntax: {{variable_name}}. You can reference them in any header value or in the body template. For GET requests you don't need to put macros in the URL — every variable is automatically appended as a query-string parameter.

Macro Description Example
{{event}} Name of the event this delivery represents. Branch your handler on this so reward crediting only runs on reward_unlocked and payout scrubbing only runs on fraud_flagged. reward_unlocked
{{member_id}} External member ID you passed in the signed link. Falls back to the internal UUID if no external ID was provided. abc123
{{points_earned}} Integer points earned, in the whole-number denomination ($0.01 → 1, not 0.01). 25
{{user_payout}} Dollar amount owed to the user on this transaction, formatted to 4 decimal places. 0.0050
{{cumulative_user_payout}} Total dollars the member has earned across every transaction on this promotion, including this one. Use this on reward_unlocked to credit the full reward in one shot. 0.2000
{{org_retention}} Dollars retained by the org/partner on this transaction (what you keep). 4 decimal places. 0.0010
{{org_gross}} Dollars in the org pool on this transaction (before the user split). 4 decimal places. 0.0060
{{platform_cut}} Rewarded Media's take on this transaction (including any CPC spread on flat-rate promotions). 4 decimal places. 0.0020
{{gross_revenue}} Advertiser-side gross (Σ campaign CPC) for this transaction. 4 decimal places. 0.0080
{{promotion_id}} Rewarded Media's internal numeric promotion ID. 42
{{promotion_slug}} Promotion slug (URL-safe identifier). winter-promo
{{transaction_id}} Rewarded Media transaction ID. Recommended for idempotency — store it on your side and ignore repeat deliveries. 1829
{{completed_at}} ISO 8601 timestamp of when the transaction occurred (UTC). 2026-04-21T16:01:42Z
{{signature}} HMAC signature of the rendered body, prefixed with the algorithm name. Header values only — not valid in the body or URL. sha256=9f3a...

HTTP Methods

  • GET — All template variables are automatically appended to the URL as query-string parameters. Any query-string params you already set on the URL are preserved.
  • POST / PUT / PATCH / DELETE — The body template is rendered with variable interpolation and sent as the request body. If the body template is left blank, the default payload is a JSON object containing every template variable (excluding {{signature}}).

Signing the Payload

When a shared secret is configured, the {{signature}} macro expands to an HMAC of the rendered request body, keyed by the shared secret, encoded as lowercase hexadecimal, prefixed with the algorithm name:

sha256=9f3a5e8c...   # SHA-256 (default)
sha512=41d2ab...      # SHA-512

By default, whenever a shared secret is configured, we send the signature in the X-Signature header automatically — you don't need to configure anything extra:

X-Signature: sha256=<lowercase-hex>

Override the header name: If your server expects a different header (e.g. X-Hub-Signature-256, Authorization), configure a custom header in the webhook form with {{signature}} as the value. When a custom signature header is present, the default X-Signature is not sent.

To verify on your server: compute HMAC-SHA256(raw_request_body, shared_secret).hexdigest(), strip the sha256= prefix from the header, and compare the two values with a constant-time comparison. For GET requests the body is empty, so the signature is an HMAC over an empty string — if you need authenticated GETs, prefer POST.

Delivery & Retry Behavior

  • Connect timeout: 5 seconds. Read timeout: 10 seconds.
  • Delivery is retried once per minute for up to 15 attempts on any failure — network errors, timeouts, or non-2xx HTTP responses. After 15 attempts the delivery is considered permanently failed.
  • Every attempt (success or failure) is recorded in the delivery log with the response status or error message.
  • Use {{transaction_id}} to deduplicate on your side in case of redelivery.