---
title: "Match an app install to a link click"
method: POST
path: "/attribution/match"
tags: ["Attribution"]
---

# Match an app install to a link click

`POST /attribution/match`

## Request

- Base URL: `https://api.warplink.app/v1`
- URL: `https://api.warplink.app/v1/attribution/match`
- Auth: HTTP bearer

## Request body

- union
  - object
    - `fingerprint` string, required: Pre-computed SHA-256 match key as a hex string, case-insensitive. Optional; SDKs use the raw-signals path instead.
    - `platform` 'ios' | 'android', required
    - `fingerprint_version` 'basic' | 'enriched' | 'enriched_tz', required: Which match variant the SDK's signals support. `enriched_tz` uses the IANA zone, `enriched` the minute offset, `basic` neither.
    - `sdk_version` string, required
    - `device_id` string, uuid: The IDFV, sent only by the iOS SDK. Not an install match on its own: it finds an install this device already has and returns it with that install's original match type, confidence, and match_guaranteed, so it never turns a probabilistic match into a guaranteed one. Checked before the probabilistic match, and skipped when is_reinstall is true.
    - `referrer` string, uuid: The WarpLink link ID carried in the Play install referrer as utm_content. Send the parsed link ID, not the raw referrer string. Deterministic match; takes precedence over the probabilistic match.
    - `accept_language` string: Raw device preferred language (e.g. "en-US"). Required for the probabilistic path. The server normalizes it to the primary subtag.
    - `timezone_offset` integer: Timezone offset in minutes, JS getTimezoneOffset() convention (UTC+2 is -120). Sent alongside `timezone`; used as the fallback when no IANA zone is available.
    - `timezone` string: IANA timezone name, e.g. `America/Toronto`. Preferred over `timezone_offset`: it carries far more entropy and does not shift at a daylight-saving boundary. Sent by SDK versions that collect a zone name; older SDKs send the offset only.
    - `app_bundle_id` string: iOS bundle identifier of the calling app. Optional. Without it an install is matched across the whole organization, which is ambiguous once the organization has more than one app.
    - `app_package_name` string: Android package name of the calling app. Optional. Without it an install is matched across the whole organization, which is ambiguous once the organization has more than one app.
    - `is_reinstall` boolean: True when this device already completed WarpLink attribution for this app under an earlier install. A reinstall counts as an install and is attributed again on every platform, and the flag is more than a label: `true` makes the server skip the device ID (IDFV) branch, so the install is matched by the probabilistic tier instead, and it stops the referrer branch from answering with the earlier install's row. Optional, and absent means false: SDKs older than the field never send it.
  - object
    - `fingerprint` string: Pre-computed SHA-256 match key as a hex string, case-insensitive. Optional; SDKs use the raw-signals path instead.
    - `platform` 'ios' | 'android', required
    - `fingerprint_version` 'basic' | 'enriched' | 'enriched_tz', required: Which match variant the SDK's signals support. `enriched_tz` uses the IANA zone, `enriched` the minute offset, `basic` neither.
    - `sdk_version` string, required
    - `device_id` string, uuid: The IDFV, sent only by the iOS SDK. Not an install match on its own: it finds an install this device already has and returns it with that install's original match type, confidence, and match_guaranteed, so it never turns a probabilistic match into a guaranteed one. Checked before the probabilistic match, and skipped when is_reinstall is true.
    - `referrer` string, uuid, required: The WarpLink link ID carried in the Play install referrer as utm_content. Send the parsed link ID, not the raw referrer string. Deterministic match; takes precedence over the probabilistic match.
    - `accept_language` string: Raw device preferred language (e.g. "en-US"). Required for the probabilistic path. The server normalizes it to the primary subtag.
    - `timezone_offset` integer: Timezone offset in minutes, JS getTimezoneOffset() convention (UTC+2 is -120). Sent alongside `timezone`; used as the fallback when no IANA zone is available.
    - `timezone` string: IANA timezone name, e.g. `America/Toronto`. Preferred over `timezone_offset`: it carries far more entropy and does not shift at a daylight-saving boundary. Sent by SDK versions that collect a zone name; older SDKs send the offset only.
    - `app_bundle_id` string: iOS bundle identifier of the calling app. Optional. Without it an install is matched across the whole organization, which is ambiguous once the organization has more than one app.
    - `app_package_name` string: Android package name of the calling app. Optional. Without it an install is matched across the whole organization, which is ambiguous once the organization has more than one app.
    - `is_reinstall` boolean: True when this device already completed WarpLink attribution for this app under an earlier install. A reinstall counts as an install and is attributed again on every platform, and the flag is more than a label: `true` makes the server skip the device ID (IDFV) branch, so the install is matched by the probabilistic tier instead, and it stops the referrer branch from answering with the earlier install's row. Optional, and absent means false: SDKs older than the field never send it.
  - object
    - `fingerprint` string: Pre-computed SHA-256 match key as a hex string, case-insensitive. Optional; SDKs use the raw-signals path instead.
    - `platform` 'ios' | 'android', required
    - `fingerprint_version` 'basic' | 'enriched' | 'enriched_tz', required: Which match variant the SDK's signals support. `enriched_tz` uses the IANA zone, `enriched` the minute offset, `basic` neither.
    - `sdk_version` string, required
    - `device_id` string, uuid, required: The IDFV, sent only by the iOS SDK. Not an install match on its own: it finds an install this device already has and returns it with that install's original match type, confidence, and match_guaranteed, so it never turns a probabilistic match into a guaranteed one. Checked before the probabilistic match, and skipped when is_reinstall is true.
    - `referrer` string, uuid: The WarpLink link ID carried in the Play install referrer as utm_content. Send the parsed link ID, not the raw referrer string. Deterministic match; takes precedence over the probabilistic match.
    - `accept_language` string: Raw device preferred language (e.g. "en-US"). Required for the probabilistic path. The server normalizes it to the primary subtag.
    - `timezone_offset` integer: Timezone offset in minutes, JS getTimezoneOffset() convention (UTC+2 is -120). Sent alongside `timezone`; used as the fallback when no IANA zone is available.
    - `timezone` string: IANA timezone name, e.g. `America/Toronto`. Preferred over `timezone_offset`: it carries far more entropy and does not shift at a daylight-saving boundary. Sent by SDK versions that collect a zone name; older SDKs send the offset only.
    - `app_bundle_id` string: iOS bundle identifier of the calling app. Optional. Without it an install is matched across the whole organization, which is ambiguous once the organization has more than one app.
    - `app_package_name` string: Android package name of the calling app. Optional. Without it an install is matched across the whole organization, which is ambiguous once the organization has more than one app.
    - `is_reinstall` boolean: True when this device already completed WarpLink attribution for this app under an earlier install. A reinstall counts as an install and is attributed again on every platform, and the flag is more than a label: `true` makes the server skip the device ID (IDFV) branch, so the install is matched by the probabilistic tier instead, and it stops the referrer branch from answering with the earlier install's row. Optional, and absent means false: SDKs older than the field never send it.
  - object
    - `fingerprint` string: Pre-computed SHA-256 match key as a hex string, case-insensitive. Optional; SDKs use the raw-signals path instead.
    - `platform` 'ios' | 'android', required
    - `fingerprint_version` 'basic' | 'enriched' | 'enriched_tz', required: Which match variant the SDK's signals support. `enriched_tz` uses the IANA zone, `enriched` the minute offset, `basic` neither.
    - `sdk_version` string, required
    - `device_id` string, uuid: The IDFV, sent only by the iOS SDK. Not an install match on its own: it finds an install this device already has and returns it with that install's original match type, confidence, and match_guaranteed, so it never turns a probabilistic match into a guaranteed one. Checked before the probabilistic match, and skipped when is_reinstall is true.
    - `referrer` string, uuid: The WarpLink link ID carried in the Play install referrer as utm_content. Send the parsed link ID, not the raw referrer string. Deterministic match; takes precedence over the probabilistic match.
    - `accept_language` string, required: Raw device preferred language (e.g. "en-US"). Required for the probabilistic path. The server normalizes it to the primary subtag.
    - `timezone_offset` integer, required: Timezone offset in minutes, JS getTimezoneOffset() convention (UTC+2 is -120). Sent alongside `timezone`; used as the fallback when no IANA zone is available.
    - `timezone` string: IANA timezone name, e.g. `America/Toronto`. Preferred over `timezone_offset`: it carries far more entropy and does not shift at a daylight-saving boundary. Sent by SDK versions that collect a zone name; older SDKs send the offset only.
    - `app_bundle_id` string: iOS bundle identifier of the calling app. Optional. Without it an install is matched across the whole organization, which is ambiguous once the organization has more than one app.
    - `app_package_name` string: Android package name of the calling app. Optional. Without it an install is matched across the whole organization, which is ambiguous once the organization has more than one app.
    - `is_reinstall` boolean: True when this device already completed WarpLink attribution for this app under an earlier install. A reinstall counts as an install and is attributed again on every platform, and the flag is more than a label: `true` makes the server skip the device ID (IDFV) branch, so the install is matched by the probabilistic tier instead, and it stops the referrer branch from answering with the earlier install's row. Optional, and absent means false: SDKs older than the field never send it.

## Response `200`

Attribution result

- union
  - object
    - `matched` true, required
    - `match_type` 'deterministic' | 'probabilistic', required
    - `match_confidence` number, required
    - `match_guaranteed` boolean, required: True only for a deterministic match, which only the Play Install Referrer produces. A device ID answer carries the flag of the install it returns, so an install matched on iOS is always false. It is not a credential: use it, like match_confidence, to choose the destination the attribution result routes to. Authenticate the user before revealing private data or performing sensitive actions, whether or not the match was guaranteed.
    - `link_id` string, uuid, required: The link this install is attributed to. A matched response always names one: an install whose link has since been deleted is reported as no match, never as a match you cannot act on.
    - `deep_link_url` string, nullable, required: The deep link for the platform that asked, or null when that platform has none. Route on `deep_link_url ?? destination_url`.
    - `destination_url` string, uri, required: Where to send the user. Always present on a matched response.
    - `custom_params` object, required
    - `install_id` string, uuid, required
    - `app_id` string, uuid, nullable, required
  - object
    - `matched` false, required

## Other responses

- `400`: Validation error
- `401`: Unauthorized
- `403`: Forbidden. The key is missing the attribution:write scope, or it is an SDK key tied to a different app (code `KEY_APP_MISMATCH`).
- `500`: Internal error
- `503`: Service temporarily unavailable. Retry after the delay in the Retry-After header

---

[API](https://skmtc.dev/warplink/apis/warplink-api.md) · [All operations](https://skmtc.dev/warplink/apis/warplink-api/llms.txt) · [OpenAPI document](https://skmtc.dev/warplink/apis/warplink-api/revisions/2a90ae8c44e8?raw)
