Skip to content
CountdownFlow Help Sign in

Receive and verify signed webhooks

Create an endpoint, validate signatures, handle retries and troubleshoot delivery failures.

3 min read

Screenshots show the real application in an isolated demo environment with fictional example data. Setup notices and controls may differ by environment, plan and role. Click an image to view it full-size, and always use the values generated in your own workspace.

Webhooks notify your server

API requests ask the app to do something; webhooks notify your HTTPS server that an event occurred. Set up an endpoint that accepts JSON POST requests and choose only the events your integration needs.

Examples include campaign.created, campaign.updated, campaign.published, campaign.paused, campaign.expired, campaign.viewed, campaign.clicked, conversion.created, recipient.started, recipient.first_opened and recipient.extended. Event availability does not make every event proof of a human interaction.

Create and test an endpoint

  1. 1Open API & webhooks and add the endpoint with at least one supported event.
  2. 2Use a public HTTPS address on port 443. Local/private addresses and redirecting endpoints are not supported by the delivery safety checks.
  3. 3Securely store the endpoint signing secret on your receiver.
  4. 4Use Send test only when you are ready: a signed sample can trigger real automation at the destination.
  5. 5Inspect delivery history, status and attempts, then test an actual event using a separate test campaign.
Webhook endpoint URL and supported event selection controls
In the application Enter your public HTTPS receiver and select the events it needs. Save before testing, and remember that a signed test can trigger real automation. View full-size screenshot (opens in a new tab)

Validate the signature before processing

Read the X-CountdownFlow-Signature header. Its format is t=TIMESTAMP,v1=SIGNATURE. The signature is an HMAC-SHA256 of the timestamp, a dot and the exact raw HTTP body, using the webhook signing secret.

Reject stale timestamps according to your replay policy and compare signatures with a timing-safe function. Do not decode and re-encode JSON before verification: that changes the signed bytes. The X-CountdownFlow-Event header identifies the event type, but a header alone is not authentication.

// Receiver-side PHP example. Supply the secret from secure server storage. $raw = file_get_contents('php://input'); $header = $_SERVER['HTTP_X_COUNTDOWNFLOW_SIGNATURE'] ?? ''; $secret = getenv('COUNTDOWNFLOW_WEBHOOK_SECRET'); if (!$secret || !preg_match('/^t=(\d+),v1=([a-f0-9]{64})$/', $header, $m)) { http_response_code(401); exit; } if (abs(time() - (int) $m[1]) > 300) { http_response_code(401); exit; } $expected = hash_hmac('sha256', $m[1].'.'.$raw, $secret); if (!hash_equals($expected, $m[2])) { http_response_code(401); exit; } $event = json_decode($raw, true, 512, JSON_THROW_ON_ERROR); // Deduplicate the payload ID and enqueue trusted processing in your system. http_response_code(200);

Retries and duplicate-safe processing

A delivery can be retried. Retries share the logical payload ID, so record that identifier and make your handler idempotent before doing things such as granting access or creating an order.

Verify and acknowledge promptly, then process heavier work in your own queue. A timeout or non-successful response may lead to retry. Redirects are not followed. Use the delivery screen to understand failures rather than exposing response bodies or credentials in support tickets.

Changes and secret rotation

Disabling an endpoint also disables its tests. If you rotate the signing secret, update the receiver securely and verify the new configuration. Do not confuse these application webhooks with the platform’s internal billing-provider webhook setup.

Related articles