Webhook Events Reference
Complete reference for all webhook events emitted by the SnipLink API. Use webhooks to receive real-time notifications when links are created, updated, deleted, or clicked.
Event Structure
All webhook events follow a standard structure:
{
"id": "evt_1234567890abcdef",
"type": "link.created",
"timestamp": "2024-02-06T12:00:00.000Z",
"workspaceId": "ws_abc123",
"data": {
// Event-specific data
}
}| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for this event (for idempotency) |
type | string | Event type (e.g., link.created) |
timestamp | string | ISO 8601 timestamp when the event occurred |
workspaceId | string | ID of the workspace where the event occurred |
data | object | Event-specific payload (see event types below) |
id field to implement idempotency in your webhook handler. If you receive the same event twice (due to retries), you should process it only once.Link Events
link.created
Emitted when a new short link is created.
{
"id": "evt_abc123",
"type": "link.created",
"timestamp": "2024-02-06T12:00:00.000Z",
"workspaceId": "ws_xyz789",
"data": {
"link": {
"id": "lnk_123456",
"url": "https://example.com/destination",
"shortUrl": "https://snip.link/abc123",
"slug": "abc123",
"domainId": "dom_custom",
"title": "Example Link",
"description": "This is an example link",
"tags": ["marketing", "campaign"],
"clickCount": 0,
"clickLimit": null,
"trackingEnabled": true,
"expiresAt": null,
"createdAt": "2024-02-06T12:00:00.000Z",
"createdBy": "usr_creator123"
}
}
}Use Cases:
- Send Slack notification when campaign links are created
- Automatically generate QR codes for new links
- Sync link creation to analytics dashboard
- Trigger email campaigns when product links are added
link.updated
Emitted when a short link is modified.
{
"id": "evt_def456",
"type": "link.updated",
"timestamp": "2024-02-06T14:30:00.000Z",
"workspaceId": "ws_xyz789",
"data": {
"link": {
"id": "lnk_123456",
"url": "https://example.com/updated-destination",
"shortUrl": "https://snip.link/abc123",
"slug": "abc123",
"domainId": "dom_custom",
"title": "Updated Example Link",
"description": "This link was just updated",
"tags": ["marketing", "campaign", "updated"],
"clickCount": 42,
"clickLimit": 1000,
"trackingEnabled": true,
"expiresAt": "2024-12-31T23:59:59.000Z",
"updatedAt": "2024-02-06T14:30:00.000Z",
"updatedBy": "usr_editor456"
},
"changes": ["url", "title", "tags", "expiresAt"]
}
}Additional Fields:
changes- Array of field names that were modifiedupdatedBy- ID of the user who made the update
Use Cases:
- Log link modifications for audit trails
- Notify team when campaign URLs are changed
- Track destination URL updates for compliance
- Trigger re-validation when links are modified
changes array tells you exactly which fields were updated, allowing you to react only to specific changes (e.g., only notify when the destination URL changes).link.deleted
Emitted when a short link is permanently deleted.
{
"id": "evt_ghi789",
"type": "link.deleted",
"timestamp": "2024-02-06T16:00:00.000Z",
"workspaceId": "ws_xyz789",
"data": {
"link": {
"id": "lnk_123456",
"slug": "abc123",
"shortUrl": "https://snip.link/abc123",
"url": "https://example.com/destination",
"domainId": "dom_custom",
"title": "Deleted Link"
},
"deletedAt": "2024-02-06T16:00:00.000Z",
"deletedBy": "usr_admin789"
}
}Additional Fields:
deletedAt- Timestamp when the link was deleteddeletedBy- ID of the user who deleted the link
Use Cases:
- Archive link data before permanent deletion
- Update external databases or caches
- Notify team of link removals
- Cleanup associated resources (QR codes, analytics)
Click Events
link.clicked
Emitted when someone clicks on a short link.
{
"id": "evt_jkl012",
"type": "link.clicked",
"timestamp": "2024-02-06T18:45:00.000Z",
"workspaceId": "ws_xyz789",
"data": {
"link": {
"id": "lnk_123456",
"slug": "abc123",
"shortUrl": "https://snip.link/abc123",
"url": "https://example.com/destination"
},
"click": {
"id": "clk_click123",
"timestamp": "2024-02-06T18:45:00.000Z",
"country": "US",
"countryName": "United States",
"city": "New York",
"region": "NY",
"device": "mobile",
"deviceBrand": "Apple",
"deviceModel": "iPhone 15",
"browser": "Safari",
"browserVersion": "17.2",
"os": "iOS",
"osVersion": "17.2",
"referrer": "https://twitter.com",
"referrerHost": "twitter.com",
"language": "en-US",
"isBot": false,
"utmSource": "twitter",
"utmMedium": "social",
"utmCampaign": "winter-sale"
}
}
}Use Cases:
- Real-time click notifications for high-value links
- Trigger actions when specific campaigns get clicks
- Feed click data into analytics platforms
- Alert when suspicious click patterns detected
qr.scanned
Emitted when a QR code is scanned (subset of link clicks).
{
"id": "evt_mno345",
"type": "qr.scanned",
"timestamp": "2024-02-06T19:15:00.000Z",
"workspaceId": "ws_xyz789",
"data": {
"link": {
"id": "lnk_123456",
"slug": "abc123",
"shortUrl": "https://snip.link/abc123",
"url": "https://example.com/destination"
},
"scan": {
"id": "scn_scan123",
"timestamp": "2024-02-06T19:15:00.000Z",
"country": "GB",
"city": "London",
"device": "mobile",
"os": "Android",
"isBot": false
}
}
}Use Cases:
- Track offline-to-online conversion events
- Count attendees at physical events
- Monitor print campaign effectiveness
- Trigger follow-up actions after QR scans
Future Events (Planned)
The following events are planned for future releases:
goal.created
Emitted when a new goal is created for a link
goal.milestone_reached
Emitted when a link reaches a click milestone (e.g., 100, 500, 1000 clicks)
goal.completed
Emitted when a link goal is successfully completed
link.expired
Emitted when a link reaches its expiration date or click limit
Event Filtering
When creating webhooks, you can filter which events trigger notifications. This helps reduce noise and webhook volume.
Filter by Event Type
Subscribe only to specific event types:
{
"targetUrl": "https://your-app.com/webhooks",
"events": ["link.created", "link.updated"],
"name": "Link Changes Webhook"
}Filter by Domain
Only receive events for links on specific domains:
{
"targetUrl": "https://your-app.com/webhooks",
"events": ["link.created"],
"filters": {
"domainId": "dom_custom123"
},
"name": "Custom Domain Links"
}Security
All webhook requests include an HMAC-SHA256 signature for verification. Always verify signatures to ensure requests came from SnipLink.
Signature Verification
Each webhook request includes these headers:
X-Sniplink-Webhook-Signature- HMAC signature (format:v1=<hex>)X-Sniplink-Webhook-Timestamp- Unix timestamp (seconds)X-Sniplink-Event-Type- Event type for quick filteringX-Sniplink-Webhook-Id- Unique delivery ID
Verification Example (Node.js)
import crypto from 039;crypto039;;
function verifyWebhookSignature(request, webhookSecret) {
const signature = request.headers[039;x-sniplink-webhook-signature039;];
const timestamp = request.headers[039;x-sniplink-webhook-timestamp039;];
const body = JSON.stringify(request.body);
// Reconstruct signed payload
const signedPayload = `${timestamp}.${body}`;
// Compute HMAC
const hmac = crypto.createHmac(039;sha256039;, webhookSecret);
hmac.update(signedPayload);
const expectedSignature = `v1=${hmac.digest('hex')}`;
// Timing-safe comparison
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Usage in webhook handler
app.post(039;/webhooks039;, (req, res) => {
const isValid = verifyWebhookSignature(req, process.env.WEBHOOK_SECRET);
if (!isValid) {
return res.status(401).json({ error: 039;Invalid signature039; });
}
// Process webhook event
const event = req.body;
console.log(039;Received event:039;, event.type);
res.json({ received: true });
});Timestamp Validation
Reject webhooks with timestamps outside an acceptable window:
function isTimestampValid(timestamp, toleranceSeconds = 300) {
const now = Math.floor(Date.now() / 1000);
const diff = Math.abs(now - timestamp);
return diff <= toleranceSeconds;
}
// In your handler
const timestamp = parseInt(request.headers[039;x-sniplink-webhook-timestamp039;]);
if (!isTimestampValid(timestamp)) {
return res.status(400).json({ error: 039;Timestamp too old or too far in future039; });
}Best Practices
Respond to webhook requests within 10 seconds. Process events asynchronously if needed. Long processing delays can trigger retries.
Use the event
id to track processed events. If you receive the same event multiple times (due to retries), process it only once.SnipLink retries failed deliveries up to 5 times with exponential backoff. Make sure your endpoint can handle duplicate events without side effects.
Webhook URLs must use HTTPS. HTTP endpoints are not supported for security reasons.
Check your webhook delivery logs regularly in the SnipLink dashboard. Failed deliveries may indicate issues with your endpoint.
Return appropriate HTTP status codes: 200 for success, 400 for client errors, 500 for server errors. SnipLink won't retry 400-level errors.
Troubleshooting
Webhooks not being delivered
- Check that your webhook endpoint is publicly accessible via HTTPS
- Verify your endpoint returns 200 OK within 30 seconds
- Check webhook delivery logs in SnipLink dashboard for error details
- Ensure your webhook hasn't been auto-disabled (happens after 10 consecutive failures)
Signature verification failing
- Verify you're using the correct webhook secret
- Check that you're not modifying the request body before verification
- Ensure timestamp is being parsed as an integer, not string
- Verify clock synchronization between your server and SnipLink
Receiving duplicate events
- This is expected behavior - implement idempotency using the event
id - Check if your endpoint is returning errors (triggering retries)
- Verify you're not creating multiple webhooks for the same events
Additional Resources
- Webhooks Hub - Central hub for all webhook documentation
- Webhooks Setup Guide - Create and register webhook endpoints
- Webhook Security - Verify signatures and secure endpoints
- API Reference - Webhooks Endpoints
- Make.com Integration Guide - Use webhooks with Make.com