> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Deep linking

> Send users from OneSignal push, in-app, email, and SMS messages to a specific screen in your Android or iOS app using Universal Links, App Links, or custom URI schemes.

## Overview

A deep link routes the recipient of a OneSignal message to a specific screen in your app instead of a web page. OneSignal delivers the link to the device; the operating system and your app are responsible for resolving it.

This guide covers how to use deep links **inside OneSignal messages** — it does not cover how to configure your app to receive them. For platform setup, see Apple's [Supporting universal links in your app](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app) and Google's [About deep links](https://developer.android.com/training/app-links).

***

## Prerequisites

Before you can deep-link from a OneSignal message, your app must already be set up to receive the link type you plan to use:

* The [OneSignal Mobile SDK](./mobile-sdk-setup) is installed and initialized.
* Your app developers have configured a deep link handler for **each platform you target**:
  * **iOS** — [Universal Links](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app) (Associated Domains entitlement + [apple-app-site-association (AASA) file](https://developer.apple.com/documentation/xcode/supporting-associated-domains) hosted on your domain) and/or a [custom URL scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app) registered in `CFBundleURLTypes`.
  * **Android** — [App Links](https://developer.android.com/training/app-links) (intent filters with `autoVerify="true"` + [Digital Asset Links file (`assetlinks.json`)](https://developer.android.com/training/app-links/verify-applinks) hosted on your domain) and/or a custom URI scheme registered in your `AndroidManifest.xml`.
* For email and SMS/RCS, only Universal Links / App Links work — custom URI schemes are not clickable from those clients.

Once a link is working on the device for a regular tap (paste into Notes on iOS or run `adb shell am start -a android.intent.action.VIEW -d "<url>"` on Android), OneSignal can deliver it through any supported channel.

***

## Choose a link type

| Type                         | URL format                    | Supported in OneSignal       | Fallback when app is not installed |
| ---------------------------- | ----------------------------- | ---------------------------- | ---------------------------------- |
| **Universal Links** (iOS 9+) | `https://yourdomain.com/path` | Push, in-app, email, SMS/RCS | Opens the URL in Safari            |
| **App Links** (Android 6+)   | `https://yourdomain.com/path` | Push, in-app, email, SMS/RCS | Opens the URL in the browser       |
| **Custom URI schemes**       | `myapp://path`                | Push, in-app only            | Fails silently or shows an error   |

**Recommendation:** Use Universal Links / App Links (`https://`) for anything sent via email or SMS — custom schemes do not work in those clients. For push and in-app messages, either works.

<Info>
  On Android 12+, an `https://` link that is **not** a verified App Link opens in the user's default browser, not your app. If deep links from push or email are opening the browser on Android, the App Link verification is the first thing to check.
</Info>

***

## Send a deep link in a push notification

OneSignal supports two ways to attach a deep link to a push notification. Pick one based on how much control you want over navigation.

### Launch URL (`url` / `app_url`)

Set the Launch URL in the dashboard, or pass `url` (all platforms) or `app_url` (mobile only) in the API.

```json theme={null}
{
  "app_id": "YOUR_APP_ID",
  "contents": { "en": "Your order shipped" },
  "url": "https://yourdomain.com/orders/12345"
}
```

| Platform | Behavior when the notification is tapped                                                                                                                                               |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Android  | Android resolves the URL. Verified App Link → opens directly in your app. Non-verified `https://` → opens the browser. Custom scheme → opens the registered app.                       |
| iOS      | OneSignal's iOS SDK calls `openURL`, which opens Safari first, then bounces to your app through the Universal Link handshake. See [iOS Launch URL behavior](#ios-launch-url-behavior). |

### Additional Data (`data`) — recommended for mobile

Put the destination in Additional Data instead and do the navigation yourself in the [push click listener](#handle-deep-links-in-your-app). This avoids the iOS browser bounce entirely and lets you pass structured context.

```json theme={null}
{
  "app_id": "YOUR_APP_ID",
  "contents": { "en": "Your order shipped" },
  "data": {
    "deep_link": "https://yourdomain.com/orders/12345",
    "order_id": "12345"
  }
}
```

<Tip>
  For mobile push, prefer Additional Data + a click listener. You get full control over navigation, no browser flash on iOS, and you can pass extra fields (like `order_id`) that aren't part of the URL.
</Tip>

### iOS Launch URL behavior

OneSignal's iOS SDK uses `openURL` to handle the `url` / `app_url` property. Even with Universal Links correctly configured, this causes the link to open in the browser first and then redirect back into the app — a noticeable "flash" for users.

You have two ways to avoid it:

1. **Use `data` instead of `url`** and route from your [push click listener](#handle-deep-links-in-your-app). Recommended.
2. **Suppress Launch URLs globally.** Add a Boolean `OneSignal_suppress_launch_urls` with value `YES` to your app's `Info.plist`. OneSignal will stop calling `openURL`, and you must handle every deep link in the click listener.

***

## Send a deep link in an in-app message

In-app messages use click actions instead of a URL property. For the full list of click action types, see [In-app click actions](./iam-click-actions).

### Drag-and-drop editor

1. Add a button, image, or background click target.
2. Open **Add click action**.
3. Choose one of:
   * **URL** — opens the link in the device's browser (or, if Universal Links / App Links are configured for that domain, opens your app).
   * **Custom action ID** — passes the value to your [in-app click listener](#handle-deep-links-in-your-app) without opening anything. Use this when you want to navigate inside the app you control.
4. Enter your deep link (e.g. `https://yourdomain.com/promo`) or action identifier (e.g. `open_promo`).

### HTML editor

In sandboxed HTML in-app messages, use the [In-App Message JS API](./in-app-message-api):

* `OneSignalIamApi.openUrl(e)` — opens the URL in the device browser using default URL handling.
* `OneSignalIamApi.addClickName(e)` — sends a custom click name to your [in-app click listener](#handle-deep-links-in-your-app) without opening a browser.

```html theme={null}
<button id="open-in-app" data-onesignal-unique-label="open-in-app">Check it out!</button>
<script>
  document.getElementById("open-in-app").addEventListener("click", function(e) {
    OneSignalIamApi.addClickName(e);
  });
</script>
```

***

## Send a deep link in an email

By default, OneSignal rewrites every link in an email for click tracking. The rewritten URL uses OneSignal's tracking domain, which does **not** match your app's Associated Domain or Digital Asset Links host — so iOS and Android treat the link as a regular web URL and open the browser instead of your app.

To preserve deep linking in email, disable click tracking for the deep link using one of these options:

| Scope                       | How                                                                                                                                                             |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Whole email (dashboard)** | Uncheck **Track link clicks** in the email editor.                                                                                                              |
| **Whole email (API)**       | Set `disable_email_click_tracking: true` in the Create message request.                                                                                         |
| **Single link (HTML)**      | Add `disable-tracking=true` to the anchor tag: `<a href="https://yourdomain.com/path" disable-tracking=true>…</a>`. Tracking stays enabled on every other link. |

<Frame caption="Track link clicks disabled in the email editor.">
  <img src="https://mintcdn.com/onesignal/jBdBk5XvQR5eKOks/images/docs/787e21d-Screenshot_2024-05-29_at_2.21.43_PM.png?fit=max&auto=format&n=jBdBk5XvQR5eKOks&q=85&s=f7e1079f4860ecf34fd95375a4332573" alt="OneSignal email editor with Track link clicks checkbox unchecked" width="1756" height="492" data-path="images/docs/787e21d-Screenshot_2024-05-29_at_2.21.43_PM.png" />
</Frame>

<Warning>
  Disabling tracking removes click metrics from [Email Message Reports](./email-message-reports). Use the per-link `disable-tracking=true` attribute to keep analytics on non–deep-link URLs.
</Warning>

### Expected behavior

| Scenario                                                          | Result                                                                |
| ----------------------------------------------------------------- | --------------------------------------------------------------------- |
| iOS + Safari + Universal Link + tracking disabled                 | Opens app directly                                                    |
| iOS + Safari + Universal Link + tracking enabled                  | Opens Safari, link doesn't match AASA, stays in browser               |
| iOS + non-Safari mail client + Universal Link + app not installed | Falls back to web; can redirect to App Store                          |
| Android + App Link + tracking disabled                            | Opens app directly                                                    |
| Android + App Link + tracking enabled                             | Opens browser, link doesn't match `assetlinks.json`, stays in browser |

***

## Send a deep link in SMS or RCS

SMS and RCS messages support only `https://` links — custom schemes like `myapp://` won't open from the Messages app. Insert the link inline in the message body.

If you use OneSignal's trackable link feature (`{{ "https://..." | track_link }}` or the **Insert Trackable Link** button), the URL is replaced with a `1sgnl.co` short link, which breaks Universal Link / App Link domain matching. Do not wrap deep-link URLs in `track_link` — send them raw so the OS can match them to your app.

See [URLs, links, & deep links → SMS/RCS trackable links](./links#sms%2Frcs-trackable-links) for the tracking-link format.

***

## Handle deep links in your app

The most reliable way to route a OneSignal deep link is to intercept it with a click listener and do the navigation yourself. This works regardless of platform quirks (iOS `openURL`, Android 12+ verification, email tracking rewrites) as long as your listener receives the event.

### Push notification click listener

Register the listener as early as possible — in `Application.onCreate()` on Android or `application(_:didFinishLaunchingWithOptions:)` on iOS — so it is active before the user taps a cold-start notification.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  OneSignal.Notifications.addClickListener { event ->
    val url = event.notification.launchURL
    val data = event.notification.additionalData
    // Route to the correct screen based on url or data
  }
  ```

  ```swift Swift theme={null}
  class AppDelegate: UIResponder, UIApplicationDelegate, OSNotificationClickListener {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
      OneSignal.Notifications.addClickListener(self)
      return true
    }

    func onClick(event: OSNotificationClickEvent) {
      let url = event.notification.launchURL
      let data = event.notification.additionalData
      // Route to the correct screen based on url or data
    }
  }
  ```

  ```javascript React Native theme={null}
  OneSignal.Notifications.addEventListener('click', (event) => {
    const url = event.notification.launchURL;
    const data = event.notification.additionalData;
    // Route to the correct screen based on url or data
  });
  ```

  ```dart Flutter theme={null}
  OneSignal.Notifications.addClickListener((event) {
    final url = event.notification.launchUrl;
    final data = event.notification.additionalData;
    // Route to the correct screen based on url or data
  });
  ```

  ```csharp Unity (C#) theme={null}
  OneSignal.Notifications.Clicked += (sender, e) => {
    var url = e.Notification.LaunchURL;
    var data = e.Notification.AdditionalData;
    // Route to the correct screen based on url or data
  };
  ```
</CodeGroup>

For the full API including Java, Objective-C, and Cordova/Ionic, see [`addClickListener()` Push](./mobile-sdk-reference#addclicklistener-push).

### In-app message click listener

Fires when a user taps a button configured with a **Custom action ID** (or when `OneSignalIamApi.addClickName` is called in HTML).

<CodeGroup>
  ```kotlin Kotlin theme={null}
  OneSignal.InAppMessages.addClickListener { event ->
    val actionId = event.result.actionId
    // Route based on the action ID (your deep link or internal route name)
  }
  ```

  ```swift Swift theme={null}
  func onClick(event: OSInAppMessageClickEvent) {
    let actionId = event.result.actionId
    // Route based on the action ID (your deep link or internal route name)
  }
  ```

  ```javascript React Native theme={null}
  OneSignal.InAppMessages.addEventListener('click', (event) => {
    const actionId = event.result.actionId;
    // Route based on the action ID (your deep link or internal route name)
  });
  ```

  ```dart Flutter theme={null}
  OneSignal.InAppMessages.addClickListener((event) {
    final actionId = event.result.actionId;
    // Route based on the action ID (your deep link or internal route name)
  });
  ```
</CodeGroup>

For the full API, see [`addClickListener()` In-App](./mobile-sdk-reference#addclicklistener-in-app).

***

## Personalize deep links per user

Every URL field in OneSignal — Launch URL, Additional Data values, in-app URL actions, email body links — accepts [Liquid syntax](./using-liquid-syntax). Use it to inject the user's `external_id`, tags, or `custom_data` into the path or query string.

```text theme={null}
https://yourdomain.com/profile/{{subscription.external_id}}
https://yourdomain.com/orders/{{message.custom_data.order_id}}
https://yourdomain.com/topic/{{ interest | default: 'home' }}
```

See [Dynamic URLs](./links#dynamic-urls) for the full list of data sources and fallback filters.

***

## Test deep links from OneSignal

1. Send a test message from the OneSignal dashboard with your deep link as the Launch URL, Additional Data value, or in-app click action.
2. Tap the notification or in-app button on a real device (Universal Links do not activate in the iOS Simulator from all sources).
3. Confirm the app opens directly to the right screen and your click listener logs the expected URL or action ID.

For platform-level verification (before involving OneSignal), Apple provides the [Associated Domains validation tool](https://search.developer.apple.com/appsearch-validation-tool/) and Android offers `adb shell pm verify-app-links --re-verify <package>`.

<Note>
  On iOS, Universal Links do not activate from the Safari address bar. Test from Notes, Mail, Messages, or a OneSignal push/in-app message.
</Note>

***

## Troubleshooting

### iOS push opens Safari for a second before my app

OneSignal's SDK uses `openURL` for the Launch URL. Fix with one of the [two approaches above](#ios-launch-url-behavior): send the link in Additional Data and route from the click listener, or set `OneSignal_suppress_launch_urls=YES` in `Info.plist`.

### Android push opens the browser instead of the app

The `https://` link is not being recognized as a verified App Link. Check that your intent filter uses `android:autoVerify="true"`, that `assetlinks.json` is reachable at `https://yourdomain.com/.well-known/assetlinks.json` (HTTP 200, `application/json`, no redirects), and that its SHA-256 fingerprint matches the certificate you actually shipped (Play App Signing uses a different fingerprint than your local keystore). See Google's [Troubleshoot App Links](https://developer.android.com/training/app-links/troubleshoot).

### Deep link works in push but not in email

Email click tracking rewrites the URL and breaks domain matching. [Disable tracking](#send-a-deep-link-in-an-email) for that link or that email.

### Deep link is received but the app doesn't navigate

* The click listener was registered **after** the click event fired. Register in `Application.onCreate()` (Android) or `application(_:didFinishLaunchingWithOptions:)` (iOS), before any other async work.
* The `launchURL` or `actionId` doesn't match what your router expects. Log the raw value.
* On iOS, the Launch URL (`url`) is being used without suppressing it — the browser may consume the link before your listener fires. Switch to Additional Data.

### I updated my AASA file and iOS still doesn't open the app

Starting in iOS 14 / macOS 11, Apple's CDN fetches AASA files, not the device. The CDN requests a refresh within 24 hours, and installed devices re-check roughly once per week. Reinstall the app to force an immediate fetch during testing.

***

## FAQ

### Should I use Launch URL or Additional Data?

Additional Data is recommended for mobile deep links. It bypasses iOS's `openURL` browser flash and gives your click listener structured data instead of a string to parse. Launch URL is simpler and fine for web push or when you accept the iOS behavior.

### Do deep links work with OneSignal Journeys?

Yes. Set the Launch URL or Additional Data on any push or in-app step the same way you would for a one-off message. Email steps follow the same click-tracking rules — disable tracking on the deep link.

### Can I deep link into another app (not mine)?

Yes, for push and in-app only. Set a custom URL scheme that the other app registers (e.g. `whatsapp://send?phone=15551234567`). Custom schemes do not work in email or SMS clients.

### What happens if the user doesn't have the app installed?

With Universal Links (iOS) or App Links (Android), the `https://` URL loads as a normal web page — you can host a landing page that redirects to the App Store / Play Store. With a custom URI scheme (`myapp://`), the tap fails silently or shows an error; there is no fallback.

### Can I use a trackable SMS link as a deep link?

No. OneSignal's SMS trackable links rewrite the URL to `1sgnl.co`, which isn't associated with your app. Send the original `https://` URL so the OS can match it to your Associated Domain or Digital Asset Links entry.

### Do I need the OneSignal SDK to handle a deep link?

No, but it's recommended. Universal Links / App Links are handled by the OS regardless of which SDK sent the message. Using OneSignal's click listener gives you a consistent entry point across channels and avoids platform-specific quirks like the iOS `openURL` flash.

***

<Columns cols={2}>
  <Card title="URLs, links, & deep links" icon="link" href="./links">
    Launch URLs, UTM parameters, dynamic URLs, and click tracking across every channel.
  </Card>

  <Card title="Mobile SDK reference" icon="code" href="./mobile-sdk-reference">
    Full API reference for push and in-app click listeners and notification events.
  </Card>

  <Card title="In-app click actions" icon="hand-pointer" href="./iam-click-actions">
    Configure URL, custom action ID, and other click actions for in-app buttons.
  </Card>

  <Card title="Mobile push setup" icon="mobile" href="./mobile-push-setup">
    Platform-specific push notification setup for Android and iOS.
  </Card>
</Columns>
