Skip to main content
guides

Receive a Referral Reward Webhook in Python with Flask

Receive a signed WarpLink referral webhook in Python with Flask: verify the HMAC signature on the raw body, dedupe by event id in SQLite, and test locally.

WarpLink Team8 min read

To receive a WarpLink referral webhook in Python, read the raw bytes with request.get_data(), verify the X-WarpLink-Signature HMAC-SHA256 over timestamp.body with the whole whsec_ secret, insert the event id so a retry does nothing, and grant the reward. This guide gives a complete Flask receiver you can run and test locally.

You get a server that verifies each delivery, stores handled event ids in SQLite, handles referral.claimed, referral.flagged, and referral.updated, and answers well inside the 10-second limit. A script signs a sample event so you can test without a phone. The Node.js version is here.

What arrives

Every delivery is a POST with Content-Type: application/json and three headers:

HeaderValue
X-WarpLink-SignatureOne or more lowercase hex signatures, separated by commas
X-WarpLink-TimestampThe send time in seconds since the Unix epoch
X-WarpLink-Event-IdThe event id, the same value as id in the body

The body has four fields: id, type, created, and data. For a referral event, data holds referral_id, app_id, link (id, slug, url), installer_id, platform, match (method, exact, confidence), tapped_at, installed_at, claimed_at, claimed_via, and flags. The webhooks guide lists the complete payloads.

The signature is the lowercase hex HMAC-SHA256 of ${timestamp}.${rawBody}, keyed with the whole signing secret including its whsec_ prefix. For 24 hours after a rotation the header holds two signatures, separated by a comma.

Step 1: Create the endpoint and copy the secret

Open the app in the dashboard, go to Referral handoff, choose the Webhook tab, and add an endpoint with a public https address. Copy the signing secret, which starts with whsec_ and is shown in full once.

Step 2: Set up the project

Step 3: Write the verifier

The order follows the docs: check the timestamp is a plain integer within 300 seconds, compute the HMAC over the raw bytes, and compare in constant time against every listed signature. Save this as verify.py:

hmac.compare_digest runs in constant time. The any call accepts a match on any listed signature, which is what makes a secret rotation painless.

Step 4: Write the receiver

Save this as app.py. It verifies the raw bytes first, records the event id in SQLite with INSERT OR IGNORE, and answers fast:

Start it:

Four choices are worth naming:

  • request.get_data(), not request.json. The signature covers the exact bytes we sent. Parse the JSON from those bytes after the check.
  • 400 for a bad signature. Any 4xx ends the event as failed, so a forged request is never retried. Only a 5xx or a timeout retries.
  • Dedupe in the database. Delivery is at least once. The PRIMARY KEY on the event id makes INSERT OR IGNORE an atomic "have I seen this?" check, even with several workers.
  • Keep the handler short. We wait 10 seconds for a reply. For slow reward systems, enqueue the event and return at once.

Step 5: Test without a phone

The dashboard's Send test button reaches a public address. For a local server, sign a sample event yourself. Save this as send_test.py:

Run it in a second terminal with the same secret:

You see 204 twice, and the server prints one reward for installer ... line. The second 204 is the dedupe working: same event id, no second reward. Change a byte of raw_body after signing and the answer is 400.

To reach your machine from the dashboard, expose port 3000 through any https tunnel, add that address as the endpoint, and press Send test. The test event has an id that starts evt_test_, goes out once, and is never retried.

Step 6: Move to production

  • Use your own database. Put the event insert and the reward write in one transaction, so a crash between them cannot lose or double a reward.
  • Run behind a production server such as gunicorn, and keep the endpoint on public https.
  • Handle referral.updated. It carries previous, the old link and match. Take the credit from the previous link's owner and give it to the new one. See exact versus estimated matches.
  • Rotate without downtime. The old secret keeps signing beside the new one for 24 hours. The verifier already accepts either signature.
  • Resend after an outage. A failed event can be sent again with the same id for 7 days, from the Referrals report or with POST /v1/webhook-events/{id}/resend.

For the rules that decide whether a claim should be paid, read verify a referral before you grant the reward.

Frequently Asked Questions

Read the raw bytes with request.get_data(), build the timestamp, a period, and the body, and compute hmac.new with the whole whsec_ secret, the signed bytes, and hashlib.sha256. Compare the hex digest to each signature in X-WarpLink-Signature with hmac.compare_digest, after checking the timestamp is within 300 seconds.

Should I use request.json or request.get_data() in Flask?

Use request.get_data(). The signature covers the exact bytes we sent, and a parsed and re-serialized body does not match. Parse the JSON from the same bytes after the check passes.

How do I stop a retry from paying a reward twice?

Insert the event id into a table with the id as primary key before granting the reward. If the insert adds no row, the event is a repeat. The id is the same on every retry and every resend.

Does the secret include the whsec_ prefix?

Yes. The HMAC key is the full secret string including the whsec_ prefix, encoded as UTF-8.

Up to 5 attempts over 20 minutes, at once and then after 30 seconds, 2 minutes, 8 minutes, and 20 minutes from the first. A 5xx answer or a timeout retries. A 4xx or 3xx answer ends the event as failed.

We do the matching, keep the record, and send a signed event the moment an install is credited. Your receiver stays small because the hard part, proving which tap led to which sign-up, is already done.

One workflow, three pillars. Deep linking carries the invitation, install attribution credits the link that produced the new user, and real-time analytics shows referrals beside every other link. Sub-10ms redirects on the way in, one SDK on the way out.

Create a free WarpLink account and get deep linking, install attribution, and real-time analytics in one SDK, with 10,000 clicks a month on the free plan.

Try WarpLink on one real link

Deep linking, install attribution, and real-time analytics in one SDK. Free for 10K clicks a month, no sales call.

Stuck on setup? Email support@warplink.app and we will help you get one real link working.

WarpLink Team

Building affordable, reliable link infrastructure for mobile teams. Deep linking, install attribution, and real-time analytics in one SDK.