# Create an in-app message

> Write the message, choose when it shows and who sees it, set how often, send a test, and launch.

Source: https://docs.grovs.io/docs/engagement/in-app-messages/create-a-message

Build an in-app message from blank to launch. The example is Medly, a health app, showing a card to people who open their plan screen for the third time without subscribing.

> **Note:** Your app or site needs [iOS SDK 3.1.0](https://docs.grovs.io/docs/sdk/ios/in-app-messages), [Android SDK 3.1.0](https://docs.grovs.io/docs/sdk/android/in-app-messages) or [Web SDK 2.1.0](https://docs.grovs.io/docs/sdk/web/in-app-messages) or later. See [SDK requirements](https://docs.grovs.io/docs/engagement/in-app-messages#sdk-requirements) for Flutter, React Native and Unity. Until a platform is connected, **In-App** shows **Connect a platform to show in-app messages** instead of the list.

### 1. Start a message

Go to **Engage** › **In-App** and click **New message**. Choose **Blank message**, or **Start from a template** for a ready-made **Ask people to update**, **Rate the app** or **Quick question** message.

The composer has six steps: **Message**, **Trigger**, **Audience**, **Experiment**, **Limits and schedule** and **Check and launch**. Drafts can be incomplete; **Save draft** works at any time.

### 2. Write the message

In **Message**, pick a **Template** (see [Formats](https://docs.grovs.io/docs/engagement/in-app-messages#formats)) and fill in **Title**, **Message** and **Image**.

Under **What people can do**, choose:

- **Tap a button**: up to two buttons, plus an optional action for a tap on the message itself.
- **Answer a question**: stars or a choice. See [Surveys and ratings](https://docs.grovs.io/docs/engagement/in-app-messages/surveys-and-ratings).

Each button has a label and an action:

| Action                        | Does                                                                                                                                                                                 |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Close the message**         | Closes it                                                                                                                                                                            |
| **Open a link**               | Opens a web or app link, such as `https://medly.app/plans` or `medly://plans`. See [Links and in-app browsers](https://docs.grovs.io/docs/engagement/in-app-messages/in-app-browser) |
| **Send data to the app**      | Passes up to 20 key-value pairs to your app's click callback                                                                                                                         |
| **Ask for push permission**   | Shows the system permission prompt. Apps only                                                                                                                                        |
| **Ask for an app rating**     | Shows the system review prompt. Apps only                                                                                                                                            |
| **Open the app's store page** | Opens your app in the App Store or Google Play. Apps only                                                                                                                            |

Messages with an app-only action never show on the web. A push permission message is skipped for people who already granted it.

Set how the message closes:

- **People can close it without answering**: close button, tap outside or swipe. Turn it off for something that must be acted on, such as a required update. The message then stays until a button or the message is tapped.
- **Show a close button**
- **Dim the app behind the message**

Pick **Colours** from a theme or set your own, and an **Orientation** if the message only works one way.

### 3. Personalize the text

**Insert a value** adds a person's attribute or a property of the triggering event, with a fallback:

```text
Hi {{ user.first_name | default: "there" }}
{{ event.plan_name | default: "Your plan" }} is one tap away
```

Fields work in the title, message, button labels and links. In a link, the value is URL-encoded and can't supply the scheme or host. If a field has no value and no fallback, the message isn't shown for that trigger.

### 4. Choose the trigger

In **Trigger**, choose what shows the message:

| Trigger                 | Fires                                                                                                                     |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **The app is opened**   | Once per visit (SDK session)                                                                                              |
| **A screen is viewed**  | When a screen or page is viewed: one by name, or any. Case-insensitive                                                    |
| **An event is tracked** | When a custom event is tracked. Exact name match. Add up to five filters under **Only when the event has**; all must pass |

**Add another trigger** allows up to five; any one fires the message.

For Medly: **A screen is viewed**, screen `Plans`.

Under **When to show it**, a **Delay** of a few seconds lets the screen settle. After the delay the message is re-checked and shown only if it still applies. A waiting message is dropped if the app goes to the background.

### 5. Choose the audience

In **Audience**, choose:

- **Everyone**: every visitor of the project.
- **People in a segment**: refreshed hourly, so people become eligible within about an hour of joining. See [Segments](https://docs.grovs.io/docs/analytics/segments).

Under **Conditions on the device (optional)**, add up to ten conditions. All must hold at trigger time. The device checks them itself, with no delay.

| Condition                       | Checks                                                                                                                                                                         |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Platform**                    | iOS, Android or web                                                                                                                                                            |
| **App version**, **OS version** | Compared as versions, so `2.10` is above `2.9`. On the web, OS version is empty and app version is what your site passes to the SDK                                            |
| **Language**                    | The device's main language, such as `en`                                                                                                                                       |
| **User attribute**              | An attribute your app has set                                                                                                                                                  |
| **Times an event happened**     | How often this device tracked an event. Counting starts when a message naming the event first reaches the device; earlier history isn't seen. Use a segment for past behaviour |
| **First seen**                  | Before or after a date, or in the last so many days                                                                                                                            |

For Medly: **Times an event happened** `plans_viewed` is above 2, and **User attribute** `subscribed` is not `true`.

### 6. Measure it (optional)

In **Experiment**, add a **Goal** (built-in event, custom event, screen or purchase, within 1, 3 or 7 days, or 30 days for purchases), a **Control group**, and up to two extra content variants. See [Experiments](https://docs.grovs.io/docs/engagement/experiments).

The control group and number of variants are fixed at launch.

### 7. Set limits, priority and schedule

In **Limits and schedule**, set **How often one person sees it**: at most so many times per visit, day, week or ever. Day and week allow up to 20. Leave a box empty for no limit.

Visit, day and week limits are per device. The "ever" limit also holds across a person's devices and reinstalls, once they're identified.

The project caps messages per device per visit and per day (10 each by default, in **Engagement Settings**). **Not held back by the project limit** exempts a message that must always show; it then doesn't count toward that limit either.

**Priority** runs from **1, lowest** to **10, highest**. When several messages match, only the highest priority shows; on a tie, the older message wins. One message is on screen at a time, with at most one waiting.

Under **When it runs**, set a start and an end, or **Until I end it**. These are one moment for everyone, entered in your time zone. **Only on certain days or hours** follows each device's clock, so 09:00 is local. A window such as 22:00 to 06:00 crosses midnight.

### 8. Send a test message

Under **See it on a device**, enter a **User identifier** and click **Send test**. Save first: the test uses the last saved version.

Use the identifier your app gives the SDK (`Grovs.userIdentifier` on iOS, `Grovs.setUserIdentifier()` on the web). The test goes to all of that person's devices, up to 50, and shows at each device's next sync within 30 minutes. It ignores triggers, conditions, limits and schedule, shows once, and isn't counted in the report.

| Device   | Shows the test                                                                                                  |
| -------- | --------------------------------------------------------------------------------------------------------------- |
| iOS app  | When the app returns to the foreground, or at its next sync                                                     |
| Web page | On the next page load, when the tab comes back into view, or at the next 15-minute sync of an open, visible tab |

The 30-minute window outlasts the web SDK's 15-minute sync, so an open tab still gets the test. After 30 minutes it expires; send it again.

A test is refused if nobody in the project has that identifier, or the message isn't complete enough to launch. Browsers skip tests that use an app-only action.

### 9. Launch

**Check and launch** summarizes the message in one sentence and lists anything to fix. Click **Launch**.

- An **Everyone** message goes **Live**, or **Scheduled** if its start is in the future.
- A segment message shows **Preparing audience** first. If the audience can't be resolved, it returns to **Draft** with the reason.

## After launch

You can edit a live message's content, triggers, conditions, limits and schedule; devices pick up changes at their next sync. You can't change its template, audience, control group or number of variants.

| Action                 | Does                                                                                                                     |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **Pause** / **Resume** | Stops and restarts delivery. Devices in use stop usually within a minute, and at most about 15 minutes on an idle device |
| **End**                | Stops it for good. The report stays                                                                                      |
| **Update audience**    | Moves a segment message to the segment as it is now. The old audience is served until the new one is ready               |
| **Duplicate**          | Copies it into a new draft                                                                                               |
| **Delete**             | Drafts only                                                                                                              |

People who answered a question or hit the "ever" limit won't see the message again. To ask again, duplicate it.

## Related

- [In-app messages overview](https://docs.grovs.io/docs/engagement/in-app-messages)
- [Reporting](https://docs.grovs.io/docs/engagement/in-app-messages/reporting)
