Kochava Migration
Migrate from Kochava to WarpLink: SmartLink and parameter mapping, SDK swap, and deep linking, install attribution, and analytics.
This guide is for teams moving from Kochava (SmartLinks) to WarpLink: mapping SmartLink parameters, exporting existing links and data, and swapping the Kochava SDK for the WarpLink SDK on each platform, carrying over deep linking, install attribution, and analytics. It assumes a WarpLink account and app are already set up.
For the provider-agnostic playbook (planning, domains, cutover, decommissioning), see the general migration guide. This page covers only what is specific to leaving Kochava.
Concept Mapping
| Kochava | WarpLink |
|---|---|
| SmartLink | Link |
| Kochava dashboard | WarpLink dashboard |
your-brand-name.smart.link subdomain | aplnk.to domain (or custom domain) |
Deep link destination / catch-all macro ({insert_deeplink_here}, {deeplink}) | ios_url / android_url |
| Custom Link Previews | og_title / og_description / og_image_url |
| Custom Parameter (key/value catch-all) | A query parameter appended to destination_url, echoed back as custom_params on resolve |
| Fraud Console, SKAdNetwork/AdAttributionKit support, Cost and Partner Reconciliation, Always-On Incremental Measurement | Not replaced by WarpLink; see Attribution and Reporting |
1. Export Your Links
Links via API. Kochava's Links API (https://links.api.kochava.com/v1, header auth Authentication-Key: {MyUserAPIKey}) exposes GET /links to list your SmartLinks with filtering and pagination, and GET /links/{id} to fetch one. Returned fields include id, guid, name, click_url, impression_url, custom_params, routes_by_app, destination_url, reengagement_url, and native_link. There is no documented bulk CSV export of link configurations, so plan to page through GET /links and reconstruct a spreadsheet yourself.
Historical click and attribution data. A separate Reporting API accepts time_start/time_end and returns CSV or JSON, with an option to have Kochava deposit reports directly into a customer-owned S3 bucket instead of polling.
Kochava's SmartLink creation guide does not publish a slug or custom-alias format for SmartLinks. Pull your actual SmartLink IDs from the Links API before assuming they will carry over as WarpLink custom slugs.
2. Recreate Your Links
Recreate each SmartLink as a link in WarpLink via the dashboard or the REST API.
Parameter Mapping
| Kochava Field | WarpLink Field |
|---|---|
| Deep link destination (Custom / Deeplink, macro-based) | ios_url / android_url |
| Destination if App is Installed (per-platform) | ios_url / android_url |
| Destination if App is Not Installed / catch-all for unsupported platforms | destination_url |
| Custom Link Preview image / title / description | og_title / og_description / og_image_url |
| Custom Parameter (key/value catch-all) | A query parameter appended to destination_url, echoed back as custom_params on resolve |
| UTM parameters | utm_campaign / utm_medium / utm_source |
Kochava's deep link macro names are case-sensitive: the placeholder in the click URL (for example {insert_deeplink_here}) must match the destination string exactly. When you move that value into ios_url or android_url, drop the macro syntax and put the resolved app-scheme path directly in the field.
Create via API
curl -X POST https://api.warplink.app/v1/links \
-H "Authorization: Bearer wl_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"destination_url": "https://yourapp.com/product/123",
"ios_url": "myapp://product/123",
"android_url": "myapp://product/123",
"og_title": "Check out this product",
"utm_campaign": "spring_sale"
}'
3. Swap the SDK
Remove Kochava:
- Remove the
KochavaMeasurementSwift Package (https://github.com/Kochava/Apple-SwiftPackage-KochavaMeasurement-XCFramework) along withKochavaNetworkingand, if present, the optionalKochavaTrackingmodule - Remove the
Measurement.shared.start(appGUIDString:)call (or its Objective-C equivalent,KVAMeasurement.shared/startWithAppGUIDString:) - Remove any
KVADeeplinkprocessing calls wired into your URL and Universal Link handlers
Add WarpLink:
// Package.swift
dependencies: [
.package(url: "https://github.com/WarpLinkApp/warplink-ios-sdk", from: "1.1.0")
]Remove Kochava:
// Remove from build.gradle.kts
// implementation("com.kochava.measurement:base:$kochava_measurement_version")Also remove the Measurement.getInstance().startWithAppGuid(context, appGuid) call and any Measurement.getInstance().processDeeplink(...) calls in your Activity, plus the intent filter entry for your-brand-name.smart.link.
Add WarpLink:
dependencies {
implementation("app.warplink:sdk:1.1.0")
}Remove Kochava:
npm uninstall react-native-kochava-measurementAdd WarpLink:
npm install @warplink/react-nativeThe iOS SDK is distributed through Swift Package Manager and needs dynamic linkage, so add this to ios/Podfile inside your app target before installing pods:
# ios/Podfile, inside your app target
use_frameworks! :linkage => :dynamiccd ios && pod install4. Update SDK Initialization
Mobile apps authenticate with an SDK key, created in the dashboard under API Keys > SDK key. An API key looks identical but cannot record installs, so pasting one here leaves deep links working while attribution silently fails.
Before (Kochava):
import KochavaMeasurement
let measurement = Measurement.shared
measurement.start(appGUIDString: "_YOUR_APP_GUID_")After (WarpLink):
import WarpLink
WarpLink.configure(
apiKey: "wl_live_yoursdkkeyhere000000000000000000",
options: WarpLinkOptions(onLink: { result in
if case .success(let link) = result, let link {
navigate(to: link) // taps AND deferred installs (check link.isDeferred)
}
})
)Before (Kochava):
import com.kochava.measurement.Measurement
Measurement.getInstance().startWithAppGuid(applicationContext, "YOUR_APP_GUID")After (WarpLink):
import app.warplink.WarpLink
import app.warplink.WarpLinkOptions
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
WarpLink.configure(
context = this,
apiKey = "wl_live_yoursdkkeyhere000000000000000000",
options = WarpLinkOptions(onLink = { result ->
result.onSuccess { link -> navigateTo(link) }
})
)
}
}Before (Kochava):
import { KochavaMeasurement } from 'react-native-kochava-measurement';
KochavaMeasurement.instance.registerAndroidAppGuid('YOUR_ANDROID_APP_GUID');
KochavaMeasurement.instance.registerAppleAppGuid('YOUR_APPLE_APP_GUID');
KochavaMeasurement.instance.start();After (WarpLink):
import { WarpLink } from '@warplink/react-native';
WarpLink.configure({
apiKey: 'wl_live_yoursdkkeyhere000000000000000000',
onLink: ({ deepLink }) => deepLink && navigateTo(deepLink.destination),
});5. Update Domain Configuration
In Signing & Capabilities > Associated Domains:
- applinks:your-brand-name.smart.link
+ applinks:aplnk.toIn AndroidManifest.xml, update the intent filter host:
<data
android:scheme="https"
- android:host="your-brand-name.smart.link" />
+ android:host="aplnk.to" />smart.link is Kochava-owned infrastructure. Every SmartLink domain, including a branded your-brand-name.smart.link subdomain, is provisioned by Kochava's Client Success team on Kochava's own infrastructure, and Kochava hosts the AASA and Digital Asset Links files for it directly. There is no documented option for a fully customer-owned root domain pointed at Kochava through your own DNS. That means migrating off Kochava is not a DNS repoint: plan for a new link domain on WarpLink from day one, and update every published link (ads, emails, social bios, QR codes, App Store metadata) to the new URL rather than expecting the old one to carry over. Whether Kochava continues serving your smart.link subdomain after you cancel is not documented; confirm directly with Kochava before assuming old links stay live during your transition window.
Moving to a custom domain on WarpLink instead of the default aplnk.to? Declare it to the SDK too: linkDomains at configure(), or the WarpLinkDomains plist key / app.warplink.DOMAINS manifest entry. See Use it in your app.
Keep your existing Kochava Associated Domains and App Links entries in place alongside the new WarpLink ones while you cut over, so links already distributed keep resolving on-device until every surface is updated to the new domain.
If Android App Links fail to verify after cutover, the most common causes are a signing-key fingerprint mismatch between assetlinks.json and the APK's actual signature (especially with Play App Signing re-signing), a missing android:autoVerify="true", or assetlinks.json being served via a redirect instead of a direct response.
6. Migrate Deep Link Handling
The onLink callback set in step 4 already receives resolved links for cold start, warm start, and deferred installs.
Kochava's own deep-linking guide does not publish exact continue userActivity / open url wiring for iOS, so check your existing integration for however your team called into Deeplink.process(url:...) and delete it. Forward the incoming activity to WarpLink instead (or set your scene delegate class to WarpLinkSceneDelegate):
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
WarpLink.continue(userActivity)
}Resolved links arrive in your onLink callback.
Delete the Measurement.getInstance().processDeeplink(path, eventAutoSend, listener) call from your Activity. Cold start is automatic once onLink is set at configure(). For warm start, forward new intents and set android:launchMode="singleTask" on the Activity:
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
WarpLink.onNewIntent(intent) // was Measurement.getInstance().processDeeplink(...)
}Resolved links arrive in your onLink callback.
Delete the KochavaMeasurement.instance.processDeeplink(path, eventAutoSend) calls from your Linking listeners. Cold and warm start flow into onLink automatically.
On iOS you are removing the Kochava call from a delegate method that already exists and already forwards to your linking library. Swap the Kochava line for the WarpLink call and leave the rest of the method alone. Do not let handleIncomingURL decide the return value: it returns nothing, and replacing the forwarded result breaks React Navigation, Expo Linking, and OAuth callbacks.
// ios/YourApp/AppDelegate.swift
import React
import warplink_react_native
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL {
WarpLinkModule.handleIncomingURL(url) // was KochavaMeasurement.instance.processDeeplink(...)
}
return RCTLinkingManager.application(
application, continue: userActivity, restorationHandler: restorationHandler)
}Android needs no host code, given android:launchMode="singleTask". See the React Native SDK guide for the Objective-C form and Expo prebuild, and the iOS SceneDelegate hook if your app has a SceneDelegate (Info.plist declares UIApplicationSceneManifest): iOS stops calling the AppDelegate method above once a scene exists.
7. Migrate Deferred Deep Links
WarpLink runs the deferred deep link check automatically from configure(). The match arrives in the same onLink callback, flagged with isDeferred. There is no separate deferred call to port.
Kochava treats deep links and deferred deep links the same way from an SDK perspective: install-state at click time is the only difference, and both flow through the same deep link processing call rather than a separate deferred callback. There is nothing further to remove beyond the calls deleted in step 6.
Handle the deferred case inside your onLink callback:
options: WarpLinkOptions(onLink: { result in
guard case .success(let deepLink) = result, let deepLink else { return }
if deepLink.isDeferred {
print("Match confidence: \(deepLink.matchConfidence ?? 0)")
}
navigate(to: deepLink)
})Handle the deferred case inside your onLink callback:
options = WarpLinkOptions(onLink = { result ->
result.onSuccess { deepLink ->
if (deepLink.isDeferred) {
Log.d("MyApp", "Match confidence: ${deepLink.matchConfidence ?: 0.0}")
}
navigateTo(deepLink)
}
})Handle the deferred case inside your onLink callback:
WarpLink.configure({
apiKey: 'wl_live_yoursdkkeyhere000000000000000000',
onLink: ({ deepLink }) => {
if (!deepLink) return;
if (deepLink.isDeferred) console.log('Match confidence:', deepLink.matchConfidence);
navigateTo(deepLink.destination);
},
});To run the check manually instead, set the deferred opt-out flag and call checkDeferredDeepLink() yourself (see the deferred deep link guides for iOS, Android, and React Native).
8. Attribution and Reporting
WarpLink attributes an install to the link that drove it through a match cascade: a referrer signal (Play Install Referrer) on Android, a device ID (IDFV) on iOS, and an enriched fingerprint as a probabilistic fallback on both platforms, with a confidence score on every result. See the platform attribution guides for iOS, Android, and React Native for match types, confidence bands, and privacy details.
WarpLink does not replace Kochava as a mobile measurement partner. Kochava bundles a Fraud Console (a real-time Global Fraud Blocklist, statistical outlier detection, and click-farm and ad-stacking flags), native SKAdNetwork and AdAttributionKit support with a dedicated SKAN advisory service, configurable attribution and lookback windows with a device attribution behavior setting, view-through attribution, ad-spend cost aggregation and partner reconciliation, and a separately priced incrementality and marketing mix modeling product. If Kochava also serves as your measurement partner for paid campaigns, fraud filtering, or spend reconciliation, keep that integration in place alongside WarpLink.
If you run Kochava and WarpLink side by side during the transition, avoid registering both SDKs for the same URL scheme or Associated Domains entry. Running two attribution SDKs against the same domain risks double-counting installs or conflicting deep link handling; stagger the cutover by app version rather than leaving both SDKs live indefinitely.
Kochava's own case-sensitivity warning for deep link macros (the placeholder name in the click URL must match the destination string exactly) is a common source of broken links when hand-porting an existing SmartLink into a new provider's parameter syntax. Double-check every ported link's destination once it is live on WarpLink, not just its configuration.
Testing Checklist
After migration, verify on each platform:
- SDK initializes: enable debug logging and check console output
- Deep links open the app: tap a WarpLink URL on a physical device
- Deep link data resolves: destination and custom parameters are correct
- Deferred deep links work: click link, install, verify match. An uninstall is enough to retest on either platform, because the completion marker goes with the app. That second install is reported as a reinstall and still counts as an install
- AASA/assetlinks verified:
curl https://aplnk.to/.well-known/apple-app-site-association - Error handling: test invalid URLs, expired links, no connectivity
- Every ported deep link macro resolves to the correct destination, not just the literal placeholder string
- All Kochava code removed: no remaining imports or dependencies
- Clean build: no compilation errors
Next Steps
- General migration guide: the provider-agnostic playbook for planning, domains, cutover, and decommissioning.
- Quickstart: create your first link and get an SDK key.
- iOS SDK, Android SDK, and React Native SDK: full setup and API reference.
Adjust Migration
Migrate from Adjust to WarpLink: tracker and parameter mapping, SDK swap, and deep linking, install attribution, and analytics.
Airbridge Migration
Migrate from Airbridge to WarpLink: link and parameter mapping, exporting data, SDK swap, and deep linking, install attribution, and analytics.