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.

PlatformPackageMinimumNotes
iOSSwift Package WhichClick (sdk/ios)iOS 14, Swift 5.9IDFV hashed; AdServices token; universal links
AndroidGradle io.whichclick.sdk (sdk/android)minSdk 23Install Referrer 2.2; GAID optional
React Native / Expo@whichclick/react-nativeRN 0.68AsyncStorage peer dependency
Webwhichclick.js (sdk/web)ES2019Stores wc_click_id 7 days, conversion helper
CTVsdk/ctv (tvOS, Android TV, Fire TV, Roku, Samsung, LG, consoles)Household attribution

Install#

iOS
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) }
Android
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) }
React Native
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" } });
Web
<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>

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#

TypeScript
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.

Text
https://links.yourbrand.com/product/123?wc_click_id=<id>
yourbrand://open?wc_click_id=<id>&deep_link_path=/product/123

On 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.

Install
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 }
Events
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#

  1. Create the app in Apps & SDK and copy the SDK key.
  2. Install the SDK, configure it with the key at launch.
  3. Wire consent (requireConsent + setConsent) to your CMP / ATT prompt.
  4. Register onAttribution and route to deep_link_path.
  5. Forward inbound URLs (handle(url:), handleIntent).
  6. Host apple-app-site-association / assetlinks.json on the link domain.
  7. Log purchase with value + currency at minimum.
  8. Verify in the dashboard: one install, session_start, events in batches — and nothing before consent.