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.
Authentication
Section titled “Authentication”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.
API key types
Section titled “API key types”Detour provides two types of API keys:
| Key Type | Purpose | Use Case |
|---|---|---|
| Publishable | Identify your app, used in SDKs | Client-side SDK implementations |
| Secret | Full platform management & administration | Server-side API calls, automation |
Generating a secret key
Section titled “Generating a secret key”Secret keys are issued per app, from the API configuration tab of the app you want to manage:
- Open the app in the dashboard and go to API configuration.
- In the Secret API Key card, click Generate secret key.
- 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.
Authentication headers
Section titled “Authentication headers”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 with401.
Example request
curl -X GET https://api.godetour.link/api/manage/short-links \ -H "Authorization: Bearer 1234567890abc..." \ -H "X-App-ID: your-app-id"Short links
Section titled “Short links”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.
List short links
Section titled “List short links”GET /api/manage/short-linksReturns every short link belonging to the app, newest first. The endpoint takes no query parameters.
Status codes — 200, 401, 429, 500
Example request
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" }]Create a short link
Section titled “Create a short link”POST /api/manage/short-linksCreates 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
-
parametersrequired -
JSON string of custom parameters (cannot be empty)
Optional fields
-
name -
User-friendly name for the short link.
Status codes — 201, 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
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"}Update a short link
Section titled “Update a short link”PATCH /api/manage/short-linksReplaces 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
-
idrequired -
UUID of the short link to update.
-
parametersrequired -
Updated JSON string of custom parameters.
Optional fields
-
name -
Updated user-friendly name.
Status codes — 200, 400, 401, 404, 429, 500
Example request
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"}Link settings
Section titled “Link settings”Every app has one main link. Its settings decide where a click is redirected and which parameters are forwarded.
Get link settings
Section titled “Get link settings”GET /api/manage/linksReturns 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 -
UUID of the link.
-
url -
Primary redirect URL.
-
redirect_url -
Fallback redirect URL.
-
params_pass_strategy -
How parameters are passed:
"all","none", or"specific" -
specific_params -
If set, only these parameters are passed through.
-
created_at -
Link creation timestamp (ISO 8601)
-
app_hash -
Hash identifier used in short link URLs.
Status codes — 200, 401, 404, 429, 500
404— the app has no link.
Example request
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"}Analytics
Section titled “Analytics”Export analytics
Section titled “Export analytics”GET /api/analytics/statsExports 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
-
typerequired -
Data type:
"overview","links", or"events" -
startDaterequired -
Start of date range (ISO 8601, for example
2026-06-01T00:00:00Z) -
endDaterequired -
End of date range (ISO 8601, for example
2026-06-30T23:59:59Z) -
format -
Export format:
"json","csv"or"xlsx" -
platform -
Filter by platform:
"all","ios", or"android" -
linkId -
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 -
Rows per page,
1–1000, or"all"for the complete set in one response.type=linksandtype=eventsonly. -
offset -
Row offset for paging. Not accepted with
limit=all.type=linksandtype=eventsonly. -
envelope -
format=jsononly. Wraps the array as{ meta, rows }instead of returning a bare array. -
paramKeys -
Comma-separated parameter names to keep in the breakdown.
type=linksonly. -
excludeParams -
Comma-separated parameter names to drop from the breakdown.
type=linksonly.
Click-level filters are listed separately under Filtering link analytics.
Plan requirements
type | Required Plan |
|---|---|
overview | Free |
links | Paid |
events | Paid |
Response — one row per day, with the columns for the requested type:
type | Columns |
|---|---|
overview | date, total_clicks, already_installed_opens, fallback_redirects, organic_installs, non_organic_installs, top_event, retention_rate |
links | date, short_link, path, param_key, param_value, clicks, matched_installs, fallbacks, already_installed_opens |
events | date, dimensions (event_name, platform, is_retention), metrics (event_count, unique_devices) |
CSV and XLSX exports flatten dimensions and metrics into plain columns.
Reading links rows
Section titled “Reading links rows”A links export mixes two kinds of row:
| Row | How to recognize it | What it holds |
|---|---|---|
| Path row | param_key and param_value are "" | Totals for one date, short link and path |
| Parameter row | param_key is set | One 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 opensclicks - 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.
Status codes
Section titled “Status codes”200, 400, 401, 402, 403, 404, 429, 500, 503
400—typeis missing or invalid, or a filter or pagination parameter is out of range or does not apply totype.402—linksandeventsrequire a paid plan.403—linkIddoes not belong to this app.404— the app was not found. Checked forlinksandeventsonly.503— the analytics query timed out. Narrow the date range, or drop high-cardinality parameters withexcludeParams.
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."}Pagination
Section titled “Pagination”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:
| Header | Meaning |
|---|---|
X-Total-Rows | Rows in the full result set. Absent when the page is empty. |
X-Returned-Rows | Rows in this response |
X-Row-Offset | Offset this page started at |
X-Row-Limit | Page size used. Absent for limit=all |
X-Truncated | true 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.
Filtering link analytics
Section titled “Filtering link analytics”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:
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:
| Parameter | Operator parameter | Dimension |
|---|---|---|
filterPath | filterPathOp | path |
filterParamName | filterParamNameOp | param_name |
filterParamValue | filterParamValueOp | param_value |
filterParam.<key> | filterParamOp | param_kv |
Comma-separated values inside one filter are combined with OR. Separate filter
parameters are combined with AND. The operator defaults to is.
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:
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):
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.csvFilter by platform:
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"Rate limits
Section titled “Rate limits”Limits are counted per app, per minute, in separate buckets:
| Endpoint | Requests / minute |
|---|---|
GET /api/manage/short-links | 120 |
POST /api/manage/short-links | 60 |
PATCH /api/manage/short-links | 60 |
GET /api/manage/links | 120 |
GET /api/analytics/stats | 30 |
A 429 response carries Retry-After (seconds), X-RateLimit-Remaining and X-RateLimit-Reset. Wait for Retry-After before retrying.
Error handling
Section titled “Error handling”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.
| Status | Meaning |
|---|---|
400 | Invalid request body |
401 | Missing or invalid API key / App ID |
402 | Plan limit reached, upgrade required |
403 | Resource belongs to another app |
404 | Resource not found |
429 | Rate limit exceeded, back off and retry |
500 | Server error |
503 | Analytics query timed out |