# Deferred deep links

When someone opens a link but doesn't have your app yet, Mocco sends them to the store. A **deferred deep link** lets the app open the linked screen anyway, at its first launch after the install. Mocco also counts every time a link opens your app, so a link's stats over the API count opens and first launches next to clicks.

This page covers the calls your app makes. In a React Native app, [Mocco's SDK](https://www.mocco.club/docs/en/articles/ww6qzh-react-native) makes them for you; apps without it (native Android or iOS, Flutter) call the API directly.

## Before you start

- Set up your link host and your app's details, and make links open the app (see [Make links open the app](https://www.mocco.club/docs/en/articles/ikc6rm-open-the-app)).
- Create a **Publishable** key with `links:app` under **API keys** in the project (see [Create links from your server or app](https://www.mocco.club/docs/en/articles/wmyyuw-api)). It is safe to ship in the app.

## How it works on Android

1. Someone without your app taps a link on an Android phone.
2. Mocco sends them to Google Play with a one-time code in the install referrer.
3. After installing, your app reads the install referrer once at first launch and sends it to Mocco.
4. Mocco answers the link, and your app opens its screen.

The code works once: the first install that sends it gets the link, and the same install asking again gets the same link. Codes stop working 7 days after the tap. If something goes wrong along the way, Mocco answers `none` and your app opens as usual.

Opens from a link are counted on both platforms.

## Deferred links on iOS

iOS has no install referrer, and Mocco doesn't guess who installed from what by matching IP addresses or browser details. Instead, the person carries the code across the install themselves:

1. Someone on an iPhone without your app taps a link. Mocco shows a short page instead of jumping to the App Store, with **Get the app**, **Open in app** and, when the link has a web fallback, **Continue on the web**.
2. Tapping **Get the app** copies a one-time code (`mocco:` and 22 letters and digits) to the clipboard, then opens the App Store. Nothing is copied until they tap, and nothing else is copied. The page says so.
3. At first launch, your app reads the clipboard once, and only if you turned that on. It sends the code to Mocco as `token`, and Mocco answers the link the same way as on Android.

**Open in app** is for people who already have the app but whose tap opened Safari instead of your app. It goes to the same link on your project's second link host (`<name>-open.<link domain>`), which makes iOS open the app. Without the app it goes straight to the App Store.

The page appears only for people on iOS who would otherwise go to the App Store, in Safari, Chrome and other browsers. Crawlers still get the share preview, and an iOS web fallback still wins. To send a link straight to the App Store, set **On iOS** to **Straight to the App Store** in the link's settings (`iosBehavior: "direct"` over the API and MCP).

### Reading the clipboard

Reading the clipboard may show iOS's paste prompt ("*Your app* would like to paste from *Safari*"). Two ways to handle it:

- **Read it at first launch.** Turn on `ios.readClipboardOnFirstLaunch` in the [React Native SDK](https://www.mocco.club/docs/en/articles/ww6qzh-react-native) with `expo-clipboard`. The SDK reads once, only at first launch, and ignores anything that isn't exactly a Mocco code. It never writes or clears the clipboard.
- **Let the person paste.** Show expo-clipboard's `ClipboardPasteButton` (iOS 16 and later) on your first screen, and pass what it gives you to `links.resolveFromPaste(text)`. A paste button shows no prompt, because the person chose to paste.

Without the SDK, read the clipboard yourself, check the whole string matches `^mocco:[A-Za-z0-9_-]{22}$`, and send the part after `mocco:` as `token` (Mocco also takes it with the prefix).

### App Store privacy label

If your app reads the clipboard, say so in App Store Connect's privacy details. Mocco's code is **usage data** about **product interaction**: it is **not linked** to the person's identity and **not used for tracking**. Check your own app's other data uses before you answer.

### How many people copied

The link's stats count **landing page copies** (`landingCopies`): how many people tapped **Get the app** and copied a code. Next to landing page views (clicks on iOS) and deferred links resolved (`deferredResolved`), they show how many people made it through the install.

## Every call sends the app's details

Each call carries three values:

- `installId`: a random UUID your app makes at first launch and keeps. Don't use a device identifier.
- `platform`: `ios` or `android`.
- `appVersion`: your app's version, such as `2.4.0`.

Mocco doesn't store the phone's IP address or browser details for these calls.

## At first launch: resolve the deferred link

On Android, read the install referrer with Google's Install Referrer library (`InstallReferrerClient`) **once**, at first launch. Play keeps it for about 90 days. Send the whole string as it is:

```http
POST /v1/links/deferred/resolve
Authorization: Bearer <publishable key>
Content-Type: application/json

{
  "installId": "6f1c2c3e-0d1b-4c4a-9a51-6a2f7c1e9b10",
  "platform": "android",
  "appVersion": "2.4.0",
  "referrer": "utm_source=google-play&utm_medium=organic&mocco_click=..."
}
```

Mocco finds its code in the string, even among other parameters. The answer is either the link:

```json
{
  "status": "resolved",
  "link": {
    "id": "…",
    "url": "https://acme.<link domain>/spring",
    "deepLinkPath": "/sale/spring",
    "params": { "promo": "SPRING" },
    "campaign": "spring-2026"
  }
}
```

or `{ "status": "none" }`, and your app opens as usual. Open the screen `deepLinkPath` names, with `params`.

Call it once at every install's first launch, even without a referrer: Mocco counts it as a first launch.

## When a link opens the app: ask what it means

When a link opens your app, the system hands it the short link (`https://acme.<link domain>/spring`), not the screen. Ask Mocco what it means:

```http
POST /v1/links/open
Authorization: Bearer <publishable key>
Content-Type: application/json

{
  "installId": "6f1c2c3e-0d1b-4c4a-9a51-6a2f7c1e9b10",
  "platform": "ios",
  "appVersion": "2.4.0",
  "url": "https://acme.<link domain>/spring"
}
```

The answer has the same shape. A link on your own link domain works too. A link that is disabled, expired, unknown or another project's answers `none`, so your app can open its home screen instead.

## Limits

One address can make 60 deferred calls and 120 open calls a minute. Past that, Mocco answers `429` with `Retry-After`.

## Where the numbers show

`GET /v1/links/{id}/stats` and the stats your AI assistant reads through Mocco's MCP tools count **opens** (`opens`), **first launches** (`firstOpens`), **deferred links resolved** (`deferredResolved`) and **iOS landing page copies** (`landingCopies`) next to clicks. The console's stats chart shows clicks.
