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

# In-app messages

> Show modals, banners, full-screen messages and content cards inside your own app from sequence steps, rendered with your app's own components

In-app messages reach contacts while they are using your product. A sequence
step decides who sees what and when; your app fetches the contact's in-app
feed and draws each item with its own components, so it always matches your
design.

| Kind | Formats | How long it stays |
| - | - | - |
| **Message** | Modal, banner (top or bottom), full screen | Shown once. It leaves the feed after it is displayed, clicked or dismissed, or after 30 days if nobody saw it. |
| **Content card** | A card in your app's feed | Until the contact dismisses it, or until an expiry you set. Pinned cards come first. |

In-app is included in every plan and does not use email credits. Nothing is
sent by email or push: if your app does not fetch the feed, contacts never see
the items.

## How it works

1. Add a **Show In-App** step to a sequence. Each contact who reaches it gets
   one item in their in-app feed, with merge tags like `{{FIRST_NAME|there}}`
   already filled in.
2. Your app fetches the signed-in contact's feed, either from the browser or
   from your backend.
3. Your app shows the items and reports `displayed`, `clicked`, `dismissed`
   and `read` back. Those reports power the step's stats and decide what stays
   in the feed.

### Set it up with an AI coding tool

In **Settings → In-app**, choose **Copy prompt** for a web app, or for a
mobile or server-rendered app. The prompt has your workspace's values filled
in and explains how to fetch the feed, render each format and report events.
Paste it into Cursor, Claude Code or Codex, or send it to your developer.

## Add an in-app step

In the sequence builder, add **Show In-App** under Messages. Choose:

* **Message** or **Content card**.
* For messages, the **format**: modal, banner or full screen. Banners go at
  the top or bottom.
* A **title** and **message** (at least one), and an optional https image.
* Up to two **buttons**. A button either opens a link (an https URL or an app
  deep link such as `myapp://billing`) or closes the item.
* Optional **extra data**: string key/values your app receives with the item,
  for example `screen: billing`. Merge tags work in values.
* An optional **expiry** in days (1 to 365), and for cards, **Pin to the top**.

On the **Design** tab, pick a style: **Brand** (white with your brand
buttons), **Soft** (tinted, pill buttons), **Bold** (your brand color as the
background) or **Dark**. Presets use the brand color and button style from
your Email design settings. You can then change the background, text and
button colors, the corner radius and the button corners.

Every item in the feed carries the resolved values in `style`
(`backgroundColor`, `textColor`, `mutedTextColor`, `buttonColor`,
`buttonTextColor`, `cornerRadius`, `buttonRadius`, `fontFamily`), so your app
can draw it in your brand without hard-coding colors. The preview shows the
result on a phone and on the web.

Through the API, set `style` on the step, for example
`{ "preset": "bold", "cornerRadius": 8 }`. When you update a step, the
`style` you send merges into its current style: set a value to `null` to go
back to the preset's value, or send `"style": null` to reset everything. If you
override a background color but not the text color on it, the text switches to
black or white when the preset's color would be hard to read.

A step never stops the journey: if the content is empty after merge tags are
filled in, the step is skipped and the contact moves on. Each contact gets at
most one item per step, even if the step is processed again.

You can also add in-app steps through the API with `type: "in_app"` on
[Create Sequence](../api-reference/sequences/create) or
[Update Sequence](../api-reference/sequences/update) steps, with
`sequenzy sequences create --steps-json` in the CLI, or with the
`insert_sequence_step` MCP tool.

## Fetch from the browser

Browser apps can call Sequenzy directly with your publishable site key
(**Settings → Integrations → Web tracking**) and an identity token that your
backend mints for the signed-in user. The token proves which contact the
browser speaks for, so one user can never read another user's feed.

1. After sign-in, your backend calls
   `POST /api/v1/web-tracking-identities` with a secret API key (scopes
   `commerce:write`, `automations:trigger` and `subscribers:write`) and
   `{ "publicKey": "seq_pk_...", "email": "<user email>" }`, and returns
   `identityToken` to the browser. If you already call `sequenzy.identify`,
   reuse that token.
2. The browser fetches the feed:

```bash theme={null}
curl -X POST "https://api.sequenzy.com/api/v1/in-app/feed" \
  --header "Content-Type: text/plain" \
  --data '{
    "companyId": "<your company ID>",
    "publicKey": "seq_pk_...",
    "email": "jane@example.com",
    "identityToken": "<identity token>",
    "kind": "card"
  }'
```

`kind` (`message` or `card`) and `limit` (1 to 100) are optional. The response
is `{ "success": true, "messages": [...] }`, with items in the
[List In-App Messages](../api-reference/in-app/list-messages) shape.

3. The browser reports events:

```bash theme={null}
curl -X POST "https://api.sequenzy.com/api/v1/in-app/feed/events" \
  --header "Content-Type: text/plain" \
  --data '{
    "companyId": "<your company ID>",
    "publicKey": "seq_pk_...",
    "email": "jane@example.com",
    "identityToken": "<identity token>",
    "messageId": "iam_123",
    "type": "displayed"
  }'
```

Send both as `text/plain` so browsers skip the CORS preflight. Requests come
from allowed origins only when the site key has an origin allowlist; mobile
apps that call these endpoints directly need a key without one, or should use
the backend endpoints below. Both endpoints are rate limited per IP address.

| Status | Meaning |
| - | - |
| 400 | Missing `companyId` or `messageId`, or an invalid `kind`, `limit` or `type` |
| 403 | The origin is not allowed, or the identity token is missing, expired or for another email |
| 404 | The item is not in this contact's feed |
| 429 | Too many requests from this IP address |

A valid identity for a contact that no longer exists returns an empty feed.

## Fetch from your backend

For iOS, Android, React Native and server-rendered apps, fetch through your
own backend with a secret API key:

* [List In-App Messages](../api-reference/in-app/list-messages) with the
  contact's `email` or `externalId` (`subscribers:read` scope).
* [Record In-App Event](../api-reference/in-app/record-event) for each report
  (`subscribers:write` scope). Forward only IDs that came from that user's own
  feed.

## Reporting events

| Event | When to send it | Effect |
| - | - | - |
| `displayed` | The item appeared on screen | Messages leave the open feed |
| `clicked` | A link button, or the item itself, was clicked | Counts every click; also marks it displayed |
| `dismissed` | The contact closed it | Leaves the feed; also marks it displayed |
| `read` | A card was opened or scrolled into view | Clears the unread state; also marks it displayed |

Only the first report of each type sets its timestamp, so retrying `displayed`,
`dismissed` and `read` is safe. Every `clicked` report adds to the click
count, so send it once per click.
Each event also appears in the contact's activity as `in_app.displayed`,
`in_app.clicked`, `in_app.dismissed` or `in_app.read`, next to
`in_app.created` when the step ran and `in_app.skipped` when it was skipped.

## Results

With stats turned on in the sequence flow, each in-app step shows how many
contacts got the item, the share that saw it and the share that clicked. The
same counts are available from
[Get In-App Step Stats](../api-reference/in-app/step-stats),
`sequenzy in-app stats <stepId>` and the `get_in_app_step_stats` MCP tool.

A contact's page lists their in-app items with what happened to each, and lets
you remove an item that is still waiting. Deleting a contact deletes their
in-app items.

## CLI and MCP

| Task | CLI | MCP tool |
| - | - | - |
| List a contact's feed | `sequenzy in-app list --email jane@example.com` | `list_in_app_messages` |
| Report an event | `sequenzy in-app event <id> --type dismissed` | `record_in_app_event` |
| Remove an item | `sequenzy in-app delete <id>` | `delete_in_app_message` |
| Step stats | `sequenzy in-app stats <stepId>` | `get_in_app_step_stats` |
| Add a step | `sequenzy sequences update <id> --insert-steps-json '...'` with `type: "in_app"` | `insert_sequence_step` with `type: "in_app"` |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.