Mobile & web SDKs
iOS, Android, React Native, web and CTV SDKs — installation, consent gate, deep links, events, and the two HTTP calls behind them.
Updated 2026-09-02
Every SDK does the same four things: one install call on first open (retried with backoff), automatic session_start, a persisted event queue, and deep-link parsing (wc_click_id, deep_link_path). No SDK reads an advertising identifier unless the app hands it over, and all have a consent gate.
| Platform | Package | Minimum | Notes |
|---|---|---|---|
| iOS | Swift Package WhichClick (sdk/ios) | iOS 14, Swift 5.9 | IDFV hashed; AdServices token; universal links |
| Android | Gradle io.whichclick.sdk (sdk/android) | minSdk 23 | Install Referrer 2.2; GAID optional |
| React Native / Expo | @whichclick/react-native | RN 0.68 | AsyncStorage peer dependency |
| Web | whichclick.js (sdk/web) | ES2019 | Stores wc_click_id 7 days, conversion helper |
| CTV | sdk/ctv (tvOS, Android TV, Fire TV, Roku, Samsung, LG, consoles) | — | Household attribution |
Install#
import WhichClick
WhichClick.configure(sdkKey: "sk_…", requireConsent: true)
WhichClick.onAttribution { attribution in
if let path = attribution.deepLinkPath { router.open(path) }
}
// after your CMP / ATT prompt:
WhichClick.setConsent(true)
// forward inbound URLs:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { WhichClick.handle(url: url) }WhichClick.configure(context, sdkKey = "sk_…", requireConsent = true)
WhichClick.onAttribution { attribution -> attribution.deepLinkPath?.let(router::open) }
WhichClick.setConsent(true)
override fun onNewIntent(intent: Intent) { super.onNewIntent(intent); WhichClick.handleIntent(intent) }import { WhichClick } from "@whichclick/react-native";
await WhichClick.init({ sdkKey: "sk_…", requireConsent: true, onAttribution: (a) => a.deepLinkPath && navigate(a.deepLinkPath) });
WhichClick.setConsent(true);
WhichClick.logEvent("purchase", { value: 49.9, currency: "USD", params: { sku: "abc" } });<script async src="https://app.whichclick.is/sdk/whichclick.js" data-sdk-key="sk_…"></script>
<script>
// later, on conversion:
window.whichclick?.conversion({ event: "lead", value: 0 });
</script>Consent gate#
With requireConsent: true the SDK starts in pending: nothing leaves the device, but the install call, session_start and logged events are queued locally and deep links are still parsed so the click id is kept. setConsent(true) persists the decision, sends the install call and flushes the queue. setConsent(false) stops sending, clears the queue and drops any advertising id. consentStatus returns pending | granted | denied.
Events#
WhichClick.logEvent("purchase", { value: 49.9, currency: "USD", params: { sku: "abc" } });Queue: flush every 10 s, at 20 events, or on background; cap 500; retry on network errors, 408, 429 and 5xx with jittered exponential backoff. Standard names: session_start, purchase, add_to_cart, login, signup, level_complete, subscribe, trial_start. session_start is automatic after 30 minutes of inactivity.
Deep links#
https://links.yourbrand.com/product/123?wc_click_id=<id>
yourbrand://open?wc_click_id=<id>&deep_link_path=/product/123On first open the click id is attached to the install call; the deep-link callback fires on every open. For existing users the SDK also calls POST /api/sdk/v1/open so re-engagement campaigns are attributed.
HTTP contract#
All SDK calls go to https://app.whichclick.is with X-SDK-Key and Content-Type: application/json.
POST /api/sdk/v1/install
X-SDK-Key: sk_…
{ "device_id": "<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" }
201 { "install_id": "ins_01…", "attribution": { "method": "CLICK_ID", "campaign": "Brand — Search", "click_id": "k3Zp9Qw1mR7tXc2b", "deep_link_path": "/product/123" }, "is_reattribution": false }POST /api/sdk/v1/events
X-SDK-Key: sk_…
{ "device_id": "<sha256>", "install_id": "ins_01…", "events": [ { "name": "purchase", "value": 49.9, "currency": "USD", "params": { "sku": "abc" } } ] }
200 { "accepted": 1 }Other SDK endpoints: open (re-engagement), push-token (uninstall tracking), subscription, ad-revenue — see the API reference.
Integration checklist#
- Create the app in Apps & SDK and copy the SDK key.
- Install the SDK, configure it with the key at launch.
- Wire consent (
requireConsent+setConsent) to your CMP / ATT prompt. - Register
onAttributionand route todeep_link_path. - Forward inbound URLs (
handle(url:),handleIntent). - Host
apple-app-site-association/assetlinks.jsonon the link domain. - Log
purchasewith value + currency at minimum. - Verify in the dashboard: one install,
session_start, events in batches — and nothing before consent.