Webhooks

Pro

What are webhooks?

Webhooks let tapurl.io notify your server in real time whenever a click or conversion happens. Your server receives a POST request with a JSON payload.

Use webhooks to feed click data into your CRM, analytics platform, or custom dashboards without polling our API.

Setup

  1. Open your project and click «Webhook» in the top right
  2. Enter the HTTPS URL of your endpoint
  3. Click Save — a signing secret is generated automatically
  4. Click «Send test webhook» to verify your endpoint is reachable

Events

EventWhen sent
clickEvery real click (bots excluded). Sent immediately after the redirect.
conversionWhen a postback is received from your affiliate network.
testSent manually when you click «Send test webhook».

Payload format

All events are JSON. The event field tells you which type it is.

Click event — full payload

{ "event": "click", "timestamp": "2026-07-01T12:00:00Z", "click_id": "a1b2c3d4e5f6...", "link_id": "uuid", "short_code": "xK3mP9q", "redirect_url": "https://offer.com/?click_id=a1b2...", "ip": "1.2.3.4", "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0...)", "country": "RU", "device": "mobile", "os": "iOS", "referrer_domain": "t.me", "referrer_url": "https://t.me/channel/123", "is_unique": true, "sub1": "facebook_ads", "sub2": "campaign_spring", "sub3": "banner_v2", "sub4": null, "sub5": null, "sub6": null, "sub7": null, "sub8": null }

Conversion event — includes original click SubIDs

{ "event": "conversion", "timestamp": "2026-07-01T12:05:00Z", "conversion_id": "uuid", "click_id": "a1b2c3d4e5f6...", "link_id": "uuid", "status": "approved", "payout": 12.50, "goal": "lead", "transaction_id": "TX123", "sub1": "facebook_ads", "sub2": "campaign_spring", "sub3": "banner_v2", "sub4": null, "sub5": null, "sub6": null, "sub7": null, "sub8": null, "click_country": "RU", "click_device": "mobile", "click_referrer": "t.me" }

Signature verification

Every request includes an HMAC-SHA256 signature in the X-Tapurl-Signature header. Use your signing secret (shown on the webhook settings page) to verify the request came from tapurl.io.

X-Tapurl-Signature: sha256=<hex>

Always verify the signature before processing the payload:

Python

import hmac, hashlib def verify(secret: str, body: bytes, header: str) -> bool: expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest() return hmac.compare_digest(f"sha256={expected}", header)

Node.js

const crypto = require('crypto'); function verify(secret, body, header) { const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(body) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(header) ); }

PHP

function verify(string $secret, string $body, string $header): bool { $expected = 'sha256=' . hash_hmac('sha256', $body, $secret); return hash_equals($expected, $header); }

Retry logic

If your endpoint returns a non-2xx status or times out, tapurl.io retries with exponential backoff:

AttemptDelay
1Immediately → 30 seconds later
22 minutes later
310 minutes later
41 hour later
56 hours later → marked as failed

After 5 failed attempts the delivery is marked as failed. You can see the history on the Webhook settings page.

Make your endpoint idempotent — use the delivery ID (X-Tapurl-Delivery header) to detect duplicates in case of retries.