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.
Availability
Section titled “Availability”Webhooks are a paid feature. The number of active webhooks you can run depends on your plan:
| Plan | Active webhooks |
|---|---|
| Free | 0 (unavailable) |
| Starter | 3 |
| Scale | 10 |
| Enterprise | Custom |
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.
Creating a webhook
Section titled “Creating a webhook”When you create a webhook, you set these fields:
-
Apprequired -
The app whose analytics data is exported.
-
Endpoint URLrequired -
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 typerequired -
The dataset to send.
Overview— aggregated performance, including link stats, organic and non-organic installs, retention, and top eventsLinks— a breakdown by link path and query parameterEvents— SDK event activity
-
Schedulerequired -
How often Detour sends the payload. See Delivery schedule.
-
Platformrequired -
The platform whose data is included.
-
Filters -
Optional filters that narrow the exported data.
Delivery schedule
Section titled “Delivery schedule”Deliveries run on a fixed daily cycle (around 08:00 UTC). Each delivery covers the period that has closed:
| Schedule | Runs on | Period covered |
|---|---|---|
Daily | Every day | The previous day |
Weekly | Mondays | The previous 7 days |
Monthly | The 1st of each month | The previous calendar month |
All period boundaries are in UTC.
Payload format
Section titled “Payload format”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 */ ]}| Field | Meaning |
|---|---|
webhook_id | The webhook subscription this delivery came from |
type | The export type, overview, links or events |
app_id | The app the exported data belongs to |
period | The window the data covers, as two ISO 8601 UTC timestamps |
delivered_at | When Detour built this delivery, ISO 8601 UTC |
meta | Row counts for this delivery, described below |
data | The rows, in the shape that matches type |
The rows in data are the same rows a manual export of that view returns.
| Field | Meaning |
|---|---|
total_rows | Rows the export matched, before any limit |
returned_rows | Rows present in data |
truncated | true when data holds only the first part of the result |
row_limit | The 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.
Row limits
Section titled “Row limits”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 schedule —
Dailyrather thanWeeklyorMonthly, so each delivery covers a smaller window. - Pull the full dataset instead — the Platform API supports paging and parameter-level filtering.
Reading links rows
Section titled “Reading links rows”A links delivery carries two kinds of row, distinguished by param_key:
- Path rows (
param_keyis"") — the totals for one date, short link and path. - Parameter rows — the breakdown of a single query parameter value inside that path.
Already-installed opens
Section titled “Already-installed opens”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 opensclicks - 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.
Verifying the signature
Section titled “Verifying the signature”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);}Delivery behavior
Section titled “Delivery behavior”- 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.
Managing webhooks
Section titled “Managing webhooks”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.