# Migrate from AppsFlyer OneLink

> After this guide, you will have your OneLink links rebuilt on Strait with the same short paths, in-app destinations, fallbacks, previews and campaigns, an app that opens them with the Strait SDK, and a safe plan to stop using OneLink.

- Time: About 15 minutes to import. The whole move is about a day of developer work plus one app release
- You need: Your OneLink Custom Links export (CSV), your app's code, a Strait workspace
- Works on: Android, iPhone, React Native, Flutter, Capacitor, Unity

Source: https://straitlink.in/docs/migrate-from-appsflyer/

Five steps. Each says what to do, what to copy and how you know it worked.

## 1. Export your links from AppsFlyer

**Do this:** in AppsFlyer, open **Experiences & Deep Linking → OneLink Custom Links**, pick the template, and press **Export**. Don't edit the file. Its columns are the link name, media source, campaign name, short URL and long URL.

```txt title="onelink-links.csv (shortened)"
Link name,Media source,Campaign name,Short URL,Long URL
Summer sale,instagram,summer_26,https://yourapp.onelink.me/AbCd/summer,https://yourapp.onelink.me/AbCd?pid=instagram&c=summer_26&af_dp=yourapp%3A%2F%2Fproduct%2F42&af_web_dp=https%3A%2F%2Fshop.example.com%2Fp%2F42
Promo,email,,https://yourapp.onelink.me/AbCd/promo,https://yourapp.onelink.me/AbCd?pid=email&deep_link_value=product_42&af_web_dp=https%3A%2F%2Fshop.example.com%2F
```

AppsFlyer's export only includes links made in its dashboard. Links made with the OneLink API, a bulk upload or the SDK aren't in it; create those on Strait with the batch API in step 3.

**You should see:** one CSV with a row per OneLink link.

## 2. Preview the import

**Do this:** open **Dashboard → Import → From AppsFlyer**. Choose the file and press **Preview**.

Each row shows the new short path, the screen your app will open, fallbacks, preview and campaign. The settings come from the long URL's parameters:

| OneLink | Strait |
|---|---|
| The end of the short URL (`yourapp.onelink.me/AbCd/summer` → `summer`), else the link name | The short path (slug). Letters, numbers, `-` and `_`, up to 64; other characters become `-` |
| `af_dp` (`yourapp://product/42`) | The path your app gets, `e.path`: `/product/42`, on your website's address |
| `deep_link_value`, `deep_link_sub1` … `deep_link_sub10` | Kept as parameters, `e.params.deep_link_value`, so app code that reads them keeps working |
| `af_web_dp` | Its address carries the path; when its page differs, it's where computers go (`desktopUrl`). A link with only `af_web_dp` keeps it as the destination |
| `af_ios_url`, `af_android_url` | `iosFallbackUrl`, `androidFallbackUrl`. Without them, people without the app go to the store, as in OneLink |
| `af_og_title`, `af_og_description`, `af_og_image` | The chat preview: `title`, `description`, `imageUrl` |
| `c`, else the campaign name column | `campaign` |
| `pid`, else the media source column | `utm_source` on the destination (how Analytics names the channel) |

A link with `af_dp` or `deep_link_value` but no `af_web_dp` needs your website's address: type it in **Your website** and preview again. Rows with a problem say why in plain words, and every parameter we don't use (such as `is_retargeting` or `af_adset`) is listed.

**You should see:** a table of your links. Any row with a problem says what's wrong.

## 3. Import

**Do this:** press **Import N links**.

Running it twice is safe: a short path that already exists is reported, never overwritten. Rows that failed can be downloaded as a CSV, fixed, and imported again with **Bulk create (CSV)**.

For links that aren't in the export, or thousands of them, use the API: `POST /v1/links/batch` takes up to 500 links per call with your **secret key** (the server-only key from **Dashboard → Settings**).

```bash
curl https://strait.link/v1/links/batch \
  -H "Authorization: Bearer $SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "links": [
      {
        "slug": "summer",
        "longUrl": "https://shop.example.com/product/42?utm_source=instagram",
        "desktopUrl": "https://shop.example.com/p/42",
        "campaign": "summer_26"
      },
      { "slug": "promo", "longUrl": "https://shop.example.com/?deep_link_value=product_42&utm_source=email" }
    ]
  }'
```

Each item gets its own answer: `201` made, `400` invalid, `402` plan limit reached, `409` short path taken. See the [REST API](https://straitlink.in/docs/api/#batch-create).

**You should see:** a summary of what was made, and your links in **Dashboard → Links** at `https://<handle>.strait.link/<path>`.

## 4. Swap the SDK in your app

**Do this:** remove the AppsFlyer deep-linking code and add our SDK ([React Native](https://straitlink.in/docs/sdks/react-native/), [Flutter](https://straitlink.in/docs/sdks/flutter/), [Capacitor](https://straitlink.in/docs/sdks/capacitor/), [Android](https://straitlink.in/docs/sdks/android/), [iOS](https://straitlink.in/docs/sdks/ios/), [Unity](https://straitlink.in/docs/sdks/unity/)). Then swap the red lines for the green ones:

```diff
- appsFlyer.onDeepLink((res) => { if (res.deepLinkStatus === 'FOUND') route(res.data.deep_link_value); });
+ links.onLink((e) => { if (e.matched) route(e.path, e.params); });   // e.params.deep_link_value is still there
```

```diff
- appsFlyer.logEvent('af_purchase', { af_revenue: 49.99, af_currency: 'USD' });
+ links.trackEvent('purchase', { value: 49.99, currency: 'USD' });
```

Links tapped before an install arrive in the same callback with `kind: 'deferred'`. Using AppsFlyer for ad attribution? Keep its SDK for that.

Last, point app verification at your new address. Android: set the intent filter's `android:host` to `hilltop.strait.link` and add your SHA-256 fingerprints in **Apps & stores**. iPhone: set Associated Domains to `applinks:hilltop.strait.link` and add your Team ID and bundle ID in **Apps & stores**. In a debug build, the SDK's `doctor()` tells you if anything is missing.

**You should see:** tapping `https://hilltop.strait.link/summer` in Messages or Gmail opens your app on the product 42 screen.

## 5. Switch over, then retire OneLink

**Do this:** go in this order.

1. **Ship the app update.** Keep the AppsFlyer SDK in this release if old OneLink links should still open the app.
2. **Make new links on Strait.** Point your backend, marketing tools and share buttons at `POST /v1/links` (or the [Node SDK](https://straitlink.in/docs/sdks/node/)).
3. **Replace old links** wherever you control the text: website, emails, app screens, QR codes you can reprint.
4. **Keep OneLink running** while old `onelink.me` links still get taps. They only work while your AppsFlyer setup does.
5. **Remove the AppsFlyer deep-linking code** in a later release, once taps on old links have stopped.

**You should see:** new taps in **Dashboard → Analytics**, and old OneLink links getting fewer taps each week.

## Frequently asked questions

### What doesn't move from AppsFlyer?

Attribution and analytics history stay in AppsFlyer, so export the reports you need first. Also not moved: the `onelink.me` domain (AppsFlyer owns it), links that aren't in the dashboard export, and parameters that only AppsFlyer reads (`is_retargeting`, `af_adset`, `af_sub1` and the like; the preview lists them).

### Can I bring my OneLink branded domain?

Not yet. Custom domains are coming soon. Until then, new links use `<handle>.strait.link`.

### What does Strait cost?

Free up to 25,000 taps a month, no card. App opens are never billed. Paid plans are on [Pricing](https://straitlink.in/pricing/).

## Common errors

- **A row says it has an in-app destination but no `af_web_dp`.** Type your website in **Your website** and preview again.
- **A short path is reported as taken.** Another link in your workspace already uses it. Rename it in the file, or keep the existing link.
- **The app opens the website, not the app.** The new build with `hilltop.strait.link` isn't installed, or fingerprints or Team ID are missing in **Apps & stores**. Run `doctor()` in a debug build.

## Next steps

- [Test](https://straitlink.in/docs/sdks/react-native/test/): check direct and deferred links on a real phone.
- [Migrate from Branch](https://straitlink.in/docs/migrate-from-branch/): the same import for Branch exports.
