<!-- AI agents: this is the Markdown version of a Reception documentation page. Index: /llms.txt -->

# Link cards

Send a message with a button. The button opens either a **link**, such as a help page, another app, an offer code or an email address, or a **screen** of your own app. You save links in the dashboard, so a new link needs no app update.

## What a link card is {#how-it-works}

A link card is an agent message with a button. The person sees your optional message, then the button with your label. A button opens one of two things:

| Target | Example | Code in your app |
| --- | --- | --- |
| A link: a website, another app like Instagram or WhatsApp, the App Store or an offer code, Mail, the phone, Messages, or your app's page in iOS Settings | `https://instagram.com/yourapp` | None |
| A screen of your app: a tab, a screen, a sheet, the paywall | Settings, Order 123 | Once, a list of your screens; see [Screens](#screens) |

Link cards need Reception iOS SDK 1.7.0 or later or react-native-reception 1.5.0 or later; every reception_flutter version has them. Older app versions get links as text; see [Older app versions](#older-sdks). Link cards work in iOS conversations only; the website chat can't send them.

## Save links in the dashboard {#dashboard}

An Admin manages each app's links in **Settings → your app → Links**. **Add a link** starts with a choice:

- **Link**: paste any link. A website, or another app's link such as an Instagram profile or a WhatsApp chat (`https://wa.me/…`), opens that app when it's installed and its website otherwise. An email address becomes a Mail link, and `example.com/help` becomes `https://example.com/help`. **Presets** fill in links you can't simply copy: Website, Offer code, Email, Phone call, Text message and App settings in iOS. The field shows what the link opens.
- **Screen in your app**: pick one of the screens your app lists. Each shows how many people's app version has it. A screen that takes a value, such as an order number, asks for it. Without listed screens, the tab offers **Copy prompt for your coding agent**; see [Screens](#screens).

Then check the **Button text** the person sees (up to 30 characters, sent exactly as written) and the **Name** your team sees (up to 60 characters). Both are suggested and can be renamed. To ask for an App Store review, use the review card instead of a link.

An app can have up to 50 links. Each link shows **Sent**, **Tapped** and **Failed** for the last 30 days. Sent counts every send, including the text an older app version gets instead of a card. Tapped counts taps; Failed counts taps where nothing could open the link, for example an app that isn't installed. A tap is not a purchase or a completed form. A screen link that no installed app version lists is marked **Not in the app**.

Editing a link changes later sends only. A sent message keeps the URL and label it was sent with. Deleting a link keeps messages already sent.

### Let the AI send a link {#ai}

Turn on **Let the AI send this link** and fill in **When to send**: a short description the AI reads to decide when the link fits, for example "When someone asks how to export their data." The same switch also appears in the AI settings under **Offers, reviews and links**.

The AI sends only saved links with this switch on, and only when the description matches what the person needs. It never invents or edits a URL.

## Send a link from the inbox {#send}

1. Open the person's latest iOS conversation in **Inbox**. Select **Reopen** if it is closed.
2. Select **Send link**, next to **Ask for review** and **Send offer**.
3. Pick a saved link, one of the screens the person's app version lists, or **Custom link** to type a link and button text for this one send, for example a personal offer code. A screen starts with a button text named after it, which you can change; a screen that takes a value asks for it. Screens and custom links are sent once and not saved. With many links, search them by name.
4. Optionally write a message above the button. Check the live preview.
5. Send it. The card appears in the conversation.

A screen the person's app version doesn't list is greyed out, with "Not in this person's app version". When the person taps the button, the conversation shows "User tapped [button label]". When nothing could open the link, it shows in red: "User tapped [button label], but the link could not be opened". Repeated taps count once.

Link cards push like any other reply. The push shows your message, or the button label when the message is empty. Links are not offers, so there is no repeat confirmation; send the same link as often as it helps.

### Automations {#automations}

Add the action **Send a link** to an [automation](/docs/automations) and pick a saved link. Automations send saved links only; add one in **Settings → your app → Links** first. If the link is deleted later, or it opens a screen the person's app version doesn't list, the automation skips that step's messages for the person and shows the reason under **Skipped**.

## Screens {#screens}

To let buttons open screens of your app, list them once at launch, after `Reception.configure`, on the main actor. Each `ReceptionScreen` has an ID and the code that opens it, exactly as a tap inside your app would. A screen can be anything your code can show: a tab, a pushed screen, a sheet, the paywall.

```swift
import Reception

Reception.shared.screens = [
    ReceptionScreen("settings") { router.selectedTab = .settings },
    ReceptionScreen("notifications") { router.open(.settings, .notifications) },
    ReceptionScreen("paywall") { router.showsPaywall = true },
    ReceptionScreen("order", value: "Order number") { number in router.openOrder(number) },
]
```

A screen that needs something from the link, such as which order, takes a `value`. Its name, here "Order number", labels the field in the dashboard, and every link to that screen carries one.

That is all: no URL scheme, `onOpenURL` or router setup. The SDK reports the screen IDs with the device, and they appear under **Add a link → Screen in your app**. When the person taps the button, the SDK closes the chat it presented, then runs the screen's code. A `ReceptionChatView` you placed yourself stays open.

A link to a screen reaches only devices whose app version lists it: the inbox greys it out for others, and automations and the AI skip them until they update. So a screen you add in version 2.1 never reaches someone on 2.0, with nothing to configure.

**Copy prompt for your coding agent** in the dialog has an agent like Claude Code or Cursor find your app's screens, write this list and test each one.

IDs use letters, digits, `_`, `-` or `.`, at most 64 characters, and start with a letter or digit. Keep them stable: a renamed ID is a new screen, and links to the old ID stop reaching new app versions. Up to 50 screens are reported.

### Build a link by hand {#recipes}

What to put in the Link field, or send through the API:

| To open | Link |
| --- | --- |
| A web page | `https://example.com/help` |
| Another app | Its link, from the app's Share → Copy link, such as `https://instagram.com/yourapp` or `https://youtu.be/…` |
| A WhatsApp chat | `https://wa.me/14155550123?text=Hi` (number with country code, no `+`) |
| An App Store offer code | `https://apps.apple.com/redeem?ctx=offercodes&id=…&code=…` from App Store Connect |
| Your app's App Store page | `https://apps.apple.com/app/id1234567890` |
| Your app's page in iOS Settings | `app-settings:` (notifications, permissions, language) |
| Mail | `mailto:support@example.com?subject=Question` |
| A phone call | `tel:+14155550123` |
| Messages | `sms:+14155550123&body=Hi` |
| A screen your app lists | `reception-screen:settings`, or `reception-screen:order?value=123` for a screen with a value |

Encode spaces and special characters in texts, like `Hi%20there`. A link with an app's own scheme, such as `instagram://` or your own `myapp://`, goes to iOS as it is, but opens only when that app is installed; otherwise the tap fails. Prefer the app's `https` link, which opens its website for people without the app. The dashboard points this out.

## What happens on tap {#on-tap}

| Link | What happens | The chat |
| --- | --- | --- |
| Website | If an installed app owns the link (a universal link, such as `https://wa.me/…` for WhatsApp), that app opens, as from Messages or Mail. Otherwise an in-app Safari sheet opens the page over the chat; Done returns to the chat. Set `Reception.shared.opensWebLinksInApp = false` to hand every web link to iOS instead. | Stays open |
| App Store page | The App Store opens the app page. | Stays open |
| App Store offer code | The App Store opens with the code filled in. The person confirms, and StoreKit reports the transaction to your app as usual, through `Transaction.updates` or your purchase SDK. | Stays open |
| Mail, phone, Messages, iOS Settings, another app's scheme | iOS opens it. Nothing installed for it counts as a failed tap. | Stays open |
| Your own URL scheme | Your app's URL handler gets the URL. | Closes |
| Screen | The screen's code runs. | Closes |

When the person comes back to your app after an external link, the conversation is still there.

## Older app versions {#older-sdks}

Devices running SDK 1.6 or older can't show link cards. Reception sends them a text message instead, with the URL on its own line after your message. This applies to the inbox, automations and the AI. The send dialog shows a hint when the device runs an older version, for example "This person's app runs SDK 1.5.0. The link is sent as text; buttons need SDK 1.7.0 or newer." Older SDKs show the URL as plain text that the person can select and copy.

A link to a screen, to another app's scheme or to iOS Settings would open nothing as text, so it can't be sent to them. The send dialog says, for example, "This person's app runs SDK 1.5.0. Links to a screen or another app need SDK 1.7.0 or newer."

## Tappable links in messages {#text-links}

With SDK 1.7 or later, `http` and `https` URLs in any message are tappable, in messages from your team and from the person. They open like a website link card (the owning app, the in-app Safari sheet, or iOS when `opensWebLinksInApp` is false) and report no tap to the dashboard.

## Events {#events}

The SDK emits `.linkOpened` (`linkOpened` in React Native) when the person taps a link card. It reports the tap, not the result: not that the page loaded, a code was redeemed or your screen opened. See [Events](/docs/events).

## React Native and Flutter {#react-native}

With react-native-reception 1.5.0 or later, links work without code. To open screens, list them once at startup, like [Screens](#screens) in Swift:

```typescript
import { router } from 'expo-router';
import { Reception } from 'react-native-reception';

Reception.screens = [
  { id: 'settings', open: () => router.push('/settings') },
  { id: 'paywall', open: () => showPaywall() },
  { id: 'order', value: 'Order number', open: (number) => router.push(`/orders/${number}`) },
];
```

`open` runs after the chat closes. Set the list again at startup after a JavaScript reload; until then the app reports no screens.

With reception_flutter, links work without code too. Screens use `Reception.screens` in Dart:

```dart
Reception.screens = [
  ReceptionScreen('settings', onOpen: () => router.push('/settings')),
  ReceptionScreen.withValue('order', value: 'Order number', onOpenWithValue: (number) => router.push('/orders/$number')),
];
```

See [Flutter](/docs/flutter#screens).

## Apple notes {#apple}

Link cards need no entitlement, capability or App Review request. Offer-code links are Apple's own redemption links, created in App Store Connect.

Links that lead to buying digital goods or subscriptions outside the App Store violate [App Review Guideline 3.1.1](https://developer.apple.com/app-store/review/guidelines/#in-app-purchase). What you link to is your responsibility as the app owner.

## Verify on a device {#verify}

Send every new link to your own device once and tap it.

1. Open support in your app on a real device and send a message, so your conversation appears in the inbox.
2. Select **Send link**, pick the link and send it.
3. Tap the button. Check that the right page, app, App Store screen or screen of your app opens.
4. Check the dashboard: the conversation shows "User tapped [button label]", and the link's **Tapped** count goes up.

For offer codes, use a code meant for testing; a real redemption uses up a one-time code. A simulator can show the card, but use a real device for App Store links and other apps.

## Troubleshooting {#troubleshooting}

| Symptom | Check |
| --- | --- |
| The person sees text with a URL instead of a button | The app runs SDK 1.6 or older. Update the SDK to 1.7.0 or later. |
| A message with this link shows no button on SDK 1.7 | The URL is not valid for the SDK. Check it in **Settings → your app → Links** and send it again. |
| **Screen in your app** shows no screens | The app doesn't set `Reception.shared.screens` yet, or no device has started a version that does. Add the list, then open the app and its support chat once. |
| A screen is greyed out for a person | Their app version doesn't list it. They get it after updating. |
| The chat closes but the screen doesn't open | Check the screen's code in `Reception.shared.screens`; it runs on the main actor right after the chat closed. |
| The conversation shows a link "could not be opened" | Nothing on the device could open it, for example an app that isn't installed. The link's **Failed** count shows how often this happens. |
| The AI never sends a link | Turn on **Let the AI send this link** and write a clear **When to send** description. |
| No tap appears in the dashboard | Tap from a device with SDK 1.7 or later and check its connection. Previews in the dashboard record nothing. |

For general delivery issues, see [Troubleshooting](/docs/troubleshooting).
