Skip to content

Webhooks

Trace fires an HTTP POST to your webhook endpoints whenever a key event occurs — installs, opens, events, and SKAdNetwork postbacks. Each app can have multiple webhooks, each filtering by event type. Webhooks are available on all plans, including the free tier.

Create, list, update, and delete webhooks via the API or CLI. Each webhook has a URL, signing secret, optional event filter, and can be enabled/disabled independently.

POST /v1/webhooks
Terminal window
# Subscribe to all events
trace webhooks create \
--url https://example.com/webhooks/trace \
--secret whsec_your_secret_here
# Subscribe to specific events only
trace webhooks create \
--url https://example.com/webhooks/installs \
--secret whsec_installs_secret \
--events install.attributed,install.organic
FieldTypeRequiredDescription
urlstringYesWebhook endpoint URL
secretstringWhen no templateHMAC-SHA256 signing secret
eventsstring[]NoEvent types to deliver (empty = all events)
templatestringNoMessage template: slack, discord, or teams
templateConfigobjectNoTemplate options (see Templates)

Response (201 Created):

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://example.com/webhooks/trace",
"events": [],
"enabled": true,
"template": null,
"templateConfig": null,
"createdAt": "2025-07-15T14:30:00.000+00:00"
}
GET /v1/webhooks

Returns all webhooks for the app. The secret field is never included in responses.

Terminal window
trace webhooks list
GET /v1/webhooks/{id}
PATCH /v1/webhooks/{id}
Terminal window
# Change URL
trace webhooks update <id> --url https://example.com/new-hook
# Disable a webhook
trace webhooks update <id> --enabled=false
# Change event filter
trace webhooks update <id> --events install.attributed,open.attributed
FieldTypeDescription
urlstring?New endpoint URL
secretstring?New signing secret
eventsstring[]?New event filter (empty array = all events)
enabledboolean?Enable or disable
DELETE /v1/webhooks/{id}
Terminal window
trace webhooks delete <id>

Returns 204 No Content on success.


Every webhook POST has a JSON body with this structure:

{
"event": "install.attributed",
"timestamp": "2025-07-15T14:30:00.000+00:00",
"data": {
// event-specific fields (see below)
}
}
HeaderDescription
Content-Typeapplication/json
X-Trace-EventEvent type (e.g. install.attributed)
X-Trace-SignatureHMAC-SHA256 hex digest of the raw body, signed with your webhook secret

Verify the X-Trace-Signature header to confirm the request came from Trace. Each webhook has its own signing secret.

import crypto from 'node:crypto';
function verifySignature(body, secret, signature) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
// In your handler:
const body = await request.text();
const sig = request.headers.get('X-Trace-Signature');
if (!verifySignature(body, process.env.WEBHOOK_SECRET, sig)) {
return new Response('Invalid signature', { status: 401 });
}
const event = JSON.parse(body);

PropertyValue
Timeout10 seconds per attempt
Max attempts3
Backoff1s, 5s, 25s (exponential)
SuccessAny 2xx status code

If all 3 attempts fail, the failure is recorded in the audit log. Every attempt (success or failure) is recorded in the delivery log, queryable via the delivery history API.


Fired when an install is recorded with no matching click (organic).

{
"event": "install.organic",
"timestamp": "2025-07-15T14:30:00.000+00:00",
"data": {
"installId": "550e8400-e29b-41d4-a716-446655440000",
"platform": "ANDROID",
"appName": "My App",
"deviceModel": "Pixel 8",
"osVersion": "14",
"locale": "en-US"
}
}
FieldTypeDescription
installIdstringUUID of the install record
platformstringANDROID or IOS
appNamestringApp display name
deviceModelstring?Device model (e.g. Pixel 8, iPhone15,2)
osVersionstring?OS version string
localestring?Device locale (e.g. en-US)

Fired when an install is matched to a click.

{
"event": "install.attributed",
"timestamp": "2025-07-15T14:30:00.000+00:00",
"data": {
"installId": "550e8400-e29b-41d4-a716-446655440000",
"method": "FINGERPRINT",
"campaignId": "summer_sale",
"platform": "IOS",
"appName": "My App",
"shortCode": "abc123",
"deepLinkPath": "/product/123",
"confidence": "0.87",
"clickedAt": "2025-07-15T14:25:00.000+00:00"
}
}
FieldTypeDescription
installIdstringUUID of the install record
methodstringAttribution method (CLICK_ID, FINGERPRINT, INSTALL_REFERRER)
campaignIdstring?Campaign ID from the matched click (null if none)
platformstringANDROID or IOS
appNamestringApp display name
shortCodestring?Short link code that was clicked
deepLinkPathstring?Deep link path from the matched click
confidencestring?Fingerprint match confidence score
clickedAtstring?When the matched click occurred

Fired when a deferred deep link open is matched to a click.

{
"event": "open.attributed",
"timestamp": "2025-07-15T14:30:00.000+00:00",
"data": {
"openId": "550e8400-e29b-41d4-a716-446655440000",
"method": "CLICK_ID",
"campaignId": "summer_sale",
"deepLinkPath": "/product/123",
"appName": "My App",
"shortCode": "abc123",
"deepLinkParams": "{\"ref\":\"hero\"}",
"confidence": null
}
}
FieldTypeDescription
openIdstringUUID of the open event record
methodstringAttribution method (CLICK_ID or FINGERPRINT)
campaignIdstring?Campaign ID from the matched click
deepLinkPathstring?Deep link path from the matched click
appNamestringApp display name
shortCodestring?Short link code that was clicked
deepLinkParamsstring?Deep link parameters JSON
confidencestring?Fingerprint match score (null for CLICK_ID)

Fired when a post-install event is recorded.

{
"event": "event.recorded",
"timestamp": "2025-07-15T14:30:00.000+00:00",
"data": {
"eventId": "550e8400-e29b-41d4-a716-446655440000",
"installId": "660e8400-e29b-41d4-a716-446655440000",
"eventName": "purchase",
"properties": "{\"amount\": \"9.99\", \"currency\": \"USD\"}",
"appName": "My App",
"platform": "android",
"userId": "user_abc123"
}
}
FieldTypeDescription
eventIdstringUUID of the event record
installIdstringUUID of the associated install
eventNamestringName of the event
propertiesstringJSON-encoded key-value properties
appNamestringApp display name
platformstringInstall platform
userIdstring?Linked user ID (if identify() was called)

Fired when an Apple SKAdNetwork postback is received.

{
"event": "skan.postback_received",
"timestamp": "2025-07-15T14:30:00.000+00:00",
"data": {
"appName": "My App",
"version": "4.0",
"ad-network-id": "example.skadnetwork",
"campaign-id": "42",
"transaction-id": "a1b2c3d4",
"source-app-id": "123456789",
"fidelity-type": 1,
"did-win": true,
"conversion-value": 63,
"postback-sequence-index": 0
}
}

The data field contains the raw Apple postback object with appName prepended.

Fired when you use the test endpoint. Useful for verifying your integration.

{
"event": "test.webhook",
"timestamp": "2025-07-15T14:30:00.000+00:00",
"data": {
"message": "Test webhook from Trace",
"appId": "550e8400-e29b-41d4-a716-446655440000",
"webhookId": "660e8400-e29b-41d4-a716-446655440000",
"appName": "My App"
}
}

Templates transform webhook payloads into platform-native rich messages. When a template is set, the raw JSON envelope is replaced with the platform’s native message format (Slack Block Kit, Discord Embeds, or Teams Adaptive Cards). HMAC signing is skipped for templated webhooks.

IDPlatformDescription
slackSlackBlock Kit messages with color-coded sidebars
discordDiscordRich embeds with color-coded sidebars
teamsMicrosoft TeamsAdaptive Card v1.4 messages
GET /v1/webhooks/templates

Returns the list of available templates:

[
{"id": "slack", "name": "Slack", "description": "Rich messages using Slack Block Kit with color-coded sidebars"},
{"id": "discord", "name": "Discord", "description": "Rich embeds with color-coded sidebars for Discord webhooks"},
{"id": "teams", "name": "Microsoft Teams", "description": "Adaptive Card messages for Microsoft Teams incoming webhooks"}
]

When using a template, secret is not required:

Terminal window
# Slack webhook
trace webhooks create \
--url https://hooks.slack.com/services/T.../B.../xxx \
--template slack
# Discord webhook
trace webhooks create \
--url https://discord.com/api/webhooks/123/abc \
--template discord

Optional templateConfig object for customization:

FieldTypeDescription
mentionTextstring?Mention/ping text (e.g. @channel for Slack, @everyone for Discord)
accentColorstring?Override the default event color (hex, e.g. #ff0000)
Terminal window
curl -X POST https://api.traceclick.io/v1/webhooks \
-H "X-Api-Key: tr_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.slack.com/services/T.../B.../xxx",
"template": "slack",
"templateConfig": {"mentionText": "@channel"}
}'

Each event type has a default accent color used for sidebars/embeds:

EventColor
install.attributedGreen (#16a34a)
install.organicGray (#6b7280)
open.attributedBlue (#2563eb)
event.recordedPurple (#9333ea)
skan.postback_receivedAmber (#f59e0b)
test.webhookSlate (#64748b)

POST /v1/webhooks/{id}/test

Send a test.webhook event to a specific webhook endpoint. The request is delivered synchronously so you get immediate feedback.

Requires: Secret key (tr_live_*)

{
"success": true
}

Returns 200 on success, 502 if delivery failed, or 404 if webhook not found.

Terminal window
trace webhooks test <webhook-id>

GET /v1/webhooks/deliveries

Query recent webhook delivery attempts for your app.

Requires: Secret key (tr_live_*)

ParameterTypeDefaultDescription
limitint25Max results (1–100)
event_typestringFilter by event type (e.g. install.attributed)
webhook_idstringFilter by webhook ID
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"app_id": "660e8400-e29b-41d4-a716-446655440000",
"webhook_id": "770e8400-e29b-41d4-a716-446655440000",
"event_type": "install.attributed",
"webhook_url": "https://example.com/webhooks/trace",
"request_body": "{\"event\":\"install.attributed\",\"timestamp\":\"...\",\"data\":{...}}",
"http_status": 200,
"response_body": "OK",
"error_message": null,
"attempt_number": 1,
"success": true,
"duration_ms": 142,
"created_at": "2025-07-15T14:30:00.000+00:00"
}
]
Terminal window
# Recent deliveries
trace webhooks deliveries --limit 10
# Filter by event type
trace webhooks deliveries --event-type install.attributed
# Filter by webhook ID
trace webhooks deliveries --webhook-id <webhook-id>