{"openapi":"3.1.0","info":{"title":"WhichClick API","version":"2026-09-02","summary":"Google-certified click tracking, mobile attribution and click-fraud protection.","description":"Public HTTP API of WhichClick. Human documentation: https://app.whichclick.is/docs · Markdown for agents: https://app.whichclick.is/llms.txt · MCP server: https://app.whichclick.is/api/mcp","contact":{"name":"WhichClick support","url":"https://app.whichclick.is/contact","email":"support@whichclick.is"},"termsOfService":"https://app.whichclick.is/legal/terms","x-logo":{"url":"https://app.whichclick.is/icon.svg"}},"externalDocs":{"description":"Developer documentation","url":"https://app.whichclick.is/docs"},"servers":[{"url":"https://app.whichclick.is","description":"Control plane (dashboard, APIs, SDK, webhooks)"},{"url":"https://go.whichclick.is","description":"Tracking domain (redirects, pixel) — replace with your own CNAME"}],"tags":[{"name":"Redirects","description":"Endpoints visitors hit on your tracking domain."},{"name":"Conversions","description":"Postbacks, conversions, click export and cost import (API key)."},{"name":"SDK","description":"Mobile / CTV / web SDK endpoints (X-SDK-Key)."},{"name":"Growth","description":"Impressions, TV, Privacy Sandbox."},{"name":"Webhooks","description":"Endpoints third parties call (Apple, Google, edge worker, beacon)."},{"name":"Dashboard","description":"Session-authenticated endpoints used by the dashboard UI."},{"name":"AI","description":"Model Context Protocol server."},{"name":"Meta","description":"Health."}],"paths":{"/c/{slug}":{"get":{"operationId":"get_c_slug","tags":["Redirects"],"summary":"Certified click redirect","description":"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.","servers":[{"url":"https://go.whichclick.is","description":"Tracking domain (your CNAME)"}],"parameters":[{"name":"slug","in":"path","required":true,"description":"Tracking link slug","schema":{"type":"string"}},{"name":"url","in":"query","required":true,"description":"Escaped landing-page URL (`{escapedlpurl}`); aliases `u`, `redirect`, `dest`, `r`. May be single-, double- or triple-encoded.","schema":{"type":"string"}},{"name":"force_transparent","in":"query","required":true,"description":"`true` — the server must follow `url` and may not substitute a backend destination. 400 when set without `url`.","schema":{"type":"string","enum":["true"]}},{"name":"gclid","in":"query","required":false,"description":"ValueTrack `{gclid}` — captured on the click","schema":{"type":"string"}},{"name":"gbraid","in":"query","required":false,"description":"ValueTrack `{gbraid}` — captured on the click","schema":{"type":"string"}},{"name":"wbraid","in":"query","required":false,"description":"ValueTrack `{wbraid}` — captured on the click","schema":{"type":"string"}},{"name":"campaignid","in":"query","required":false,"description":"ValueTrack `{campaignid}` — captured on the click","schema":{"type":"string"}},{"name":"adgroupid","in":"query","required":false,"description":"ValueTrack `{adgroupid}` — captured on the click","schema":{"type":"string"}},{"name":"creative","in":"query","required":false,"description":"ValueTrack `{creative}` — captured on the click","schema":{"type":"string"}},{"name":"keyword","in":"query","required":false,"description":"ValueTrack `{keyword}` — captured on the click","schema":{"type":"string"}},{"name":"matchtype","in":"query","required":false,"description":"ValueTrack `{matchtype}` — captured on the click","schema":{"type":"string"}},{"name":"network","in":"query","required":false,"description":"ValueTrack `{network}` — captured on the click","schema":{"type":"string"}},{"name":"device","in":"query","required":false,"description":"ValueTrack `{device}` — captured on the click","schema":{"type":"string"}},{"name":"placement","in":"query","required":false,"description":"ValueTrack `{placement}` — captured on the click","schema":{"type":"string"}},{"name":"targetid","in":"query","required":false,"description":"ValueTrack `{targetid}` — captured on the click","schema":{"type":"string"}},{"name":"loc_physical_ms","in":"query","required":false,"description":"ValueTrack `{loc_physical_ms}` — captured on the click","schema":{"type":"string"}},{"name":"loc_interest_ms","in":"query","required":false,"description":"ValueTrack `{loc_interest_ms}` — captured on the click","schema":{"type":"string"}},{"name":"adposition","in":"query","required":false,"description":"ValueTrack `{adposition}` — captured on the click","schema":{"type":"string"}}],"responses":{"302":{"description":"Redirect to the landing page with `?wc_click_id=<id>` appended.","headers":{"Location":{"schema":{"type":"string","example":"https://landing.example/?gclid=…&wc_click_id=k3Zp9Qw1mR7tXc2b"}},"Cache-Control":{"schema":{"type":"string","example":"no-store"}}}},"400":{"description":"`force_transparent=true` without a `url`, or an unsafe (non-http) destination.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown slug, paused link, or hostname not bound to this link."}},"security":[],"x-auth":"None"}},"/l/{slug}":{"get":{"operationId":"get_l_slug","tags":["Redirects"],"summary":"Smart link","description":"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).","servers":[{"url":"https://go.whichclick.is","description":"Tracking domain (your CNAME)"}],"parameters":[{"name":"slug","in":"path","required":true,"description":"Smart link slug","schema":{"type":"string"}},{"name":"url","in":"query","required":false,"description":"Transparent override — behaves like `/c/{slug}`","schema":{"type":"string"}},{"name":"*","in":"query","required":false,"description":"Any query parameter is available to rules as `param.<name>` and passed through per link settings","schema":{"type":"string"}}],"responses":{"200":{"description":"HTML interstitial (custom-scheme fallback for in-app browsers), QR page on desktop, or Open Graph preview for social crawlers."},"302":{"description":"Redirect (store, deep link, web fallback, split variant)."},"403":{"description":"Rule action `block` (also 404/410 per rule)."},"410":{"description":"Link expired or click cap reached."}},"security":[],"x-auth":"None"}},"/i/{campaign}":{"get":{"operationId":"get_i_campaign","tags":["Redirects"],"summary":"Impression pixel","description":"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.","servers":[{"url":"https://go.whichclick.is","description":"Tracking domain (your CNAME)"}],"parameters":[{"name":"campaign","in":"path","required":true,"description":"Campaign id or ad-platform external id","schema":{"type":"string"}},{"name":"app","in":"query","required":false,"description":"App id","schema":{"type":"string"}},{"name":"cr","in":"query","required":false,"description":"Creative id","schema":{"type":"string"}},{"name":"pl","in":"query","required":false,"description":"Placement","schema":{"type":"string"}},{"name":"p","in":"query","required":false,"description":"Partner / network (e.g. roku, dv360)","schema":{"type":"string"}}],"responses":{"200":{"description":"image/gif, `Cache-Control: no-store`"}},"security":[],"x-auth":"None"}},"/postback":{"get":{"operationId":"get_postback","tags":["Conversions"],"summary":"Inbound postback (GET)","description":"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.","parameters":[{"name":"api_key","in":"query","required":false,"description":"API key when the Authorization header cannot be set","schema":{"type":"string"}},{"name":"click_id","in":"query","required":false,"description":"`wc_click_id` value","schema":{"type":"string"}},{"name":"gclid","in":"query","required":false,"description":"Alternative to click_id","schema":{"type":"string"}},{"name":"event","in":"query","required":false,"description":"Event name (default `conversion`)","schema":{"type":"string"}},{"name":"value","in":"query","required":false,"description":"Conversion value","schema":{"type":"number"}},{"name":"currency","in":"query","required":false,"description":"ISO 4217","schema":{"type":"string"}},{"name":"order_id","in":"query","required":false,"description":"Idempotency key with `event`","schema":{"type":"string"}}],"responses":{"200":{"description":"Stored (matched or unattributed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversionResult"},"example":{"ok":true,"id":"clx8n2…","matched":true}}}},"400":{"description":"Validation error or neither click_id nor gclid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing / revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]},{"apiKeyQuery":[]}],"x-auth":"API key (Bearer)"}},"/api/v1/conversions":{"post":{"operationId":"post_api_v1_conversions","tags":["Conversions"],"summary":"Create conversion","description":"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.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversionInput"},"example":{"click_id":"k3Zp9Qw1mR7tXc2b","event":"purchase","value":49.9,"currency":"USD","order_id":"ORD-1001"}}}},"responses":{"201":{"description":"Created / updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversionResult"},"example":{"ok":true,"id":"clx8n2…","matched":true}}}},"400":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]},{"apiKeyQuery":[]}],"x-auth":"API key (Bearer)"},"get":{"operationId":"get_api_v1_conversions","tags":["Conversions"],"summary":"List conversions","description":"Most recent conversions for the organization.","parameters":[{"name":"limit","in":"query","required":false,"description":"1–1000 (default 100)","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Conversion"}}}}}}}},"security":[{"apiKey":[]},{"apiKeyQuery":[]}],"x-auth":"API key (Bearer)"}},"/api/v1/clicks":{"get":{"operationId":"get_api_v1_clicks","tags":["Conversions"],"summary":"Export clicks","description":"Click facts (ValueTrack fields, next hop, transparency flags) for offline conversion import or auditing. Newest first.","parameters":[{"name":"limit","in":"query","required":false,"description":"1–1000 (default 100)","schema":{"type":"integer"}},{"name":"since","in":"query","required":false,"description":"ISO timestamp lower bound","schema":{"type":"string","format":"date-time"}},{"name":"gclid","in":"query","required":false,"description":"Filter by gclid","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Click"}}}},"example":{"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}]}}}}},"security":[{"apiKey":[]},{"apiKeyQuery":[]}],"x-auth":"API key (Bearer)"}},"/api/v1/costs":{"post":{"operationId":"post_api_v1_costs","tags":["Conversions"],"summary":"Import daily spend","description":"Bulk upsert of daily media cost per campaign (enables CPI / ROAS). Rows referencing unknown campaigns are skipped; the response counts upserts.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/CostRow"},"minItems":1,"maxItems":5000}},"required":["rows"]},"example":{"rows":[{"campaign_external_id":"2098765432","day":"2026-09-01","spend":1240.5,"currency":"USD","impressions":91000}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":""},"upserted":{"type":"integer","description":""}}},"example":{"ok":true,"upserted":1}}}}},"security":[{"apiKey":[]},{"apiKeyQuery":[]}],"x-auth":"API key (Bearer)"}},"/api/v1/impressions":{"post":{"operationId":"post_api_v1_impressions","tags":["Growth"],"summary":"Log impressions (S2S)","description":"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.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"impressions":{"type":"array","items":{"$ref":"#/components/schemas/Impression"},"minItems":1,"maxItems":5000}},"required":["impressions"]},"example":{"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":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"integer","description":""}}}}}}},"security":[{"apiKey":[]},{"apiKeyQuery":[]}],"x-auth":"API key (Bearer)"}},"/api/v1/tv-airings":{"post":{"operationId":"post_api_v1_tv_airings","tags":["Growth"],"summary":"Import TV airings","description":"Linear TV post-log import. Attribution runs immediately: baseline traffic before each spot vs. the spike inside `window_min` minutes after it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"airings":{"type":"array","items":{"$ref":"#/components/schemas/TvAiring"},"minItems":1,"maxItems":5000}},"required":["airings"]},"example":{"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":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"integer","description":""},"attributed_airings":{"type":"integer","description":""}}}}}}},"security":[{"apiKey":[]},{"apiKeyQuery":[]}],"x-auth":"API key (Bearer)"}},"/.well-known/attribution-reporting/register-source":{"get":{"operationId":"get_well_known_attribution_reporting_register_source","tags":["Growth"],"summary":"Privacy Sandbox: register source","description":"Responds with an `Attribution-Reporting-Register-Source` header for Android Privacy Sandbox (`MeasurementManager.registerSource`) and Chrome `attributionsrc` tags.","servers":[{"url":"https://go.whichclick.is","description":"Tracking domain (your CNAME)"}],"parameters":[{"name":"link","in":"query","required":false,"description":"Smart-link or tracking-link slug","schema":{"type":"string"}},{"name":"campaign","in":"query","required":false,"description":"Campaign id","schema":{"type":"string"}}],"responses":{"200":{"description":"Empty body; registration header set."}},"security":[],"x-auth":"None"}},"/.well-known/attribution-reporting/register-trigger":{"get":{"operationId":"get_well_known_attribution_reporting_register_trigger","tags":["Growth"],"summary":"Privacy Sandbox: register trigger","description":"Responds with an `Attribution-Reporting-Register-Trigger` header for a conversion event.","servers":[{"url":"https://go.whichclick.is","description":"Tracking domain (your CNAME)"}],"parameters":[{"name":"event","in":"query","required":false,"description":"Event name","schema":{"type":"string"}},{"name":"value","in":"query","required":false,"description":"Value","schema":{"type":"number"}}],"responses":{"200":{"description":"Empty body; trigger header set."}},"security":[],"x-auth":"None"}},"/.well-known/attribution-reporting/report-event-attribution":{"post":{"operationId":"post_well_known_attribution_reporting_report_event_attribution","tags":["Growth"],"summary":"Privacy Sandbox: reports","description":"Receives event-level reports. `report-aggregate-attribution` receives aggregatable reports and `debug/*` receives debug reports on the same prefix.","servers":[{"url":"https://go.whichclick.is","description":"Tracking domain (your CNAME)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Stored."}},"security":[],"x-auth":"None"}},"/api/sdk/v1/install":{"post":{"operationId":"post_api_sdk_v1_install","tags":["SDK"],"summary":"Report install","description":"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.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallRequest"},"example":{"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":{"description":"Existing install (idempotent repeat).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallResponse"}}}},"201":{"description":"New install attributed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallResponse"},"example":{"install_id":"ins_01…","attribution":{"method":"CLICK_ID","campaign":"Brand — Search","click_id":"k3Zp9Qw1mR7tXc2b","deep_link_path":"/product/123"},"is_reattribution":false}}}},"400":{"description":"Validation error or platform mismatch for the SDK key."},"401":{"description":"Invalid SDK key."}},"security":[{"sdkKey":[]}],"x-auth":"SDK key (`X-SDK-Key`)"}},"/api/sdk/v1/events":{"post":{"operationId":"post_api_sdk_v1_events","tags":["SDK"],"summary":"Send in-app events","description":"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.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventsRequest"},"example":{"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":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"integer","description":""}}}}}}},"security":[{"sdkKey":[]}],"x-auth":"SDK key (`X-SDK-Key`)"}},"/api/sdk/v1/open":{"post":{"operationId":"post_api_sdk_v1_open","tags":["SDK"],"summary":"Re-engagement open","description":"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`).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenRequest"},"example":{"device_id":"2f7c…sha256","url":"https://link.yourbrand.com/product/123?wc_click_id=abc"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"reengagement_id":{"type":"string","description":""},"attributed":{"type":"boolean","description":""},"campaign_id":{"type":["string","null"],"description":""},"deep_link_path":{"type":["string","null"],"description":""}}}}}},"404":{"description":"Install not found — call /install first."}},"security":[{"sdkKey":[]}],"x-auth":"SDK key (`X-SDK-Key`)"}},"/api/sdk/v1/push-token":{"post":{"operationId":"post_api_sdk_v1_push_token","tags":["SDK"],"summary":"Register push token","description":"Stores the APNs / FCM token on the install so the uninstall checker can send silent pushes.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushTokenRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"install_id":{"type":"string","description":""}}}}}},"404":{"description":"Install not found."}},"security":[{"sdkKey":[]}],"x-auth":"SDK key (`X-SDK-Key`)"}},"/api/sdk/v1/subscription":{"post":{"operationId":"post_api_sdk_v1_subscription","tags":["SDK"],"summary":"Link subscription","description":"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).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionRequest"},"example":{"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":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"subscription_id":{"type":"string","description":""},"install_id":{"type":["string","null"],"description":""},"linked":{"type":"boolean","description":""},"created":{"type":"boolean","description":""}}}}}}},"security":[{"sdkKey":[]}],"x-auth":"SDK key (`X-SDK-Key`)"}},"/api/sdk/v1/ad-revenue":{"post":{"operationId":"post_api_sdk_v1_ad_revenue","tags":["SDK"],"summary":"Send ad revenue","description":"Impression-level ad revenue (AdMob paid events, MAX / ironSource ARM). Stored as `ad_revenue` events so it flows into LTV / ROAS.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdRevenueRequest"},"example":{"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":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"integer","description":""},"install_id":{"type":["string","null"],"description":""}}}}}}},"security":[{"sdkKey":[]}],"x-auth":"SDK key (`X-SDK-Key`)"}},"/api/skan/postback":{"post":{"operationId":"post_api_skan_postback","tags":["Webhooks"],"summary":"SKAdNetwork postback","description":"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.","parameters":[{"name":"org","in":"query","required":true,"description":"Organization id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkanPostback"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":""},"id":{"type":"string","description":""}}}}}},"400":{"description":"Missing org or invalid body."}},"security":[],"x-auth":"None"}},"/api/subscriptions/apple":{"post":{"operationId":"post_api_subscriptions_apple","tags":["Webhooks"],"summary":"App Store Server Notifications v2","description":"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.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"signedPayload":{"type":"string","description":"JWS"}},"required":["signedPayload"]}}}},"responses":{"200":{"description":"Processed."},"400":{"description":"Invalid JWS."}},"security":[],"x-auth":"None"}},"/api/subscriptions/google":{"post":{"operationId":"post_api_subscriptions_google","tags":["Webhooks"],"summary":"Google Play RTDN","description":"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.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"object","properties":{"data":{"type":"string","description":"base64 JSON"},"messageId":{"type":"string","description":""}}},"subscription":{"type":"string","description":""}}}}}},"responses":{"200":{"description":"Processed."}},"security":[],"x-auth":"None"}},"/api/protect/beacon":{"post":{"operationId":"post_api_protect_beacon","tags":["Webhooks"],"summary":"Engagement beacon","description":"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.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BeaconRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":""}}}}}},"404":{"description":"Unknown click id."}},"security":[],"x-auth":"None"}},"/api/ingest/clicks":{"post":{"operationId":"post_api_ingest_clicks","tags":["Webhooks"],"summary":"Edge batch ingest","description":"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.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","additionalProperties":true},"minItems":1,"maxItems":1000}},"required":["events"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"integer","description":""}}}}}},"401":{"description":"Bad signature."}},"security":[{"hmac":[]}],"x-auth":"HMAC signature"}},"/api/protect/export":{"get":{"operationId":"get_api_protect_export","tags":["Dashboard"],"summary":"Invalid-click evidence export","description":"CSV of invalid clicks (IP, reasons, score, gclid, campaign) for a Google Ads invalid-activity investigation / refund claim.","parameters":[{"name":"days","in":"query","required":false,"description":"Default 30","schema":{"type":"integer"}}],"responses":{"200":{"description":"text/csv"}},"security":[{"session":[]}],"x-auth":"Dashboard session"}},"/api/protect/sync":{"post":{"operationId":"post_api_protect_sync","tags":["Dashboard"],"summary":"Sync IP exclusions to Google Ads","description":"Pushes the blocklist as negative IP criteria to every active campaign (500 per campaign cap).","responses":{"200":{"description":"Sync summary."}},"security":[{"session":[]}],"x-auth":"Dashboard session"}},"/api/dashboard/realtime":{"get":{"operationId":"get_api_dashboard_realtime","tags":["Dashboard"],"summary":"Real-time stats","description":"Last-15-minute click / install / conversion counters and stream.","responses":{"200":{"description":"OK"}},"security":[{"session":[]}],"x-auth":"Dashboard session"}},"/api/dashboard/export":{"get":{"operationId":"get_api_dashboard_export","tags":["Dashboard"],"summary":"Report export (CSV)","description":"Pivot / clicks / conversions CSV as shown in Reports.","parameters":[{"name":"type","in":"query","required":false,"description":"clicks | conversions | pivot","schema":{"type":"string"}}],"responses":{"200":{"description":"text/csv"}},"security":[{"session":[]}],"x-auth":"Dashboard session"}},"/api/dashboard/route-test":{"post":{"operationId":"post_api_dashboard_route_test","tags":["Dashboard"],"summary":"Routing simulator","description":"Evaluates a smart link for a simulated visitor and returns the decision (same engine as the MCP `test_smart_link_route` tool).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteTestRequest"}}}},"responses":{"200":{"description":"`{ context, decision, pool }`"}},"security":[{"session":[]}],"x-auth":"Dashboard session"}},"/api/dashboard/ippool/lookup":{"get":{"operationId":"get_api_dashboard_ippool_lookup","tags":["Dashboard"],"summary":"IP lookup","description":"Pool classification, geo, rate-limit key, exclusion form and live intel for an IP.","parameters":[{"name":"ip","in":"query","required":true,"description":"","schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"session":[]}],"x-auth":"Dashboard session"}},"/api/edge/links/hot":{"get":{"operationId":"get_api_edge_links_hot","tags":["Dashboard"],"summary":"Hot links for edge warm-up","description":"Top links by clicks in the last 24 h; the edge cron uses it to warm KV.","responses":{"200":{"description":"Array of link configs."}},"security":[{"apiKey":[]},{"apiKeyQuery":[]}],"x-auth":"API key (Bearer)"}},"/api/mcp":{"post":{"operationId":"post_api_mcp","tags":["AI"],"summary":"MCP server (Streamable HTTP)","description":"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.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpRequest"},"example":{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_docs","arguments":{"query":"tracking template"}}}}}},"responses":{"200":{"description":"JSON-RPC response."},"202":{"description":"Notification accepted (no body)."},"401":{"description":"Key required for this tool."}},"security":[{"apiKey":[]},{"apiKeyQuery":[]}],"x-auth":"API key (Bearer)"}},"/api/health":{"get":{"operationId":"get_api_health","tags":["Meta"],"summary":"Health","description":"Liveness + database round-trip.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"},"example":{"status":"ok","db":"ok","latencyMs":1,"version":"0.1.0"}}}}},"security":[],"x-auth":"None"}}},"components":{"schemas":{"Error":{"type":"object","properties":{"error":{"description":"Error code or Zod validation summary","oneOf":[{"type":"string"},{"type":"object"}]},"retry_after":{"type":"integer","description":"Seconds to wait (429 only)"}},"required":["error"]},"ConversionInput":{"type":"object","properties":{"click_id":{"type":"string","description":"WhichClick click id (`wc_click_id` appended to the landing page)","minLength":4,"maxLength":64},"gclid":{"type":"string","description":"Google click id — used when click_id is unknown; matched to the most recent click","maxLength":256},"event":{"type":"string","description":"Event name","default":"conversion","maxLength":64},"value":{"type":"number","description":"Conversion value","minimum":0},"currency":{"type":"string","description":"ISO 4217 currency","minLength":3,"maxLength":3,"example":"USD"},"order_id":{"type":"string","description":"Idempotency key together with `event`","maxLength":128}}},"ConversionResult":{"type":"object","properties":{"ok":{"type":"boolean","description":"Always true"},"id":{"type":"string","description":"Conversion id"},"matched":{"type":"boolean","description":"True when a click was found"}},"required":["ok","id","matched"]},"Click":{"type":"object","properties":{"id":{"type":"string","description":"Click id"},"createdAt":{"type":"string","description":"ISO timestamp"},"gclid":{"type":["string","null"],"description":""},"gbraid":{"type":["string","null"],"description":""},"wbraid":{"type":["string","null"],"description":""},"campaignExtId":{"type":["string","null"],"description":"{campaignid}"},"adGroupExtId":{"type":["string","null"],"description":"{adgroupid}"},"creativeId":{"type":["string","null"],"description":"{creative}"},"keyword":{"type":["string","null"],"description":"{keyword}"},"matchType":{"type":["string","null"],"description":"e | p | b | a"},"network":{"type":["string","null"],"description":"g | s | d | ytv | yts"},"device":{"type":["string","null"],"description":"m | t | c"},"nextHopUrl":{"type":"string","description":"Destination the visitor was sent to"},"transparentParamPresent":{"type":"boolean","description":"A visible url= parameter was present"},"forceTransparent":{"type":"boolean","description":"force_transparent=true was present"},"isBot":{"type":"boolean","description":"Verified search-engine bot"},"latencyMs":{"type":["integer","null"],"description":"Redirect latency"}}},"Conversion":{"type":"object","properties":{"id":{"type":"string","description":""},"createdAt":{"type":"string","description":""},"clickId":{"type":["string","null"],"description":""},"campaignId":{"type":["string","null"],"description":""},"eventName":{"type":"string","description":""},"value":{"type":["number","null"],"description":""},"currency":{"type":["string","null"],"description":""},"orderId":{"type":["string","null"],"description":""},"gclid":{"type":["string","null"],"description":""},"source":{"type":"string","description":"postback | api | import"}}},"CostRow":{"type":"object","properties":{"campaign_id":{"type":"string","description":"WhichClick campaign id"},"campaign_external_id":{"type":"string","description":"Ad-platform campaign id (alternative to campaign_id)"},"day":{"type":"string","description":"YYYY-MM-DD","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"spend":{"type":"number","description":"","minimum":0},"currency":{"type":"string","description":"","default":"USD"},"impressions":{"type":"integer","description":""}},"required":["day","spend"]},"Impression":{"type":"object","properties":{"campaign_id":{"type":"string","description":""},"campaign_external_id":{"type":"string","description":""},"app_id":{"type":"string","description":""},"partner":{"type":"string","description":"","maxLength":64},"creative_id":{"type":"string","description":""},"placement":{"type":"string","description":""},"ip":{"type":"string","description":"Viewer IP (hashed server-side)"},"user_agent":{"type":"string","description":""},"os":{"type":"string","description":""},"device_type":{"type":"string","description":""},"country":{"type":"string","description":"ISO-3166 alpha-2"},"ts":{"type":"string","description":"ISO timestamp"}},"required":["ip"]},"TvAiring":{"type":"object","properties":{"network":{"type":"string","description":""},"program":{"type":"string","description":""},"region":{"type":"string","description":""},"aired_at":{"type":"string","description":"ISO timestamp"},"spot_length_sec":{"type":"integer","description":""},"cost":{"type":"number","description":""},"campaign_external_id":{"type":"string","description":""},"campaign_id":{"type":"string","description":""},"window_min":{"type":"integer","description":"Attribution window after the spot (2–60)"}},"required":["network","aired_at"]},"InstallRequest":{"type":"object","properties":{"device_id":{"type":"string","description":"SHA-256 of IDFV / GAID / app-set id (raw ids are hashed server-side)","minLength":8,"maxLength":128},"platform":{"type":"string","description":"","enum":["ios","android","tvos","android_tv","roku","fire_tv","samsung_tv","lg_tv","windows","macos","playstation","xbox","nintendo","web"]},"os_version":{"type":"string","description":""},"app_version":{"type":"string","description":""},"sdk_version":{"type":"string","description":""},"click_id":{"type":["string","null"],"description":"wc_click_id from the launch deep link"},"install_referrer":{"type":["string","null"],"description":"Raw Android Install Referrer string"},"gclid":{"type":["string","null"],"description":""},"installed_at":{"type":"string","description":"ISO timestamp"},"locale":{"type":"string","description":""},"timezone":{"type":"string","description":""},"advertising_id":{"type":["string","null"],"description":"Only with ATT / consent; forwarded to Google's app conversion API, never stored"},"limit_ad_tracking":{"type":"boolean","description":""},"apple_attribution_token":{"type":["string","null"],"description":"AdServices attribution token (iOS 14.3+)"},"ttclid":{"type":["string","null"],"description":"TikTok click id"},"sc_click_id":{"type":["string","null"],"description":"Snap click id"}},"required":["device_id","platform"]},"InstallResponse":{"type":"object","properties":{"install_id":{"type":"string","description":""},"attribution":{"type":"object","properties":{"method":{"type":"string","description":"","enum":["CLICK_ID","INSTALL_REFERRER","GCLID","DEFERRED_DEEPLINK","PROBABILISTIC","VIEW_THROUGH","HOUSEHOLD","SKAN","ORGANIC"]},"campaign":{"type":["string","null"],"description":""},"click_id":{"type":["string","null"],"description":""},"deep_link_path":{"type":["string","null"],"description":"Deferred deep link to open"}}},"is_reattribution":{"type":"boolean","description":""}}},"EventsRequest":{"type":"object","properties":{"device_id":{"type":"string","description":""},"install_id":{"type":["string","null"],"description":""},"events":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"e.g. purchase, session_start"},"value":{"type":["number","null"],"description":""},"currency":{"type":["string","null"],"description":""},"params":{"type":["object","null"],"additionalProperties":true},"occurred_at":{"type":"string","description":"ISO timestamp"}},"required":["name"]},"minItems":1,"maxItems":100}},"required":["device_id","events"]},"OpenRequest":{"type":"object","properties":{"device_id":{"type":"string","description":""},"click_id":{"type":["string","null"],"description":""},"url":{"type":["string","null"],"description":"The deep link that opened the app"},"occurred_at":{"type":"string","description":""}},"required":["device_id"]},"PushTokenRequest":{"type":"object","properties":{"device_id":{"type":"string","description":""},"push_token":{"type":"string","description":"APNs device token or FCM registration token"},"platform":{"type":"string","description":"","enum":["ios","android","tvos","android_tv","fire_tv"]}},"required":["device_id","push_token","platform"]},"SubscriptionRequest":{"type":"object","properties":{"device_id":{"type":"string","description":""},"install_id":{"type":["string","null"],"description":""},"store":{"type":"string","description":"","enum":["apple","google"]},"original_transaction_id":{"type":["string","null"],"description":"StoreKit 2 Transaction.originalID"},"purchase_token":{"type":["string","null"],"description":"Play Billing purchaseToken"},"product_id":{"type":"string","description":""},"is_trial":{"type":"boolean","description":""},"price_micros":{"type":"integer","description":"Price × 1,000,000"},"currency":{"type":["string","null"],"description":""},"expires_at":{"type":["string","null"],"description":""},"occurred_at":{"type":["string","null"],"description":""}},"required":["device_id","store","product_id"]},"AdRevenueRequest":{"type":"object","properties":{"device_id":{"type":"string","description":""},"install_id":{"type":["string","null"],"description":""},"events":{"type":"array","items":{"type":"object","properties":{"network":{"type":"string","description":"admob | max | ironsource | …"},"ad_unit":{"type":["string","null"],"description":""},"format":{"type":["string","null"],"description":"banner | interstitial | rewarded | native"},"placement":{"type":["string","null"],"description":""},"revenue":{"type":"number","description":"Currency units (AdMob micros ÷ 1,000,000)"},"currency":{"type":"string","description":"","default":"USD"},"precision":{"type":["string","null"],"description":""},"source":{"type":["string","null"],"description":""},"ts":{"type":["string","null"],"description":""}},"required":["network","revenue"]}}},"required":["device_id","events"]},"BeaconRequest":{"type":"object","properties":{"click_id":{"type":"string","description":""},"fingerprint":{"type":"string","description":"Client fingerprint hash"},"screen":{"type":"string","description":"e.g. 390x844"},"timezone":{"type":"string","description":""},"languages":{"type":"string","description":""},"webdriver":{"type":"boolean","description":""},"touch":{"type":"boolean","description":""},"dwell_ms":{"type":"integer","description":""},"scroll":{"type":"integer","description":"Max scroll depth %"}},"required":["click_id"]},"SkanPostback":{"type":"object","properties":{"version":{"type":"string","description":"2.2 – 4.0"},"ad-network-id":{"type":"string","description":""},"transaction-id":{"type":"string","description":""},"app-id":{"oneOf":[{"type":"integer"},{"type":"string"}]},"campaign-id":{"oneOf":[{"type":"integer"},{"type":"string"}]},"source-identifier":{"oneOf":[{"type":"integer"},{"type":"string"}]},"conversion-value":{"type":"integer","description":""},"coarse-conversion-value":{"type":"string","description":""},"fidelity-type":{"type":"integer","description":""},"did-win":{"type":"boolean","description":""},"source-app-id":{"oneOf":[{"type":"integer"},{"type":"string"}]},"postback-sequence-index":{"type":"integer","description":""},"attribution-signature":{"type":"string","description":""}},"required":["version","ad-network-id","transaction-id"]},"RouteTestRequest":{"type":"object","properties":{"slug":{"type":"string","description":""},"country":{"type":"string","description":""},"region":{"type":"string","description":""},"city":{"type":"string","description":""},"os":{"type":"string","description":"ios | android | desktop"},"ua":{"type":"string","description":""},"ip":{"type":"string","description":""},"language":{"type":"string","description":""},"timezone":{"type":"string","description":""},"hour":{"type":"integer","description":""},"params":{"type":"string","description":"Query string"},"inApp":{"type":"string","description":"'true' to simulate an in-app browser"}},"required":["slug"]},"McpRequest":{"type":"object","properties":{"jsonrpc":{"type":"string","description":"","const":"2.0"},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"method":{"type":"string","description":"initialize | tools/list | tools/call | resources/list | resources/read | prompts/list | prompts/get | ping"},"params":{"type":"object","additionalProperties":true}},"required":["jsonrpc","method"]},"Health":{"type":"object","properties":{"status":{"type":"string","description":""},"db":{"type":"string","description":""},"latencyMs":{"type":"integer","description":""},"version":{"type":"string","description":""}}}},"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"wc_live_…","description":"Organization API key from Settings → API keys."},"apiKeyQuery":{"type":"apiKey","in":"query","name":"api_key","description":"Same key as a query parameter (postback URLs)."},"sdkKey":{"type":"apiKey","in":"header","name":"X-SDK-Key","description":"Per-app SDK key from Apps → SDK."},"session":{"type":"apiKey","in":"cookie","name":"wc_session","description":"Dashboard session cookie (browser only)."},"hmac":{"type":"apiKey","in":"header","name":"X-WC-Signature","description":"hex(HMAC-SHA256(body, EDGE_INGEST_SECRET))"}}}}