Webhooks
Webhooks let you receive real-time HTTP notifications when events occur in your Appspace. Configure webhook endpoints in your dashboard to get notified about link clicks, installs, deferred link claims, and referral activity.
Events
Section titled “Events”| Event | Triggered when |
|---|---|
link.clicked | A user clicks a deep link |
install.tracked | A user is redirected to an app store |
deferred_link.claimed | An SDK claims a deferred deep link after install |
referral.created | A new referral code is created via the API |
referral.completed | A referral is marked as completed |
ecommerce.purchase | A purchase event is recorded |
ecommerce.refund | A refund event is recorded |
ecommerce.cart_abandoned | A cart is detected as abandoned |
ecommerce.add_to_cart | A user adds an item to their cart |
ecommerce.begin_checkout | A user starts the checkout process |
ecommerce.add_to_wishlist | A user adds an item to their wishlist |
ecommerce.fraud_flagged | An event flagged by fraud detection |
Payload format
Section titled “Payload format”Every webhook delivery sends a POST request with a JSON body:
{ "event": "link.clicked", "timestamp": "2026-03-02T12:00:00.000Z", "data": { "prefix": "merchant", "token": "abc123", "hostname": "myapp.tolinku.com", "ip": "203.0.113.42", "platform": "ios", "device_type": "mobile", "campaign": "spring-sale" }}Event data fields
Section titled “Event data fields”link.clicked:
| Field | Description |
|---|---|
prefix | Route prefix |
token | Dynamic link token (if applicable) |
hostname | Appspace domain |
ip | Visitor IP address |
platform | ios, android, or desktop |
device_type | mobile or desktop |
campaign | UTM campaign (if present) |
install.tracked:
| Field | Description |
|---|---|
hostname | Appspace domain |
ip | Visitor IP address |
platform | ios or android |
device_type | Device type |
campaign | UTM campaign (if present) |
deferred_link.claimed:
| Field | Description |
|---|---|
deep_link_path | The claimed deep link path |
method | token or signals |
ip | Visitor IP address |
referral.created:
| Field | Description |
|---|---|
referral_code | The generated code |
referrer_id | The referrer’s user ID |
referral_url | The full referral URL |
referral.completed:
| Field | Description |
|---|---|
referral_code | The referral code |
referrer_id | The referrer’s user ID |
referred_user_id | The referred user’s ID |
milestone | The completion milestone |
reward_type | Referrer reward type (e.g. credit, discount), or null |
reward_value | Referrer reward value, or null |
referee_reward_type | Referee reward type, or null |
referee_reward_value | Referee reward value, or null |
ecommerce.purchase:
| Field | Description |
|---|---|
transaction_id | Unique order identifier |
user_id | The buyer’s user ID |
revenue | Total revenue amount |
currency | ISO 4217 currency code |
items_count | Number of items in the order |
coupon_code | Applied coupon (if any) |
ecommerce.refund:
| Field | Description |
|---|---|
transaction_id | The refunded order identifier |
user_id | The buyer’s user ID |
revenue | Refund amount |
currency | ISO 4217 currency code |
ecommerce.cart_abandoned:
| Field | Description |
|---|---|
user_id | The user who abandoned checkout |
cart_id | Cart session identifier |
checkout_timestamp | When the checkout was started |
hours_since_checkout | Hours elapsed since checkout began |
ecommerce.fraud_flagged:
| Field | Description |
|---|---|
event_type | The ecommerce event type (e.g. purchase) |
transaction_id | Order identifier (if applicable) |
user_id | The user who triggered the event |
revenue | Revenue amount |
rule | Which fraud rule was triggered (bot_detection, impossible_revenue, velocity_check, currency_mismatch) |
severity | 1 (flagged, stored but marked) |
Request headers
Section titled “Request headers”Every webhook request includes:
| Header | Description |
|---|---|
Content-Type | application/json |
X-Webhook-Event | The event type (e.g. link.clicked) |
X-Webhook-Signature | HMAC-SHA256 signature of the request body |
Signature verification
Section titled “Signature verification”Every webhook endpoint has a signing secret (prefixed whsec_). Use it to verify that webhook deliveries are authentic.
The signature is an HMAC-SHA256 hex digest of the raw request body:
HMAC-SHA256(request_body, signing_secret) = signatureVerification examples
Section titled “Verification examples”Node.js:
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) { const expected = crypto .createHmac('sha256', secret) .update(body) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) );}
// In your webhook handler:app.post('/webhooks/tolinku', (req, res) => { const signature = req.headers['x-webhook-signature']; const isValid = verifyWebhook(req.rawBody, signature, 'whsec_your_secret');
if (!isValid) { return res.status(401).json({ error: 'Invalid signature' }); }
const { event, data } = req.body; // Process the event... res.status(200).json({ ok: true });});Python:
import hmacimport hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool: expected = hmac.new( secret.encode(), body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected)Delivery and retries
Section titled “Delivery and retries”- Timeout: 10 seconds. If your endpoint does not respond within 10 seconds, the delivery is marked as failed.
- Success: Any HTTP 2xx status code is considered successful.
- Retries: Failed deliveries are retried up to 3 times with increasing delays:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
After 4 total attempts (1 original + 3 retries), the delivery is marked as permanently failed.
Delivery logs
Section titled “Delivery logs”Every delivery attempt (success or failure) is logged. You can view delivery history in the Webhooks section of your dashboard, including:
- HTTP status code
- Response time
- Error message (if failed)
- Attempt number
Best practices
Section titled “Best practices”- Respond quickly. Return a
200status code as soon as you receive the payload. Process the event asynchronously if your handler needs to do significant work. - Handle duplicates. In rare cases, the same event may be delivered more than once. Use the
timestampand event data to deduplicate. - Verify signatures. Always verify the
X-Webhook-Signatureheader before processing the payload. - Use HTTPS. Webhook URLs must use HTTPS in production.
Plan limits
Section titled “Plan limits”| Plan | Endpoints | Deliveries/mo |
|---|---|---|
| Free | 1 | 500 |
| Standard | Unlimited | 10,000 |
| Growth | Unlimited | 50,000 |
| Scale | Unlimited | 200,000 |
| Enterprise | Unlimited | Unlimited |