{"id":730,"date":"2026-04-08T09:00:00","date_gmt":"2026-04-08T14:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=730"},"modified":"2026-03-07T03:33:21","modified_gmt":"2026-03-07T08:33:21","slug":"referral-program-automation","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/referral-program-automation\/","title":{"rendered":"Automating Your Referral Program with Webhooks"},"content":{"rendered":"\n<p>Most referral programs start the same way. You build a referral link system, hand out codes to users, and track who referred whom. Then you realize you need to actually do something when a referral completes: credit the referrer&#39;s account, send a thank-you email, update your CRM, notify your support team. Doing this manually does not scale past your first hundred users.<\/p>\n\n\n\n<p>Webhooks turn your referral program from a passive tracking system into an automated pipeline. Every time a referral event fires (a code is created, a milestone is reached, a referral completes), your backend gets a POST request with the event data. From there, you can trigger whatever workflow makes sense: issue a discount code, send a push notification, update a Slack channel, or log it to your analytics warehouse.<\/p>\n\n\n\n<p>This guide walks through the technical setup for automating referral workflows with webhooks, using <a href=\"https:\/\/tolinku.com\/features\/referrals\">Tolinku&#39;s referral system<\/a> as the foundation.<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/screenshot-referrals-1772819416568.png\" alt=\"Tolinku referral program dashboard with analytics\">\n<em>The referrals page with stats cards, referral list, and leaderboard tabs.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How Referral Webhooks Work<\/h2>\n\n\n\n<p>A referral webhook follows the same pattern as any other webhook: your server registers an endpoint URL, subscribes to specific event types, and receives HTTP POST requests when those events occur.<\/p>\n\n\n\n<p>Tolinku fires two referral-specific webhook events:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Event<\/th>\n<th>Fires When<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td><code>referral.created<\/code><\/td>\n<td>A new referral code is generated via the API<\/td>\n<\/tr>\n<tr>\n<td><code>referral.completed<\/code><\/td>\n<td>A referred user completes the required milestone<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<p>Each event arrives as a JSON payload with a consistent envelope:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;event&quot;: &quot;referral.completed&quot;,\n  &quot;timestamp&quot;: &quot;2026-04-08T14:30:00.000Z&quot;,\n  &quot;data&quot;: {\n    &quot;referral_code&quot;: &quot;ABC1234567&quot;,\n    &quot;referrer_id&quot;: &quot;user_123&quot;,\n    &quot;referred_user_id&quot;: &quot;user_456&quot;,\n    &quot;milestone&quot;: &quot;first_purchase&quot;\n  }\n}\n<\/code><\/pre>\n\n\n\n<p>The <code>referral.created<\/code> payload includes the <code>referral_code<\/code>, <code>referrer_id<\/code>, and the full <code>referral_url<\/code>. The <code>referral.completed<\/code> payload adds the <code>referred_user_id<\/code> and the <code>milestone<\/code> that triggered completion.<\/p>\n\n\n\n<p>Every webhook request includes three headers you should care about:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>X-Webhook-Event<\/code>: The event type (e.g., <code>referral.completed<\/code>)<\/li>\n<li><code>X-Webhook-Signature<\/code>: An HMAC-SHA256 signature for verifying authenticity<\/li>\n<li><code>Content-Type<\/code>: Always <code>application\/json<\/code><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Setting Up the Webhook Endpoint<\/h2>\n\n\n\n<p>Before wiring up automation logic, you need a webhook receiver. This is a standard HTTP endpoint that accepts POST requests, verifies the signature, and dispatches the event to the right handler.<\/p>\n\n\n\n<p>Here is a minimal Node.js example using Express:<\/p>\n\n\n\n<pre><code class=\"language-typescript\">import express from &#39;express&#39;;\nimport crypto from &#39;crypto&#39;;\n\nconst app = express();\n\n\/\/ Parse raw body for signature verification\napp.post(&#39;\/webhooks\/tolinku&#39;, express.raw({ type: &#39;application\/json&#39; }), (req, res) =&gt; {\n  const signature = req.headers[&#39;x-webhook-signature&#39;] as string;\n  const event = req.headers[&#39;x-webhook-event&#39;] as string;\n  const body = req.body.toString();\n\n  \/\/ Verify signature\n  const secret = process.env.TOLINKU_WEBHOOK_SECRET;\n  const expected = crypto.createHmac(&#39;sha256&#39;, secret).update(body).digest(&#39;hex&#39;);\n\n  if (signature !== expected) {\n    return res.status(401).json({ error: &#39;Invalid signature&#39; });\n  }\n\n  \/\/ Parse and dispatch\n  const payload = JSON.parse(body);\n\n  switch (event) {\n    case &#39;referral.created&#39;:\n      handleReferralCreated(payload.data);\n      break;\n    case &#39;referral.completed&#39;:\n      handleReferralCompleted(payload.data);\n      break;\n  }\n\n  \/\/ Always respond 2xx quickly\n  res.status(200).json({ received: true });\n});\n<\/code><\/pre>\n\n\n\n<p>Two things to note here. First, you need the raw request body (not the parsed JSON) for signature verification, because the HMAC is computed over the exact string that was sent. Second, respond with a 2xx status code as fast as possible. If your endpoint takes longer than 10 seconds to respond, the delivery is marked as failed and retried.<\/p>\n\n\n\n<p>Tolinku retries failed deliveries on an escalating schedule: 1 minute, 5 minutes, then 30 minutes. After four total attempts, the event is marked as permanently failed. You can monitor delivery status in the <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/webhooks\/\">webhook dashboard<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Configuring Webhooks in the Dashboard<\/h2>\n\n\n\n<p>You can create webhook endpoints in the Tolinku dashboard under <strong>Webhooks<\/strong> in your Appspace settings. Each webhook needs:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Name<\/strong>: A descriptive label (e.g., &quot;Referral Automation Handler&quot;)<\/li>\n<li><strong>URL<\/strong>: Your HTTPS endpoint. Tolinku enforces HTTPS and blocks localhost, private IPs, and internal TLDs to prevent <a href=\"https:\/\/owasp.org\/www-community\/attacks\/Server_Side_Request_Forgery\" rel=\"nofollow noopener\" target=\"_blank\">SSRF attacks<\/a>.<\/li>\n<li><strong>Events<\/strong>: Select which event types this endpoint should receive. For referral automation, subscribe to <code>referral.created<\/code> and <code>referral.completed<\/code>.<\/li>\n<\/ol>\n\n\n\n<p>After creation, Tolinku generates a signing secret (prefixed with <code>whsec_<\/code>). This secret is shown once, so copy it immediately and store it in your environment variables. You will use it for HMAC signature verification in your webhook handler.<\/p>\n\n\n\n<p>You can also <a href=\"https:\/\/tolinku.com\/docs\/developer\/api-reference\/\">create webhooks via the API<\/a> if you prefer infrastructure-as-code over dashboard clicks.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"944\" height=\"250\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/referral-automation-webhook-form.png\" alt=\"Tolinku webhook creation form with name, URL, and event checkboxes\" class=\"wp-image-726\" srcset=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/referral-automation-webhook-form.png 944w, https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/referral-automation-webhook-form-300x79.png 300w, https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/referral-automation-webhook-form-768x203.png 768w\" sizes=\"auto, (max-width: 944px) 100vw, 944px\" \/><figcaption class=\"wp-element-caption\">Creating a webhook endpoint in the Tolinku dashboard<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Automating Reward Fulfillment<\/h2>\n\n\n\n<p>The most common automation is issuing rewards when a referral completes. Tolinku tracks the referral lifecycle and fires <code>referral.completed<\/code> when the referred user hits the configured milestone. Your job is to fulfill the actual reward.<\/p>\n\n\n\n<p>Here is a practical example that issues a Stripe coupon to the referrer when their referral completes:<\/p>\n\n\n\n<pre><code class=\"language-typescript\">import Stripe from &#39;stripe&#39;;\n\nconst stripe = new Stripe(process.env.STRIPE_SECRET_KEY);\n\nasync function handleReferralCompleted(data: {\n  referral_code: string;\n  referrer_id: string;\n  referred_user_id: string;\n  milestone: string;\n}) {\n  \/\/ Look up the referrer in your database\n  const referrer = await db.users.findOne({ id: data.referrer_id });\n  if (!referrer?.stripeCustomerId) return;\n\n  \/\/ Create a one-time coupon\n  const coupon = await stripe.coupons.create({\n    amount_off: 500, \/\/ $5.00\n    currency: &#39;usd&#39;,\n    duration: &#39;once&#39;,\n    name: `Referral reward: ${data.referral_code}`,\n  });\n\n  \/\/ Attach it to the referrer&#39;s next invoice\n  await stripe.customers.update(referrer.stripeCustomerId, {\n    coupon: coupon.id,\n  });\n\n  \/\/ Mark reward as claimed in Tolinku\n  await fetch(&#39;https:\/\/your-app.tolinku.com\/v1\/api\/referral\/claim-reward&#39;, {\n    method: &#39;POST&#39;,\n    headers: {\n      &#39;Content-Type&#39;: &#39;application\/json&#39;,\n      &#39;X-API-Key&#39;: process.env.TOLINKU_API_KEY,\n    },\n    body: JSON.stringify({ referral_code: data.referral_code }),\n  });\n\n  \/\/ Notify the referrer\n  await sendPushNotification(referrer.id, {\n    title: &#39;You earned a reward!&#39;,\n    body: &#39;Your friend signed up. $5 credit has been applied to your account.&#39;,\n  });\n}\n<\/code><\/pre>\n\n\n\n<p>This pattern separates concerns cleanly. Tolinku handles referral tracking, attribution, and milestone progression. Your backend handles reward fulfillment, payment integration, and user notifications. The webhook is the bridge between the two.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Handling Double-Sided Rewards<\/h2>\n\n\n\n<p>Many referral programs reward both the referrer and the referred user. Tolinku supports this natively. When you configure your <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/referrals\/\">referral settings<\/a>, you can set both a referrer reward and a referee reward (type and value for each).<\/p>\n\n\n\n<p>When <code>referral.completed<\/code> fires, you can fulfill both sides in the same handler:<\/p>\n\n\n\n<pre><code class=\"language-typescript\">async function handleReferralCompleted(data) {\n  \/\/ Fetch the full referral details from the API\n  const res = await fetch(\n    `https:\/\/your-app.tolinku.com\/v1\/api\/referral\/${data.referral_code}`,\n    { headers: { &#39;X-API-Key&#39;: process.env.TOLINKU_API_KEY } }\n  );\n  const referral = await res.json();\n\n  \/\/ Referrer reward\n  if (referral.reward_type === &#39;credit&#39;) {\n    await issueCredit(data.referrer_id, referral.reward_value);\n    await claimReward(data.referral_code, &#39;referrer&#39;);\n  }\n\n  \/\/ Referee reward\n  if (referral.referee_reward_type === &#39;credit&#39;) {\n    await issueCredit(data.referred_user_id, referral.referee_reward_value);\n    await claimReward(data.referral_code, &#39;referee&#39;);\n  }\n}\n\nasync function claimReward(code: string, side: &#39;referrer&#39; | &#39;referee&#39;) {\n  const endpoint = side === &#39;referrer&#39;\n    ? &#39;\/v1\/api\/referral\/claim-reward&#39;\n    : &#39;\/v1\/api\/referral\/claim-referee-reward&#39;;\n\n  await fetch(`https:\/\/your-app.tolinku.com${endpoint}`, {\n    method: &#39;POST&#39;,\n    headers: {\n      &#39;Content-Type&#39;: &#39;application\/json&#39;,\n      &#39;X-API-Key&#39;: process.env.TOLINKU_API_KEY,\n    },\n    body: JSON.stringify({ referral_code: code }),\n  });\n}\n<\/code><\/pre>\n\n\n\n<p>Calling the <code>claim-reward<\/code> and <code>claim-referee-reward<\/code> endpoints updates the referral record so you can track which rewards have been fulfilled. This prevents double-issuing if your webhook handler gets retried.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Multi-Step Referral Funnels with Milestones<\/h2>\n\n\n\n<p>Not every referral should complete on signup. Sometimes you want the referred user to take a meaningful action first: make a purchase, complete onboarding, or reach a usage threshold. Tolinku supports custom milestones for exactly this.<\/p>\n\n\n\n<p>Configure your milestones in the Appspace referral settings. For example, an e-commerce app might define these milestones:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>signed_up<\/code>: User created an account<\/li>\n<li><code>first_purchase<\/code>: User completed their first order<\/li>\n<li><code>completed<\/code>: Final milestone that triggers the reward<\/li>\n<\/ul>\n\n\n\n<p>Your app tracks user progress and reports milestones to Tolinku via the API:<\/p>\n\n\n\n<pre><code class=\"language-typescript\">\/\/ When the referred user signs up\nawait fetch(&#39;https:\/\/your-app.tolinku.com\/v1\/api\/referral\/milestone&#39;, {\n  method: &#39;POST&#39;,\n  headers: {\n    &#39;Content-Type&#39;: &#39;application\/json&#39;,\n    &#39;X-API-Key&#39;: process.env.TOLINKU_API_KEY,\n  },\n  body: JSON.stringify({\n    referral_code: &#39;ABC1234567&#39;,\n    milestone: &#39;signed_up&#39;,\n  }),\n});\n\n\/\/ Later, when they make their first purchase\nawait fetch(&#39;https:\/\/your-app.tolinku.com\/v1\/api\/referral\/milestone&#39;, {\n  method: &#39;POST&#39;,\n  headers: {\n    &#39;Content-Type&#39;: &#39;application\/json&#39;,\n    &#39;X-API-Key&#39;: process.env.TOLINKU_API_KEY,\n  },\n  body: JSON.stringify({\n    referral_code: &#39;ABC1234567&#39;,\n    milestone: &#39;first_purchase&#39;,\n  }),\n});\n<\/code><\/pre>\n\n\n\n<p>Each milestone update is stored in the referral&#39;s <code>milestone_history<\/code> array with a timestamp. When the milestone matches your configured <code>referral_reward_milestone<\/code>, the referral auto-completes and the <code>referral.completed<\/code> webhook fires. You do not need to call the completion endpoint separately.<\/p>\n\n\n\n<p>This gives you a clean separation: your app reports what the user did, Tolinku decides when the referral qualifies for a reward, and your webhook handler fulfills it.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"973\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/referral-automation-settings.png\" alt=\"Tolinku referral program configuration with milestones and reward settings\" class=\"wp-image-727\" srcset=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/referral-automation-settings.png 640w, https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/referral-automation-settings-197x300.png 197w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><figcaption class=\"wp-element-caption\">Referral settings in the Tolinku dashboard, including milestones and reward configuration<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">CRM and Analytics Integration<\/h2>\n\n\n\n<p>Beyond reward fulfillment, webhooks let you pipe referral data into your broader tech stack. Here are patterns that work well:<\/p>\n\n\n\n<p><strong>CRM updates<\/strong>: When <code>referral.created<\/code> fires, tag the referrer in your CRM as an active advocate. When <code>referral.completed<\/code> fires, update the referred user&#39;s record with acquisition source data.<\/p>\n\n\n\n<pre><code class=\"language-typescript\">async function handleReferralCreated(data) {\n  await crm.contacts.update(data.referrer_id, {\n    tags: [&#39;active-referrer&#39;],\n    custom_fields: {\n      last_referral_code: data.referral_code,\n      referral_url: data.referral_url,\n    },\n  });\n}\n<\/code><\/pre>\n\n\n\n<p><strong>Analytics warehouse<\/strong>: Forward every referral event to your data warehouse for cohort analysis. Track referral-acquired users separately to measure their lifetime value against other acquisition channels.<\/p>\n\n\n\n<pre><code class=\"language-typescript\">async function forwardToWarehouse(event: string, data: any) {\n  await bigquery.insert(&#39;referral_events&#39;, {\n    event_type: event,\n    referral_code: data.referral_code,\n    referrer_id: data.referrer_id,\n    referred_user_id: data.referred_user_id || null,\n    timestamp: new Date().toISOString(),\n  });\n}\n<\/code><\/pre>\n\n\n\n<p><strong>Slack notifications<\/strong>: Post to a channel when milestones are hit. This is especially useful during the early days of a referral program when you want visibility into what is working.<\/p>\n\n\n\n<pre><code class=\"language-typescript\">async function notifySlack(data) {\n  await fetch(process.env.SLACK_WEBHOOK_URL, {\n    method: &#39;POST&#39;,\n    headers: { &#39;Content-Type&#39;: &#39;application\/json&#39; },\n    body: JSON.stringify({\n      text: `Referral completed! ${data.referrer_id} referred ${data.referred_user_id} (code: ${data.referral_code})`,\n    }),\n  });\n}\n<\/code><\/pre>\n\n\n\n<p>Tolinku&#39;s <a href=\"https:\/\/tolinku.com\/features\/referrals\">analytics dashboard<\/a> gives you referral stats, conversion rates, and a leaderboard out of the box. Webhooks let you extend that data into systems Tolinku does not control.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"944\" height=\"100\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/referral-automation-stats.png\" alt=\"Tolinku referral stats showing total, pending, completed, expired, and conversion rate\" class=\"wp-image-728\" srcset=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/referral-automation-stats.png 944w, https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/referral-automation-stats-300x32.png 300w, https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/referral-automation-stats-768x81.png 768w\" sizes=\"auto, (max-width: 944px) 100vw, 944px\" \/><figcaption class=\"wp-element-caption\">Referral analytics in the Tolinku dashboard<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Idempotency and Error Handling<\/h2>\n\n\n\n<p>Webhook handlers must be <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Glossary\/Idempotent\" rel=\"nofollow noopener\" target=\"_blank\">idempotent<\/a>. Because Tolinku retries failed deliveries up to three times, your handler might receive the same event more than once. If you issue a reward on every <code>referral.completed<\/code> event without checking whether you have already processed that code, you will double-credit users.<\/p>\n\n\n\n<p>The simplest approach is to track processed referral codes:<\/p>\n\n\n\n<pre><code class=\"language-typescript\">async function handleReferralCompleted(data) {\n  \/\/ Check if already processed\n  const existing = await db.processedReferrals.findOne({\n    referral_code: data.referral_code,\n  });\n  if (existing) return; \/\/ Already handled\n\n  \/\/ Process the reward\n  await issueReward(data);\n\n  \/\/ Mark as processed\n  await db.processedReferrals.insertOne({\n    referral_code: data.referral_code,\n    processed_at: new Date(),\n  });\n}\n<\/code><\/pre>\n\n\n\n<p>Alternatively, use the <code>reward_claimed<\/code> field from Tolinku&#39;s API. Before issuing a reward, fetch the referral and check whether <code>reward_claimed<\/code> is already <code>true<\/code>. This works because the <code>claim-reward<\/code> endpoint is itself idempotent.<\/p>\n\n\n\n<p>For transient failures in your own systems (database down, payment API timeout), let the webhook response return a 5xx status. Tolinku will retry, giving your infrastructure time to recover. Do not swallow errors and return 200 if the reward was not actually issued.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fraud Prevention in Automated Referrals<\/h2>\n\n\n\n<p>Automated reward fulfillment creates a target for abuse. Without safeguards, bad actors can self-refer, create fake accounts, or exploit your reward system at scale. See the <a href=\"https:\/\/tolinku.com\/blog\/?p=680\">referral fraud prevention guide<\/a> for a deep dive, but here are the key controls relevant to webhook automation:<\/p>\n\n\n\n<p><strong>Rate limits<\/strong>: Tolinku&#39;s <code>referral_max_per_user<\/code> setting caps how many active pending referrals a single user can have. This prevents one user from flooding your system with referral codes.<\/p>\n\n\n\n<p><strong>Expiration<\/strong>: The <code>referral_expiration_days<\/code> setting automatically expires unclaimed referrals. When a referral expires, its status changes to <code>expired<\/code> and no completion webhook will fire. This limits the window for abuse.<\/p>\n\n\n\n<p><strong>Verification in your handler<\/strong>: Before issuing rewards, validate the referral against your own business rules. Check that the referred user is a real account (not a disposable email), that the referrer and referee are different people (different IP, different device), and that the completion milestone represents genuine engagement.<\/p>\n\n\n\n<pre><code class=\"language-typescript\">async function handleReferralCompleted(data) {\n  const referrer = await db.users.findOne({ id: data.referrer_id });\n  const referee = await db.users.findOne({ id: data.referred_user_id });\n\n  \/\/ Basic fraud checks\n  if (referrer.email.split(&#39;@&#39;)[1] === referee.email.split(&#39;@&#39;)[1]) {\n    \/\/ Same email domain, might be self-referral from a custom domain\n    await flagForReview(data.referral_code);\n    return;\n  }\n\n  if (referrer.signupIp === referee.signupIp) {\n    \/\/ Same IP address, likely self-referral\n    await flagForReview(data.referral_code);\n    return;\n  }\n\n  \/\/ Proceed with reward\n  await issueReward(data);\n}\n<\/code><\/pre>\n\n\n\n<p>The point is not to catch every fraud case in the webhook handler. It is to add a layer of validation before money changes hands. Flag suspicious cases for manual review rather than blocking them outright.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Testing Your Webhook Integration<\/h2>\n\n\n\n<p>Tolinku provides a test webhook feature in the dashboard. Click &quot;Send Test&quot; on any configured webhook to receive a test payload:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;event&quot;: &quot;test&quot;,\n  &quot;timestamp&quot;: &quot;2026-04-08T10:00:00.000Z&quot;,\n  &quot;data&quot;: {\n    &quot;message&quot;: &quot;This is a test webhook from Tolinku.&quot;,\n    &quot;webhook_id&quot;: &quot;abc123&quot;,\n    &quot;appspace_id&quot;: &quot;def456&quot;\n  }\n}\n<\/code><\/pre>\n\n\n\n<p>For end-to-end testing of referral automation, use the <a href=\"https:\/\/tolinku.com\/docs\/developer\/api-reference\/\">referral API<\/a> to create a referral, advance it through milestones, and complete it. Each step fires the corresponding webhook, letting you verify your handler processes the full lifecycle correctly.<\/p>\n\n\n\n<p>During development, tools like <a href=\"https:\/\/webhook.site\" rel=\"nofollow noopener\" target=\"_blank\">webhook.site<\/a> or <a href=\"https:\/\/ngrok.com\/docs\" rel=\"nofollow noopener\" target=\"_blank\">ngrok<\/a> are useful for inspecting payloads before your handler code is ready. Once you move to staging, point the webhook at your actual endpoint and monitor the delivery log in the Tolinku dashboard for failures.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Putting It All Together<\/h2>\n\n\n\n<p>Here is the full architecture for an automated referral program:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>User generates a referral link<\/strong> via your app&#39;s UI, which calls the <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/referrals\/\">Tolinku referral API<\/a> to create a code.<\/li>\n<li><strong>Tolinku fires <code>referral.created<\/code><\/strong> to your webhook. Your handler tags the user as an active referrer in your CRM and posts to Slack.<\/li>\n<li><strong>The referred user clicks the link<\/strong>. Tolinku handles deep linking (or <a href=\"https:\/\/tolinku.com\/blog\/?p=363\">deferred deep linking<\/a> if the app is not installed), attribution, and routing.<\/li>\n<li><strong>Your app reports milestones<\/strong> as the referred user progresses through your funnel: signup, activation, first purchase.<\/li>\n<li><strong>Tolinku fires <code>referral.completed<\/code><\/strong> when the reward milestone is reached. Your handler issues rewards to both sides via Stripe, marks them as claimed via the API, and sends notifications.<\/li>\n<li><strong>Everything is logged<\/strong>: Tolinku tracks the referral lifecycle and delivery attempts. Your warehouse gets the raw events for long-term analysis.<\/li>\n<\/ol>\n\n\n\n<p>The entire pipeline runs without manual intervention. You configure it once, and it scales with your user base. When you need to change reward amounts or add a new milestone, update the <a href=\"https:\/\/tolinku.com\/features\/referrals\">referral settings<\/a> in the dashboard. The webhook handler adapts because it reads reward values from the referral record rather than hardcoding them.<\/p>\n\n\n\n<p><a href=\"https:\/\/tolinku.com\/blog\/?p=379\">Building Referral Programs That Actually Work<\/a> covers the strategy side: what incentives drive sharing, how to structure tiers, and when to launch. This article covered the plumbing. Together, they give you a referral program that runs itself.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Automate referral program workflows with webhooks. Trigger reward fulfillment, send notifications, and update your CRM when referral events fire.<\/p>\n","protected":false},"author":2,"featured_media":729,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Automating Your Referral Program with Webhooks","rank_math_description":"Automate referral program workflows with webhooks. Trigger reward fulfillment, notifications, and CRM updates on referral events.","rank_math_focus_keyword":"referral program automation","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-referral-program-automation.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-referral-program-automation.png","footnotes":""},"categories":[13],"tags":[62,38,39,20,46,44,45,61],"class_list":["post-730","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-growth","tag-api","tag-campaign-tracking","tag-conversion","tag-deep-linking","tag-growth-hacking","tag-referral-programs","tag-referrals","tag-webhooks"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/730","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=730"}],"version-history":[{"count":4,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/730\/revisions"}],"predecessor-version":[{"id":2136,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/730\/revisions\/2136"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/729"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=730"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=730"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=730"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}