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
- Open your project and click «Webhook» in the top right
- Enter the HTTPS URL of your endpoint
- Click Save — a signing secret is generated automatically
- Click «Send test webhook» to verify your endpoint is reachable
Events
| Event | When sent |
click | Every real click (bots excluded). Sent immediately after the redirect. |
conversion | When a postback is received from your affiliate network. |
test | Sent 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:
| Attempt | Delay |
| 1 | Immediately → 30 seconds later |
| 2 | 2 minutes later |
| 3 | 10 minutes later |
| 4 | 1 hour later |
| 5 | 6 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.