---
title: "Email destinations in Adapty Mail"
description: "The page each Adapty Mail campaign sends people to: the three destination types, the placeholders that personalize the URL, and the hook that fills the rest."
---

> **AI agents**: to search Adapty docs faster and with fewer tokens, install the Adapty skill. Claude Code (self-updating via plugin): `claude plugin marketplace add adaptyteam/adapty-skills && claude plugin install adapty-skills@adapty` — other tools: `npx skills add adaptyteam/adapty-skills --all`

Every campaign needs somewhere to send its recipients. That place is an **email destination** — a saved name, URL, and type that you set up once and reuse across campaigns.

A campaign points at exactly one destination, and every clickable part of its emails leads there: the CTA button, the logo, and the hero image. They are recorded separately in [analytics](mail-analytics), but they share an address.

## Destination types

The purpose of your campaign decides the destination type. You can't change the destination type after you create it.

| Type | Compatible campaigns | Where it takes the recipient | Where the URL comes from |
| --- | --- | --- | --- |
| **Web Paywall** | Every marketing campaign, and [double opt-in](mail-double-opt-in) | A checkout page in the browser, where purchases are web payments rather than App Store or Google Play transactions. | Generated by Adapty Mail, or pasted from a paywall you host. [Set up checkout](mail-checkout) covers both. |
| **Deep Link** | App activation | Your app, at a screen you choose. Without the app installed, your store listing instead. | Pasted, from your linking provider — OneLink, Branch, or Firebase. |
| **Cancellation Page** | The cancellation page campaign | Your own account page, or your billing provider's portal, where a subscriber reviews, changes, or cancels. | Pasted, from your own site or your billing provider. |

:::note
Any destination URL is valid as long as it uses HTTPS. So a **Cancellation Page** can be a universal link that opens your app, not just a web page. That matters because App Store and Google Play subscriptions are cancelled in the store, not on a page you host.
:::

### Destinations for transactional campaigns

Adapty Mail has three transactional campaigns:

- **Double opt-in** — fires when a new profile arrives from your [entry point](mail-entry-points). Its button opens Adapty Mail's confirmation page. Once the recipient confirms, they go on to your web paywall — which is why this campaign needs one.
- **App activation** — follows a purchase, and gets the buyer into your app.
- **Cancellation page** — follows a purchase, and hands the buyer the subscription controls.

When you create a campaign, an empty **Email Destination** dropdown means you have no destination of that type yet. Its **Create** link opens the right form.

## Create a destination

1. Go to **Email Destinations** and select the type your campaign needs.
2. Click **Create**.
3. Give the destination a **Name** — you'll pick it by that name when you attach it to a campaign.
4. Fill in the **URL**. It must use HTTPS.
5. Add [placeholders](#placeholders) for the values that differ per recipient.
6. If the URL uses the `{response.<key>}` placeholder, add a [**Hook URL**](#hooks) under **Advanced**.
7. Click **Save**.

Destination settings become read-only when you add it to a campaign — even a draft campaign. To send a campaign somewhere else, create a second destination.

## Placeholders

Placeholders personalize your destination URL for each user. Without them your page can't tell who arrived. Adapty Mail replaces the placeholders with values unique to the recipients.

| Placeholder | What it resolves to |
| --- | --- |
| `{email}` | The recipient's email address. |
| `{adapty.external_profile_id}` | Their profile ID, for data from the [Adapty integration](mail-collect-emails). |
| `{funnelfox.external_profile_id}` | Their profile ID, for data from [FunnelFox](mail-funnelfox). |
| `{custom.external_profile_id}` | The user ID you sent, for data from the [Adapty Mail API](mail-send-data-via-api). |
| `{scheduled_email_id}` | The ID of the email that carried the link. |
| `{custom_attribute.<key>}` | Any custom attribute on the recipient's profile. Name the key yourself — `{custom_attribute.plan}`. |
| `{response.<key>}` | A value your own endpoint returns at click time. Covered in [Hooks](#hooks). |

- A placeholder the recipient has no value for resolves to an empty string, so it never reaches your page as literal text.
- Custom attribute and hook values are percent-encoded, so a value containing `&` can't cut the URL in half.
- A placeholder Adapty Mail doesn't recognize blocks the save, and the error names both what it rejected and what it accepts. A typo such as `{emial}` is caught here rather than in a recipient's browser.

## Hooks

The `{response.<key>}` placeholder reserves space for a value that comes from your own endpoint. Common uses include a one-time sign-in token, or a session signature. Create a new **Hook URL** under **Advanced** to configure this placeholder — otherwise, the URL can't be saved.

How it works:

1. The recipient clicks a link in the email.
2. Adapty Mail sends a GET to your **Hook URL**, with its placeholders filled for that recipient. It waits five seconds and makes one attempt.
3. Your endpoint verifies the signature and answers with a flat JSON object.
4. Adapty Mail fills each `{response.<key>}` from the key of the same name. A key you don't return keeps its placeholder.
5. The recipient lands on your page, whether or not the hook answered.

### The request

The hook URL forwards the rest of your placeholders to your endpoint. Use these placeholders to ID the user. Two headers authenticate the call:

| Header | Value |
| --- | --- |
| `X-Adapty-Mail-Timestamp` | Unix time in seconds, when the request was made. |
| `X-Adapty-Mail-Signature` | Hex HMAC-SHA256 of `<timestamp>.<url>`, keyed with the **Secret key** from **Settings** > **Project**. |

Verify both before you answer. An unsigned endpoint can hand sensitive info to unauthorized users. Reject a timestamp too old to be a live click:

```
expected = hmac_sha256(
    key=project_secret_key,
    message=f"{timestamp}.{full_request_url}",
).hexdigest()
```

### The response

A flat JSON object. Each key fills the `{response.<key>}` of the same name:

```json
{ "checkout_token": "ct_9f2a…", "plan": "annual" }
```

Strings, numbers, and booleans all become strings. Nested objects and arrays are dropped, since a URL has nowhere to put them.

## What happens after the click

Adapty Mail fills the values, calls the hook if there is one, records the click, and sends the recipient to your page.

Your job is to identify the visitor, take their payment, and report the purchase back to Adapty. See [Set up checkout](mail-checkout#what-your-page-has-to-do) for paywall setup, and [Campaign analytics](mail-analytics) for purchase attribution.