Skip to content
Dashboard

Webhooks

A webhook sends an app’s analytics data to your HTTPS endpoint on a recurring schedule. On each run, Detour sends a signed JSON payload.

Each webhook belongs to one app and one export type. You manage an organization’s webhooks in the Webhooks section of the dashboard sidebar.


Webhooks are a paid feature. The number of active webhooks you can run depends on your plan:

PlanActive webhooks
Free0 (unavailable)
Starter3
Scale10
EnterpriseCustom

For full plan details, see Billing & Payments.

  • If you try to create a webhook beyond your active limit, it is saved but created paused (inactive). Activate it later after deactivating another webhook.
  • If you downgrade to a plan with a lower limit, the oldest active webhooks are automatically paused to fit the new limit.

When you create a webhook, you set these fields:

App required

The app whose analytics data is exported.

Endpoint URL required
Type URL

Where Detour sends the payload. It must be a public HTTPS URL on port 443. Detour rejects endpoints that resolve to private or internal addresses.

Export type required
Type Overview | Links | Events

The dataset to send.

  • Overview — aggregated performance, including link stats, organic and non-organic installs, retention, and top events
  • Links — a breakdown by link path and query parameter
  • Events — SDK event activity
Schedule required
Type Daily | Weekly | Monthly

How often Detour sends the payload. See Delivery schedule.

Platform required
Type all | ios | android

The platform whose data is included.

Filters

Optional filters that narrow the exported data.


Deliveries run on a fixed daily cycle (around 08:00 UTC). Each delivery covers the period that has closed:

ScheduleRuns onPeriod covered
DailyEvery dayThe previous day
WeeklyMondaysThe previous 7 days
MonthlyThe 1st of each monthThe previous calendar month

All period boundaries are in UTC.


Each delivery is an HTTP POST with a JSON body:

{
"webhook_id": "<your webhook id>",
"type": "overview | links | events",
"app_id": "<your app id>",
"period": {
"start": "2026-05-01T00:00:00.000Z",
"end": "2026-06-01T00:00:00.000Z"
},
"delivered_at": "2026-06-01T08:00:00.000Z",
"meta": {
"total_rows": 4821,
"returned_rows": 4821,
"truncated": false,
"row_limit": 10000
},
"data": [ /* rows for the selected export type */ ]
}
FieldMeaning
webhook_idThe webhook subscription this delivery came from
typeThe export type, overview, links or events
app_idThe app the exported data belongs to
periodThe window the data covers, as two ISO 8601 UTC timestamps
delivered_atWhen Detour built this delivery, ISO 8601 UTC
metaRow counts for this delivery, described below
dataThe rows, in the shape that matches type

The rows in data are the same rows a manual export of that view returns.

FieldMeaning
total_rowsRows the export matched, before any limit
returned_rowsRows present in data
truncatedtrue when data holds only the first part of the result
row_limitThe per-delivery row cap in force

meta is additive. data keeps its existing shape and position, so an integration that reads only data needs no change.


A single delivery carries at most 10,000 rows. The limit applies to the HTTP body Detour sends, not to your data, so you can still get the full result in other ways.

Check meta.truncated on every delivery. When it is true, data holds the first row_limit rows of a larger result, and meta.total_rows says how many rows there were in total.

When deliveries are truncated, you have three options:

  • Narrow the webhook with filters — fewer clicks are then in scope.
  • Switch to a shorter scheduleDaily rather than Weekly or Monthly, so each delivery covers a smaller window.
  • Pull the full dataset instead — the Platform API supports paging and parameter-level filtering.

A links delivery carries two kinds of row, distinguished by param_key:

  • Path rows (param_key is "") — the totals for one date, short link and path.
  • Parameter rows — the breakdown of a single query parameter value inside that path.

Path rows also carry already_installed_opens, the taps that opened an installed app directly through Universal Links or App Links. These opens are included in clicks:

  • clicks — all opens
  • clicks - already_installed_opens — web opens only

They appear on path rows only, never on parameter rows. Their short_link is empty when the tap was on a plain deep link, or when the open was recorded before Detour began attributing these opens.


Every request includes an X-Webhook-Signature header so you can confirm the payload came from Detour and wasn’t tampered with:

X-Webhook-Signature: sha256=<hex digest>

The digest is an HMAC-SHA256 of the raw request body, keyed with your webhook’s signing secret. Compute the same HMAC on your side and compare:

import { createHmac, timingSafeEqual } from "crypto";
function isValidSignature(rawBody, header, signingSecret) {
const expected = createHmac("sha256", signingSecret)
.update(rawBody)
.digest("hex");
const received = (header ?? "").replace(/^sha256=/, "");
const a = Buffer.from(expected);
const b = Buffer.from(received);
return a.length === b.length && timingSafeEqual(a, b);
}

  • Detour posts only to the configured URL. Redirects are not followed.
  • The endpoint must respond with a 2xx status within 10 seconds for the delivery to count as successful.
  • Failed deliveries are not retried within the same cycle. The next attempt is the next scheduled run. Make sure your endpoint is reachable at delivery time.
  • The dashboard shows the last successful delivery time for each webhook.

In the Webhooks section you can:

  • Pause or resume — Resuming counts toward your plan’s active-webhook limit. At the limit, pause another webhook first.
  • Rotate the signing secret — The new secret is shown once, and the previous one stops working immediately.
  • Delete — Removes the webhook.

You cannot edit a webhook’s URL, schedule, export type or platform. To change them, delete the webhook and create a new one.

The Last attempt column shows when Detour last tried to deliver and whether it succeeded, so you can tell a webhook that never ran from one that keeps failing. On failure, the tooltip on the warning icon gives the reason, for example the HTTP status your endpoint returned.