REST API reference

Every public endpoint with parameters, request bodies, responses and auth — generated from the same source as the OpenAPI document.

Updated 2026-09-02

Tip

Machine-readable version: https://app.whichclick.is/openapi.json (OpenAPI 3.1). Import it into Postman, Insomnia, Stainless or an agent's tool loader. The MCP server exposes the same operations as tools — see MCP server.

Base URL for everything except redirects: https://app.whichclick.is. Redirects and the pixel run on your tracking domain. Authentication is described in Authentication & limits.

Redirects#

GET /c/{slug}#

Certified click redirect · Auth: None · Host: your tracking domain

Google Ads transparent click tracker endpoint. Reads the next hop from the visible url parameter, appends exactly one first-party click id and answers with a single 302. No HTML, JavaScript or cookies; safe for parallel tracking.

ParameterInRequiredDescription
slugpathyesTracking link slug
urlqueryyesEscaped landing-page URL ({escapedlpurl}); aliases u, redirect, dest, r. May be single-, double- or triple-encoded.
force_transparentqueryyestrue — the server must follow url and may not substitute a backend destination. 400 when set without url.
gclidquerynoValueTrack {gclid} — captured on the click
gbraidquerynoValueTrack {gbraid} — captured on the click
wbraidquerynoValueTrack {wbraid} — captured on the click
campaignidquerynoValueTrack {campaignid} — captured on the click
adgroupidquerynoValueTrack {adgroupid} — captured on the click
creativequerynoValueTrack {creative} — captured on the click
keywordquerynoValueTrack {keyword} — captured on the click
matchtypequerynoValueTrack {matchtype} — captured on the click
networkquerynoValueTrack {network} — captured on the click
devicequerynoValueTrack {device} — captured on the click
placementquerynoValueTrack {placement} — captured on the click
targetidquerynoValueTrack {targetid} — captured on the click
loc_physical_msquerynoValueTrack {loc_physical_ms} — captured on the click
loc_interest_msquerynoValueTrack {loc_interest_ms} — captured on the click
adpositionquerynoValueTrack {adposition} — captured on the click

Responses:

  • 302 Redirect to the landing page with ?wc_click_id=<id> appended. Headers: Location: https://landing.example/?gclid=…&wc_click_id=k3Zp9Qw1mR7tXc2b, Cache-Control: no-store
  • 400 force_transparent=true without a url, or an unsafe (non-http) destination.
    • error required: string | integer — Error code or Zod validation summary
    • retry_after: integer — Seconds to wait (429 only)
  • 404 Unknown slug, paused link, or hostname not bound to this link.

GET /l/{slug}#

Smart link · Auth: None · Host: your tracking domain

Evaluates the link's routing rules (geo, OS, IP type, in-app browser, params, time …) and redirects to a store, deep link, web fallback or A/B variant. Android store redirects carry referrer=wc_click_id=… for the Install Referrer API. A visible url= parameter always overrides the rules (Google transparency).

ParameterInRequiredDescription
slugpathyesSmart link slug
urlquerynoTransparent override — behaves like /c/{slug}
*querynoAny query parameter is available to rules as param.<name> and passed through per link settings

Responses:

  • 200 HTML interstitial (custom-scheme fallback for in-app browsers), QR page on desktop, or Open Graph preview for social crawlers.
  • 302 Redirect (store, deep link, web fallback, split variant).
  • 403 Rule action block (also 404/410 per rule).
  • 410 Link expired or click cap reached.

GET /i/{campaign}#

Impression pixel · Auth: None · Host: your tracking domain

1×1 GIF for display, video (VAST <Impression>) and CTV tags. Records an impression (IP hash + household hash, no cookies) for view-through and household attribution.

ParameterInRequiredDescription
campaignpathyesCampaign id or ad-platform external id
appquerynoApp id
crquerynoCreative id
plquerynoPlacement
pquerynoPartner / network (e.g. roku, dv360)

Responses:

  • 200 image/gif, Cache-Control: no-store

Conversions#

GET /postback#

Inbound postback (GET) · Auth: API key (Bearer)

Server-to-server conversion postback. Authenticate with Authorization: Bearer or api_key query parameter. click_id or gclid is required. POST with the same query parameters is accepted too.

ParameterInRequiredDescription
api_keyquerynoAPI key when the Authorization header cannot be set
click_idquerynowc_click_id value
gclidquerynoAlternative to click_id
eventquerynoEvent name (default conversion)
valuequerynoConversion value
currencyquerynoISO 4217
order_idquerynoIdempotency key with event

Responses:

  • 200 Stored (matched or unattributed).
200 response
{
  "ok": true,
  "id": "clx8n2…",
  "matched": true
}
  • 400 Validation error or neither click_id nor gclid.
    • error required: string | integer — Error code or Zod validation summary
    • retry_after: integer — Seconds to wait (429 only)
  • 401 Missing / revoked API key.
    • error required: string | integer — Error code or Zod validation summary
    • retry_after: integer — Seconds to wait (429 only)

POST /api/v1/conversions#

Create conversion · Auth: API key (Bearer)

JSON variant of the postback. Idempotent on order_id + event (the value/currency are updated on repeats). Matching: click_id first, else latest click with the same gclid; unmatched conversions are stored with matched: false. Outbound postbacks fan out asynchronously.

Request body:

  • click_id: string — WhichClick click id (wc_click_id appended to the landing page)
  • gclid: string — Google click id — used when click_id is unknown; matched to the most recent click
  • event: string — Event name
  • value: number — Conversion value
  • currency: string — ISO 4217 currency
  • order_id: string — Idempotency key together with event
Example request
{
  "click_id": "k3Zp9Qw1mR7tXc2b",
  "event": "purchase",
  "value": 49.9,
  "currency": "USD",
  "order_id": "ORD-1001"
}

Responses:

  • 201 Created / updated.
201 response
{
  "ok": true,
  "id": "clx8n2…",
  "matched": true
}
  • 400 Validation error.
    • error required: string | integer — Error code or Zod validation summary
    • retry_after: integer — Seconds to wait (429 only)
  • 401 Unauthorized.
    • error required: string | integer — Error code or Zod validation summary
    • retry_after: integer — Seconds to wait (429 only)

GET /api/v1/conversions#

List conversions · Auth: API key (Bearer)

Most recent conversions for the organization.

ParameterInRequiredDescription
limitqueryno1–1000 (default 100)

Responses:

  • 200 OK

GET /api/v1/clicks#

Export clicks · Auth: API key (Bearer)

Click facts (ValueTrack fields, next hop, transparency flags) for offline conversion import or auditing. Newest first.

ParameterInRequiredDescription
limitqueryno1–1000 (default 100)
sincequerynoISO timestamp lower bound
gclidquerynoFilter by gclid

Responses:

  • 200 OK
200 response
{
  "data": [
    {
      "id": "k3Zp9Qw1mR7tXc2b",
      "createdAt": "2026-09-02T09:15:00.000Z",
      "gclid": "Cj0KCQjw…",
      "campaignExtId": "2098765432",
      "adGroupExtId": "1234567",
      "creativeId": "7654321",
      "keyword": "running shoes",
      "matchType": "e",
      "network": "g",
      "device": "m",
      "nextHopUrl": "https://shop.example/?gclid=…",
      "transparentParamPresent": true,
      "forceTransparent": true,
      "isBot": false,
      "latencyMs": 4
    }
  ]
}

POST /api/v1/costs#

Import daily spend · Auth: API key (Bearer)

Bulk upsert of daily media cost per campaign (enables CPI / ROAS). Rows referencing unknown campaigns are skipped; the response counts upserts.

Request body:

  • rows required: array<CostRow>
Example request
{
  "rows": [
    {
      "campaign_external_id": "2098765432",
      "day": "2026-09-01",
      "spend": 1240.5,
      "currency": "USD",
      "impressions": 91000
    }
  ]
}

Responses:

  • 200 OK
200 response
{
  "ok": true,
  "upserted": 1
}

Growth#

POST /api/v1/impressions#

Log impressions (S2S) · Auth: API key (Bearer)

Server-to-server impression log for view-through and household (CTV) attribution. IPs are hashed with the day; /24 (IPv4) and /48 (IPv6) household hashes are derived.

Request body:

  • impressions required: array<Impression>
Example request
{
  "impressions": [
    {
      "campaign_external_id": "2098765432",
      "partner": "dv360",
      "creative_id": "cr-1",
      "ip": "203.0.113.7",
      "os": "android",
      "country": "TR",
      "ts": "2026-09-02T09:15:00Z"
    }
  ]
}

Responses:

  • 200 OK

POST /api/v1/tv-airings#

Import TV airings · Auth: API key (Bearer)

Linear TV post-log import. Attribution runs immediately: baseline traffic before each spot vs. the spike inside window_min minutes after it.

Request body:

  • airings required: array<TvAiring>
Example request
{
  "airings": [
    {
      "network": "ATV",
      "program": "Evening news",
      "region": "TR",
      "aired_at": "2026-09-01T19:32:00+03:00",
      "spot_length_sec": 30,
      "cost": 8500,
      "campaign_external_id": "tv-sep",
      "window_min": 8
    }
  ]
}

Responses:

  • 200 OK
    • accepted: integer
    • attributed_airings: integer

GET /.well-known/attribution-reporting/register-source#

Privacy Sandbox: register source · Auth: None · Host: your tracking domain

Responds with an Attribution-Reporting-Register-Source header for Android Privacy Sandbox (MeasurementManager.registerSource) and Chrome attributionsrc tags.

ParameterInRequiredDescription
linkquerynoSmart-link or tracking-link slug
campaignquerynoCampaign id

Responses:

  • 200 Empty body; registration header set.

GET /.well-known/attribution-reporting/register-trigger#

Privacy Sandbox: register trigger · Auth: None · Host: your tracking domain

Responds with an Attribution-Reporting-Register-Trigger header for a conversion event.

ParameterInRequiredDescription
eventquerynoEvent name
valuequerynoValue

Responses:

  • 200 Empty body; trigger header set.

POST /.well-known/attribution-reporting/report-event-attribution#

Privacy Sandbox: reports · Auth: None · Host: your tracking domain

Receives event-level reports. report-aggregate-attribution receives aggregatable reports and debug/* receives debug reports on the same prefix.

Request body:

object

Responses:

  • 200 Stored.

SDK#

POST /api/sdk/v1/install#

Report install · Auth: SDK key (X-SDK-Key)

Called once per install on first open (SDKs retry with backoff). Runs the attribution waterfall and returns the deferred deep link. Re-attribution after reattributionDays and reinstall detection are handled here.

Request body:

  • device_id required: string — SHA-256 of IDFV / GAID / app-set id (raw ids are hashed server-side)
  • platform required: string (ios | android | tvos | android_tv | roku | fire_tv | samsung_tv | lg_tv | windows | macos | playstation | xbox | nintendo | web)
  • os_version: string
  • app_version: string
  • sdk_version: string
  • click_id: string | null — wc_click_id from the launch deep link
  • install_referrer: string | null — Raw Android Install Referrer string
  • gclid: string | null
  • installed_at: string — ISO timestamp
  • locale: string
  • timezone: string
  • advertising_id: string | null — Only with ATT / consent; forwarded to Google's app conversion API, never stored
  • limit_ad_tracking: boolean
  • apple_attribution_token: string | null — AdServices attribution token (iOS 14.3+)
  • ttclid: string | null — TikTok click id
  • sc_click_id: string | null — Snap click id
Example request
{
  "device_id": "2f7c…sha256",
  "platform": "ios",
  "os_version": "17.5",
  "app_version": "1.2.0",
  "sdk_version": "1.0.0",
  "click_id": "k3Zp9Qw1mR7tXc2b",
  "installed_at": "2026-09-02T09:20:00Z",
  "locale": "en_US",
  "timezone": "Europe/Istanbul"
}

Responses:

  • 200 Existing install (idempotent repeat).
    • install_id: string
    • attribution: object
    • is_reattribution: boolean
  • 201 New install attributed.
201 response
{
  "install_id": "ins_01…",
  "attribution": {
    "method": "CLICK_ID",
    "campaign": "Brand — Search",
    "click_id": "k3Zp9Qw1mR7tXc2b",
    "deep_link_path": "/product/123"
  },
  "is_reattribution": false
}
  • 400 Validation error or platform mismatch for the SDK key.
  • 401 Invalid SDK key.

POST /api/sdk/v1/events#

Send in-app events · Auth: SDK key (X-SDK-Key)

Batched in-app events (max 100 per call). session_start powers retention; purchase with value + currency powers LTV / ROAS. Events before an install call are stored without an install link.

Request body:

  • device_id required: string
  • install_id: string | null
  • events required: array<- name required: string — e.g. purchase, session_start
  • value: number | null
  • currency: string | null
  • params: object | null
  • occurred_at: string — ISO timestamp>
Example request
{
  "device_id": "2f7c…sha256",
  "install_id": "ins_01…",
  "events": [
    {
      "name": "purchase",
      "value": 49.9,
      "currency": "USD",
      "params": {
        "sku": "abc"
      },
      "occurred_at": "2026-09-02T09:25:00Z"
    }
  ]
}

Responses:

  • 200 OK

POST /api/sdk/v1/open#

Re-engagement open · Auth: SDK key (X-SDK-Key)

App opened by an existing user via deep link / universal link / push. Attributes the re-open to a retargeting click (click_id or click id parsed from url).

Request body:

  • device_id required: string
  • click_id: string | null
  • url: string | null — The deep link that opened the app
  • occurred_at: string
Example request
{
  "device_id": "2f7c…sha256",
  "url": "https://link.yourbrand.com/product/123?wc_click_id=abc"
}

Responses:

  • 200 OK
    • reengagement_id: string
    • attributed: boolean
    • campaign_id: string | null
    • deep_link_path: string | null
  • 404 Install not found — call /install first.

POST /api/sdk/v1/push-token#

Register push token · Auth: SDK key (X-SDK-Key)

Stores the APNs / FCM token on the install so the uninstall checker can send silent pushes.

Request body:

  • device_id required: string
  • push_token required: string — APNs device token or FCM registration token
  • platform required: string (ios | android | tvos | android_tv | fire_tv)

Responses:

  • 200 OK
  • 404 Install not found.

POST /api/sdk/v1/subscription#

Link subscription · Auth: SDK key (X-SDK-Key)

Links a StoreKit / Play purchase to the install so store-side renewals, cancellations and refunds (received via the store webhooks) are attributed. Records a trial_start or subscribe event. De-duplicated on (store, original_transaction_id).

Request body:

  • device_id required: string
  • install_id: string | null
  • store required: string (apple | google)
  • original_transaction_id: string | null — StoreKit 2 Transaction.originalID
  • purchase_token: string | null — Play Billing purchaseToken
  • product_id required: string
  • is_trial: boolean
  • price_micros: integer — Price × 1,000,000
  • currency: string | null
  • expires_at: string | null
  • occurred_at: string | null
Example request
{
  "device_id": "2f7c…sha256",
  "install_id": "ins_01…",
  "store": "apple",
  "original_transaction_id": "2000000123456789",
  "product_id": "pro_monthly",
  "is_trial": false,
  "price_micros": 9990000,
  "currency": "USD",
  "expires_at": "2026-10-02T09:25:00Z"
}

Responses:

  • 200 OK
    • subscription_id: string
    • install_id: string | null
    • linked: boolean
    • created: boolean

POST /api/sdk/v1/ad-revenue#

Send ad revenue · Auth: SDK key (X-SDK-Key)

Impression-level ad revenue (AdMob paid events, MAX / ironSource ARM). Stored as ad_revenue events so it flows into LTV / ROAS.

Request body:

  • device_id required: string
  • install_id: string | null
  • events required: array<- network required: string — admob | max | ironsource | …
  • ad_unit: string | null
  • format: string | null — banner | interstitial | rewarded | native
  • placement: string | null
  • revenue required: number — Currency units (AdMob micros ÷ 1,000,000)
  • currency: string
  • precision: string | null
  • source: string | null
  • ts: string | null>
Example request
{
  "device_id": "2f7c…sha256",
  "events": [
    {
      "network": "admob",
      "ad_unit": "ca-app-pub-1/2",
      "format": "interstitial",
      "placement": "level_end",
      "revenue": 0.0125,
      "currency": "USD",
      "precision": "ESTIMATED",
      "ts": "2026-09-02T09:30:00Z"
    }
  ]
}

Responses:

  • 200 OK
    • accepted: integer
    • install_id: string | null

Webhooks#

POST /api/skan/postback#

SKAdNetwork postback · Auth: None

Apple SKAdNetwork 2.2 – 4 postback ingestion. Point NSAdvertisingAttributionReportEndpoint at your WhichClick domain or forward copies from your ad network. Signature verified when Apple's public key is configured.

ParameterInRequiredDescription
orgqueryyesOrganization id

Request body:

  • version required: string — 2.2 – 4.0
  • ad-network-id required: string
  • transaction-id required: string
  • app-id: string | integer
  • campaign-id: string | integer
  • source-identifier: string | integer
  • conversion-value: integer
  • coarse-conversion-value: string
  • fidelity-type: integer
  • did-win: boolean
  • source-app-id: string | integer
  • postback-sequence-index: integer
  • attribution-signature: string

Responses:

  • 200 OK
    • ok: boolean
    • id: string
  • 400 Missing org or invalid body.

POST /api/subscriptions/apple#

App Store Server Notifications v2 · Auth: None

Receives JWS signedPayload notifications; the certificate chain is verified against Apple's root. Renewals, cancellations, refunds, grace periods and billing retries update the subscription and emit events.

Request body:

  • signedPayload required: string — JWS

Responses:

  • 200 Processed.
  • 400 Invalid JWS.

POST /api/subscriptions/google#

Google Play RTDN · Auth: None

Pub/Sub push endpoint for Real-time Developer Notifications. The base64 message.data contains the subscriptionNotification; purchase state is fetched via the Play Developer API when credentials are configured.

Request body:

  • message: object
  • subscription: string

Responses:

  • 200 Processed.

POST /api/protect/beacon#

Engagement beacon · Auth: None

Sent by wc.js from the landing page (sendBeacon; JSON or text/plain). Adds dwell time, scroll depth, webdriver flag, screen / timezone / language consistency and fingerprint reuse to the click's risk score.

Request body:

  • click_id required: string
  • fingerprint: string — Client fingerprint hash
  • screen: string — e.g. 390x844
  • timezone: string
  • languages: string
  • webdriver: boolean
  • touch: boolean
  • dwell_ms: integer
  • scroll: integer — Max scroll depth %

Responses:

  • 200 OK
  • 404 Unknown click id.

POST /api/ingest/clicks#

Edge batch ingest · Auth: HMAC signature

Batch ingest from the Cloudflare edge worker. Body signed with HMAC-SHA256 (X-WC-Signature) using EDGE_INGEST_SECRET. Up to 1000 pre-built click rows per call.

Request body:

  • events required: array<object>

Responses:

  • 200 OK
  • 401 Bad signature.

Dashboard#

GET /api/protect/export#

Invalid-click evidence export · Auth: Dashboard session

CSV of invalid clicks (IP, reasons, score, gclid, campaign) for a Google Ads invalid-activity investigation / refund claim.

ParameterInRequiredDescription
daysquerynoDefault 30

Responses:

  • 200 text/csv

POST /api/protect/sync#

Sync IP exclusions to Google Ads · Auth: Dashboard session

Pushes the blocklist as negative IP criteria to every active campaign (500 per campaign cap).

Responses:

  • 200 Sync summary.

GET /api/dashboard/realtime#

Real-time stats · Auth: Dashboard session

Last-15-minute click / install / conversion counters and stream.

Responses:

  • 200 OK

GET /api/dashboard/export#

Report export (CSV) · Auth: Dashboard session

Pivot / clicks / conversions CSV as shown in Reports.

ParameterInRequiredDescription
typequerynoclicks | conversions | pivot

Responses:

  • 200 text/csv

POST /api/dashboard/route-test#

Routing simulator · Auth: Dashboard session

Evaluates a smart link for a simulated visitor and returns the decision (same engine as the MCP test_smart_link_route tool).

Request body:

  • slug required: string
  • country: string
  • region: string
  • city: string
  • os: string — ios | android | desktop
  • ua: string
  • ip: string
  • language: string
  • timezone: string
  • hour: integer
  • params: string — Query string
  • inApp: string — 'true' to simulate an in-app browser

Responses:

  • 200 { context, decision, pool }

GET /api/dashboard/ippool/lookup#

IP lookup · Auth: Dashboard session

Pool classification, geo, rate-limit key, exclusion form and live intel for an IP.

ParameterInRequiredDescription
ipqueryyes

Responses:

  • 200 OK

GET /api/edge/links/hot#

Hot links for edge warm-up · Auth: API key (Bearer)

Top links by clicks in the last 24 h; the edge cron uses it to warm KV.

Responses:

  • 200 Array of link configs.

AI#

POST /api/mcp#

MCP server (Streamable HTTP) · Auth: API key (Bearer)

Model Context Protocol endpoint (JSON-RPC 2.0 over HTTP, stateless). Without an API key only the documentation tools are exposed; with Authorization: Bearer wc_live_… the analytics and management tools are available. See the MCP server page.

Request body:

  • jsonrpc required: string
  • id: string | integer
  • method required: string — initialize | tools/list | tools/call | resources/list | resources/read | prompts/list | prompts/get | ping
  • params: object
Example request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "search_docs",
    "arguments": {
      "query": "tracking template"
    }
  }
}

Responses:

  • 200 JSON-RPC response.
  • 202 Notification accepted (no body).
  • 401 Key required for this tool.

Meta#

GET /api/health#

Health · Auth: None

Liveness + database round-trip.

Responses:

  • 200 OK
200 response
{
  "status": "ok",
  "db": "ok",
  "latencyMs": 1,
  "version": "0.1.0"
}

Schemas#

Error#

  • error required: string | integer — Error code or Zod validation summary
  • retry_after: integer — Seconds to wait (429 only)

ConversionInput#

  • click_id: string — WhichClick click id (wc_click_id appended to the landing page)
  • gclid: string — Google click id — used when click_id is unknown; matched to the most recent click
  • event: string — Event name
  • value: number — Conversion value
  • currency: string — ISO 4217 currency
  • order_id: string — Idempotency key together with event

ConversionResult#

  • ok required: boolean — Always true
  • id required: string — Conversion id
  • matched required: boolean — True when a click was found

Click#

  • id: string — Click id
  • createdAt: string — ISO timestamp
  • gclid: string | null
  • gbraid: string | null
  • wbraid: string | null
  • campaignExtId: string | null — {campaignid}
  • adGroupExtId: string | null — {adgroupid}
  • creativeId: string | null — {creative}
  • keyword: string | null — {keyword}
  • matchType: string | null — e | p | b | a
  • network: string | null — g | s | d | ytv | yts
  • device: string | null — m | t | c
  • nextHopUrl: string — Destination the visitor was sent to
  • transparentParamPresent: boolean — A visible url= parameter was present
  • forceTransparent: boolean — force_transparent=true was present
  • isBot: boolean — Verified search-engine bot
  • latencyMs: integer | null — Redirect latency

Conversion#

  • id: string
  • createdAt: string
  • clickId: string | null
  • campaignId: string | null
  • eventName: string
  • value: number | null
  • currency: string | null
  • orderId: string | null
  • gclid: string | null
  • source: string — postback | api | import

CostRow#

  • campaign_id: string — WhichClick campaign id
  • campaign_external_id: string — Ad-platform campaign id (alternative to campaign_id)
  • day required: string — YYYY-MM-DD
  • spend required: number
  • currency: string
  • impressions: integer

Impression#

  • campaign_id: string
  • campaign_external_id: string
  • app_id: string
  • partner: string
  • creative_id: string
  • placement: string
  • ip required: string — Viewer IP (hashed server-side)
  • user_agent: string
  • os: string
  • device_type: string
  • country: string — ISO-3166 alpha-2
  • ts: string — ISO timestamp

TvAiring#

  • network required: string
  • program: string
  • region: string
  • aired_at required: string — ISO timestamp
  • spot_length_sec: integer
  • cost: number
  • campaign_external_id: string
  • campaign_id: string
  • window_min: integer — Attribution window after the spot (2–60)

InstallRequest#

  • device_id required: string — SHA-256 of IDFV / GAID / app-set id (raw ids are hashed server-side)
  • platform required: string (ios | android | tvos | android_tv | roku | fire_tv | samsung_tv | lg_tv | windows | macos | playstation | xbox | nintendo | web)
  • os_version: string
  • app_version: string
  • sdk_version: string
  • click_id: string | null — wc_click_id from the launch deep link
  • install_referrer: string | null — Raw Android Install Referrer string
  • gclid: string | null
  • installed_at: string — ISO timestamp
  • locale: string
  • timezone: string
  • advertising_id: string | null — Only with ATT / consent; forwarded to Google's app conversion API, never stored
  • limit_ad_tracking: boolean
  • apple_attribution_token: string | null — AdServices attribution token (iOS 14.3+)
  • ttclid: string | null — TikTok click id
  • sc_click_id: string | null — Snap click id

InstallResponse#

  • install_id: string
  • attribution: object
  • is_reattribution: boolean

EventsRequest#

  • device_id required: string
  • install_id: string | null
  • events required: array<- name required: string — e.g. purchase, session_start
  • value: number | null
  • currency: string | null
  • params: object | null
  • occurred_at: string — ISO timestamp>

OpenRequest#

  • device_id required: string
  • click_id: string | null
  • url: string | null — The deep link that opened the app
  • occurred_at: string

PushTokenRequest#

  • device_id required: string
  • push_token required: string — APNs device token or FCM registration token
  • platform required: string (ios | android | tvos | android_tv | fire_tv)

SubscriptionRequest#

  • device_id required: string
  • install_id: string | null
  • store required: string (apple | google)
  • original_transaction_id: string | null — StoreKit 2 Transaction.originalID
  • purchase_token: string | null — Play Billing purchaseToken
  • product_id required: string
  • is_trial: boolean
  • price_micros: integer — Price × 1,000,000
  • currency: string | null
  • expires_at: string | null
  • occurred_at: string | null

AdRevenueRequest#

  • device_id required: string
  • install_id: string | null
  • events required: array<- network required: string — admob | max | ironsource | …
  • ad_unit: string | null
  • format: string | null — banner | interstitial | rewarded | native
  • placement: string | null
  • revenue required: number — Currency units (AdMob micros ÷ 1,000,000)
  • currency: string
  • precision: string | null
  • source: string | null
  • ts: string | null>

BeaconRequest#

  • click_id required: string
  • fingerprint: string — Client fingerprint hash
  • screen: string — e.g. 390x844
  • timezone: string
  • languages: string
  • webdriver: boolean
  • touch: boolean
  • dwell_ms: integer
  • scroll: integer — Max scroll depth %

SkanPostback#

  • version required: string — 2.2 – 4.0
  • ad-network-id required: string
  • transaction-id required: string
  • app-id: string | integer
  • campaign-id: string | integer
  • source-identifier: string | integer
  • conversion-value: integer
  • coarse-conversion-value: string
  • fidelity-type: integer
  • did-win: boolean
  • source-app-id: string | integer
  • postback-sequence-index: integer
  • attribution-signature: string

RouteTestRequest#

  • slug required: string
  • country: string
  • region: string
  • city: string
  • os: string — ios | android | desktop
  • ua: string
  • ip: string
  • language: string
  • timezone: string
  • hour: integer
  • params: string — Query string
  • inApp: string — 'true' to simulate an in-app browser

McpRequest#

  • jsonrpc required: string
  • id: string | integer
  • method required: string — initialize | tools/list | tools/call | resources/list | resources/read | prompts/list | prompts/get | ping
  • params: object

Health#

  • status: string
  • db: string
  • latencyMs: integer
  • version: string