Skip to content
Dashboard

Platform API

The Platform API lets your own backend manage Detour resources and pull analytics without going through the dashboard. It covers three areas:

  • short links and the parameters they carry,
  • the app’s link settings,
  • analytics exports in JSON, CSV or XLSX.

Every endpoint is authenticated with a secret API key. The base URL is https://api.godetour.link.

Each request carries two headers: a secret API key as a Bearer token, and the ID of the app it applies to. A request missing either one is rejected with 401.

Detour provides two types of API keys:

Key TypePurposeUse Case
PublishableIdentify your app, used in SDKsClient-side SDK implementations
SecretFull platform management & administrationServer-side API calls, automation

Secret keys are issued per app, from the API configuration tab of the app you want to manage:

  1. Open the app in the dashboard and go to API configuration.
  2. In the Secret API Key card, click Generate secret key.
  3. Copy the key from the dialog and store it somewhere safe.

The card afterwards shows only whether a key is active and when it was generated. To replace a key, use Regenerate secret key: the previous key is revoked the moment the new one is issued, so any server-side integration still using the old key stops working immediately. Plan the rollout before you regenerate. Generating and regenerating keys requires the Owner or Admin role.

Include both headers in every request:

Authorization: Bearer <SECRET_API_KEY>
X-App-ID: <YOUR_APP_ID>
  • Authorization — the app’s secret key, as a Bearer token.
  • X-App-ID — the app’s ID (a UUID), from the same API configuration tab. A value that is not a UUID is rejected with 401.
Example request
Terminal window
curl -X GET https://api.godetour.link/api/manage/short-links \
-H "Authorization: Bearer 1234567890abc..." \
-H "X-App-ID: your-app-id"

Short links wrap the app’s main link together with a set of custom parameters, which the SDK returns to your app after a deferred or direct open.

GET /api/manage/short-links

Returns every short link belonging to the app, newest first. The endpoint takes no query parameters.

Status codes200, 401, 429, 500

Example request
Terminal window
curl -X GET https://api.godetour.link/api/manage/short-links \
-H "Authorization: Bearer a1b2c3d4e5f6..." \
-H "X-App-ID: my-app"
Example response
[
{
"id": "short_link_uuid",
"hash": "abc123",
"url": "https://acme-corp.godetour.link/abc123",
"parameters": "{\"campaign\":\"summer_sale\"}",
"name": "Summer Sale Campaign",
"link_id": "link_uuid",
"created_at": "2026-06-16T10:30:00.000Z"
}
]

POST /api/manage/short-links

Creates a short link on the app’s main link and returns it, including the public URL.

Request body

{
"parameters": "{\"campaign\":\"summer_sale\",\"utm_source\":\"email\"}",
"name": "Summer Sale Email Campaign"
}

Required fields

parameters required
Type string

JSON string of custom parameters (cannot be empty)

Optional fields

name
Type string

User-friendly name for the short link.

Status codes201, 400, 401, 402, 404, 429, 500

  • 402 — the Free plan limit of 50 short links per app is reached. Paid plans have no limit.
  • 404 — the app has no link yet.
Example request
Terminal window
curl -X POST https://api.godetour.link/api/manage/short-links \
-H "Authorization: Bearer a1b2c3d4e5f6..." \
-H "X-App-ID: my-app" \
-H "Content-Type: application/json" \
-d '{
"parameters": "{\"campaign\":\"summer_sale\"}",
"name": "Summer Sale"
}'
Example response
{
"id": "short_link_uuid",
"hash": "abc123",
"url": "https://acme-corp.godetour.link/abc123",
"parameters": "{\"campaign\":\"summer_sale\",\"utm_source\":\"email\"}",
"name": "Summer Sale Email Campaign",
"link_id": "link_uuid",
"created_at": "2026-06-16T10:35:00.000Z"
}

PATCH /api/manage/short-links

Replaces the parameters, and optionally the name, of an existing short link. The hash and URL stay the same, so links already distributed keep working.

Request body

{
"id": "short_link_uuid",
"parameters": "{\"campaign\":\"fall_sale\"}",
"name": "Fall Sale Campaign"
}

Required fields

id required
Type string

UUID of the short link to update.

parameters required
Type string

Updated JSON string of custom parameters.

Optional fields

name
Type string

Updated user-friendly name.

Status codes200, 400, 401, 404, 429, 500

Example request
Terminal window
curl -X PATCH https://api.godetour.link/api/manage/short-links \
-H "Authorization: Bearer a1b2c3d4e5f6..." \
-H "X-App-ID: my-app" \
-H "Content-Type: application/json" \
-d '{
"id": "short_link_uuid",
"parameters": "{\"campaign\":\"fall_sale\"}",
"name": "Fall Sale Campaign"
}'
Example response
{
"id": "short_link_uuid",
"hash": "abc123",
"url": "https://acme-corp.godetour.link/abc123",
"parameters": "{\"campaign\":\"fall_sale\"}",
"name": "Fall Sale Campaign",
"link_id": "link_uuid",
"created_at": "2026-06-16T10:35:00.000Z"
}

Every app has one main link. Its settings decide where a click is redirected and which parameters are forwarded.

GET /api/manage/links

Returns the app’s main link: its destination, its fallback, and how query parameters are passed through. The endpoint takes no query parameters.

Response fields

id
Type string

UUID of the link.

url
Type string

Primary redirect URL.

redirect_url
Type string

Fallback redirect URL.

params_pass_strategy
Type string

How parameters are passed: "all", "none", or "specific"

specific_params
Type array | null

If set, only these parameters are passed through.

created_at
Type string

Link creation timestamp (ISO 8601)

app_hash
Type string

Hash identifier used in short link URLs.

Status codes200, 401, 404, 429, 500

  • 404 — the app has no link.
Example request
Terminal window
curl -X GET https://api.godetour.link/api/manage/links \
-H "Authorization: Bearer a1b2c3d4e5f6..." \
-H "X-App-ID: my-app"
Example response
{
"id": "link_uuid",
"url": "https://example.com",
"redirect_url": "https://fallback.example.com",
"params_pass_strategy": "all",
"specific_params": null,
"created_at": "2026-06-16T10:00:00.000Z",
"app_hash": "x9y8z7"
}

GET /api/analytics/stats

Exports the same figures the dashboard’s Analytics section shows, over a date range you choose. The response is always sent as a file download, in the format given by format.

Query parameters

type required
Type string

Data type: "overview", "links", or "events"

startDate required
Type string

Start of date range (ISO 8601, for example 2026-06-01T00:00:00Z)

endDate required
Type string

End of date range (ISO 8601, for example 2026-06-30T23:59:59Z)

format
Type string Default json

Export format: "json", "csv" or "xlsx"

platform
Type string Default all

Filter by platform: "all", "ios", or "android"

linkId
Type string

UUID of a specific link to scope the query. Defaults to the main app link. Relevant for "links" and "overview" types, ignored for "events"

limit
Type number | "all" Default 1000

Rows per page, 11000, or "all" for the complete set in one response. type=links and type=events only.

offset
Type number Default 0

Row offset for paging. Not accepted with limit=all. type=links and type=events only.

envelope
Type boolean Default false

format=json only. Wraps the array as { meta, rows } instead of returning a bare array.

paramKeys
Type string

Comma-separated parameter names to keep in the breakdown. type=links only.

excludeParams
Type string

Comma-separated parameter names to drop from the breakdown. type=links only.

Click-level filters are listed separately under Filtering link analytics.

Plan requirements

typeRequired Plan
overviewFree
linksPaid
eventsPaid

Response — one row per day, with the columns for the requested type:

typeColumns
overviewdate, total_clicks, already_installed_opens, fallback_redirects, organic_installs, non_organic_installs, top_event, retention_rate
linksdate, short_link, path, param_key, param_value, clicks, matched_installs, fallbacks, already_installed_opens
eventsdate, dimensions (event_name, platform, is_retention), metrics (event_count, unique_devices)

CSV and XLSX exports flatten dimensions and metrics into plain columns.

A links export mixes two kinds of row:

RowHow to recognize itWhat it holds
Path rowparam_key and param_value are ""Totals for one date, short link and path
Parameter rowparam_key is setOne 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, and filters apply to them. 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.

Summed this way, type=links matches type=overview and the dashboard for the same app, period and platform.

200, 400, 401, 402, 403, 404, 429, 500, 503

  • 400type is missing or invalid, or a filter or pagination parameter is out of range or does not apply to type.
  • 402links and events require a paid plan.
  • 403linkId does not belong to this app.
  • 404 — the app was not found. Checked for links and events only.
  • 503 — the analytics query timed out. Narrow the date range, or drop high-cardinality parameters with excludeParams.

Error bodies on this endpoint carry a machine-readable code alongside error, and sometimes a hint:

{
"error": "Analytics query timed out",
"code": "query_timeout",
"hint": "Narrow the date range, or use paramKeys/excludeParams to drop high-cardinality parameters such as fbclid."
}

type=links and type=events are paged. The default page is 1,000 rows, and the response always reports whether more rows exist, so a truncated result is always signalled.

Every format carries the same metadata in response headers, because CSV and XLSX cannot carry a response envelope:

HeaderMeaning
X-Total-RowsRows in the full result set. Absent when the page is empty.
X-Returned-RowsRows in this response
X-Row-OffsetOffset this page started at
X-Row-LimitPage size used. Absent for limit=all
X-Truncatedtrue when more rows follow
Link<next page url>; rel="next", when more rows follow

All of them are listed in Access-Control-Expose-Headers, so a browser-based consumer reading the API cross-origin can see them.

With envelope=true and format=json the same values also come back in the body:

{
"meta": {
"total_rows": 4821,
"returned_rows": 1000,
"limit": 1000,
"offset": 0,
"truncated": true,
"next_offset": 1000
},
"rows": [...]
}

Follow next_offset, or the Link header, until truncated is false.

limit=all returns the complete set in a single response instead. It is opt-in because a large result takes longer to answer than a page, and it rejects offset.

Two independent filter layers apply to type=links, and they compose. Both are rejected with 400 on type=overview and type=events rather than silently ignored. A dropped filter would read as “no clicks matched”, which is indistinguishable from a real answer.

Row filters: paramKeys and excludeParams. These decide which breakdown rows come back. They never change the click totals on path rows. Use them to keep a high-cardinality parameter such as fbclid from consuming the whole page:

Terminal window
curl -X GET "https://api.godetour.link/api/analytics/stats?type=links&startDate=2026-06-01T00:00:00Z&endDate=2026-06-30T23:59:59Z&excludeParams=fbclid,gclid" \
-H "Authorization: Bearer a1b2c3d4e5f6..." \
-H "X-App-ID: my-app"

Click filters. These decide which clicks are counted, so they do change clicks_count. There are four dimensions, path, param_name, param_kv and param_value, each usable with the operators is, contains and starts_with. Shorthand parameters avoid URL-encoding JSON:

ParameterOperator parameterDimension
filterPathfilterPathOppath
filterParamNamefilterParamNameOpparam_name
filterParamValuefilterParamValueOpparam_value
filterParam.<key>filterParamOpparam_kv

Comma-separated values inside one filter are combined with OR. Separate filter parameters are combined with AND. The operator defaults to is.

Terminal window
curl -X GET "https://api.godetour.link/api/analytics/stats?type=links&startDate=2026-06-01T00:00:00Z&endDate=2026-06-30T23:59:59Z&filterPath=/checkout&filterPathOp=starts_with&filterParam.utm_source=facebook" \
-H "Authorization: Bearer a1b2c3d4e5f6..." \
-H "X-App-ID: my-app"

The same filters can be sent as JSON in a single filters parameter, as an array of { "dimension": { "key": … }, "operator": …, "values": [ … ], "paramKey": … } objects. Use that form when a value contains a comma, which the shorthand cannot express.

At most 20 filters and 200 filter values may be combined in one request.

More example requests

Get overview analytics:

Terminal window
curl -X GET "https://api.godetour.link/api/analytics/stats?type=overview&startDate=2026-06-01T00:00:00Z&endDate=2026-06-30T23:59:59Z" \
-H "Authorization: Bearer a1b2c3d4e5f6..." \
-H "X-App-ID: my-app"

Export link analytics as CSV (paid):

Terminal window
curl -X GET "https://api.godetour.link/api/analytics/stats?type=links&format=csv&startDate=2026-06-01T00:00:00Z&endDate=2026-06-30T23:59:59Z" \
-H "Authorization: Bearer a1b2c3d4e5f6..." \
-H "X-App-ID: my-app" \
-o analytics.csv

Filter by platform:

Terminal window
curl -X GET "https://api.godetour.link/api/analytics/stats?type=events&platform=ios&startDate=2026-06-01T00:00:00Z&endDate=2026-06-30T23:59:59Z" \
-H "Authorization: Bearer a1b2c3d4e5f6..." \
-H "X-App-ID: my-app"

Limits are counted per app, per minute, in separate buckets:

EndpointRequests / minute
GET /api/manage/short-links120
POST /api/manage/short-links60
PATCH /api/manage/short-links60
GET /api/manage/links120
GET /api/analytics/stats30

A 429 response carries Retry-After (seconds), X-RateLimit-Remaining and X-RateLimit-Reset. Wait for Retry-After before retrying.


All errors return a JSON response with an error field. Each endpoint above lists the codes it returns and explains the ones specific to it.

StatusMeaning
400Invalid request body
401Missing or invalid API key / App ID
402Plan limit reached, upgrade required
403Resource belongs to another app
404Resource not found
429Rate limit exceeded, back off and retry
500Server error
503Analytics query timed out