Skip to content

Webhooks

Webhooks let you react to events in real time — installs, opens, post-install events, and SKAdNetwork postbacks. Send raw JSON to your own endpoint with HMAC signing, or use a built-in template to push rich messages directly to Slack, Discord, or Teams with no code.

  1. Get your webhook URL

    Go to api.slack.com/apps → Create New App → From Scratch. Pick a workspace, then go to Incoming Webhooks → Activate → Add New Webhook to Workspace → pick a channel → copy the URL.

    https://hooks.slack.com/services/T.../B.../xxx
  2. Create the webhook

    Terminal window
    trace webhooks create \
    --url https://hooks.slack.com/services/T.../B.../xxx \
    --template slack
  3. Send a test event

    Terminal window
    trace webhooks test <webhook-id>

    You’ll see a rich message appear in your channel (for templated webhooks) or a JSON POST hit your endpoint (for custom webhooks).

  4. Done! Trace will now fire webhooks whenever installs, opens, or events are recorded for your app.

Webhooks fire for these events:

EventWhen it fires
install.attributedAn install is matched to a click (campaign attribution)
install.organicAn install is recorded with no matching click
open.attributedAn app open is matched to a click (deep link open)
event.recordedA post-install event is tracked via trackEvent()
skan.postback_receivedAn Apple SKAdNetwork postback arrives
test.webhookYou call trace webhooks test

By default, webhooks receive all event types. To subscribe to specific events only:

Terminal window
# Only attributed installs
trace webhooks create \
--url https://hooks.slack.com/services/T.../B.../xxx \
--template slack \
--events install.attributed
# Installs and events
trace webhooks create \
--url https://hooks.slack.com/services/T.../B.../xxx \
--template slack \
--events install.attributed,install.organic,event.recorded

Templates transform webhook payloads into platform-native rich messages with color-coded sidebars, structured fields, and event-specific formatting.

Terminal window
trace webhooks templates
TemplatePlatformFormat
slackSlackBlock Kit with color-coded sidebar
discordDiscordRich embeds with inline fields
teamsMicrosoft TeamsAdaptive Card v1.4

Each event type has a distinct color so you can scan channels at a glance:

EventColorMeaning
install.attributedGreenA campaign drove this install
install.organicGrayOrganic install, no campaign match
open.attributedBlueDeep link open from a campaign
event.recordedPurplePost-install event (purchase, signup, etc.)
skan.postback_receivedAmberApple SKAdNetwork postback
test.webhookSlateTest event

Webhook titles include a (Sandbox) suffix when the event came from a sandbox link. This makes it easy to distinguish test traffic from production data in your channels.

Optionally customize templates with templateConfig:

OptionTypeDescription
mentionTextstringPing users or groups (e.g. @channel for Slack, @everyone for Discord)
accentColorstringOverride 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"}
}'

For your own HTTP endpoints, Trace sends a JSON envelope with HMAC-SHA256 signing.

{
"event": "install.attributed",
"timestamp": "2025-07-15T14:30:00.000+00:00",
"data": {
"installId": "550e8400-...",
"method": "FINGERPRINT",
"campaignId": "summer_sale",
"platform": "IOS",
"appName": "My App"
}
}

Every webhook POST includes an X-Trace-Signature header — an HMAC-SHA256 hex digest of the raw body, signed with your webhook 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);
HeaderDescription
Content-Typeapplication/json
X-Trace-EventEvent type (e.g. install.attributed)
X-Trace-SignatureHMAC-SHA256 hex digest of the raw body

Each app can have multiple webhooks, each with its own URL, event filter, and template.

Terminal window
# List all webhooks
trace webhooks list
# Update a webhook
trace webhooks update <id> --events install.attributed,open.attributed
trace webhooks update <id> --enabled=false
# Delete a webhook
trace webhooks delete <id>
# View delivery history
trace webhooks deliveries
trace webhooks deliveries --event-type install.attributed --limit 10
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 delivery log. Every attempt (success or failure) is queryable via trace webhooks deliveries.

For the full list of event payloads with field-by-field documentation, see the Webhooks API reference.

Example: Slack notifications for attributed installs

Section titled “Example: Slack notifications for attributed installs”

A common setup: get a Slack notification whenever a campaign drives an install.

Terminal window
# Create a Slack webhook for attributed installs only
trace webhooks create \
--url https://hooks.slack.com/services/T.../B.../xxx \
--template slack \
--events install.attributed
# Verify it works
trace webhooks test <webhook-id>

The message will show the attribution method, campaign ID, platform, and deep link path — color-coded green for attributed installs.

For full control, send all events to your own server with HMAC signing:

Terminal window
trace webhooks create \
--url https://api.yourapp.com/webhooks/trace \
--secret whsec_$(openssl rand -hex 20)

Then in your handler, switch on the event type:

app.post('/webhooks/trace', (req, res) => {
// Verify signature first (see above)
const { event, data } = req.body;
switch (event) {
case 'install.attributed':
analytics.track('attributed_install', {
campaign: data.campaignId,
method: data.method,
});
break;
case 'event.recorded':
if (data.eventName === 'purchase_completed') {
crm.tagUser(data.userId, 'paying_customer');
}
break;
}
res.sendStatus(200);
});