River
Y CombinatorBacked by Y Combinator
FREE TEMPLATE

In-App Messaging Guidelines Template

Everyone says to cap message frequency across teams. Nobody measures the sum first, so the cap gets set without knowing what it changes.

Free download  ·  No account needed

Every guide on in-app messaging arrives at the same conclusion: cap frequency centrally rather than per campaign, hold a priority order, and use one or two messages per session as a ceiling. That is correct and it is the second half of the job. The first half, which none of them mentions, is measuring what a user currently receives. Adopt a cap without that and nobody in the room knows whether it removes five per cent of your messages or sixty, which one loses the slot, or who is affected by it.

So this pack starts with a replay. Take every live message's trigger conditions and run them against ninety days of real event data, which produces messages per user per week. Read it at the percentiles and never at the mean. At the fictional Halberton the mean was 1.7 a week and the worst individual week was 17. One operations lead got eleven messages from six teams over three days, because she invited four teammates, connected an integration and crossed her plan limit. Three pairs fired off one event, seconds apart.

The inventory is a finding on its own: six teams believed 31 messages were live and the replay found 47. Then the cap comes out of your own engagement-by-position curve and arrives priced, which is what survives a room with six teams in it. Send the list of systems that can interrupt a user and your event data. What comes back is the distribution, then a standard the launch, release note and sunset messages all have to work inside.

47 live messages against a mean of 1.7 and a worst week of 17

The inventory rebuilt by replay rather than by asking, one real user's worst week written out, and engagement measured by where a message lands rather than by which message it is.

Message Register

Thirty-one is what the teams believed. Forty-seven is what fires

Illustrative, for a fictional B2B resource-planning product called Halberton. Six teams, three delivery systems: 22 messages in the messaging tool, 16 hardcoded in the product, 9 in a legacy lifecycle platform nobody had opened in fourteen months.

MessageTeamSurfaceTriggerIts own ruleKnows about othersBand
M-09 Review data sharingSecurityBannerIntegration scope grantedUntil dismissedNo1 Action required
M-10 Approaching your project limitBillingBanner80% of plan limitUntil resolvedNo1 Action required
M-11 Upgrade nowGrowthModal80% of plan limitOnce per 7 daysNo5 Growth
M-07 Integration connectedGrowthToastIntegration authorisedNoneNo4 Education
M-08 Connect two more sourcesLifecycleChecklistExactly one integrationOnce per 3 daysNo6 Lifecycle
M-03 Invite sentGrowthToastInvite submittedNoneNo4 Education
M-04 Move to the team planLifecycleBannerAny invite in 24 hoursOnce per 7 daysNo6 Lifecycle
M-13 Rate this featureProductSlide-upAnother message dismissedOnce per 30 daysYes, backwards6 Lifecycle

The column that is the whole problem

Thirty-one of the 47 carry a frequency rule of their own. Sixteen carry none. Not one of the 47 references any other message, so every team had capped itself and nobody had capped the sum. M-10 and M-11 share a trigger exactly, so a user crossing the plan limit gets a banner and a modal about one fact in the same second. M-13 is the only message aware of another and it is the wrong way round: it fires when something else is dismissed, which guarantees it arrives when the user is already dismissing.

Frequency Cap by User

The mean is 1.7. This is one real week

Ines Braga, operations lead at a mid-market account. Over three days she invited four teammates, connected the calendar integration and crossed her plan limit, which are the three most valuable things a customer did that week.

WhenWhat she didWhat firedTeam
Mon 09:14Invited four teammatesToast, invite sentGrowth
Mon 09:15Same invite eventBanner, move to the team planLifecycle
Mon 09:22Opened permissionsCoachmark, set up rolesProduct
Tue 11:02Connected the calendarToast, integration liveGrowth
Tue 11:03Same integration eventChecklist, connect two moreLifecycle
Tue 11:40Scope grantedBanner, review data sharingSecurity
Tue 15:31Crossed 80% of her limitBanner, approaching your limitBilling
Tue 15:31Same limit eventModal, upgrade nowGrowth
Wed 08:57Opened a new viewModal, what is newProduct
Wed 09:04Closed that modalSlide-up, rate this featureProduct
Wed 16:12Day 30 of the accountModal, how likely to recommendCustomer success

She is not the exception, she is the pattern

Across 12,610 weekly active users the median is 1 a week, the ninetieth percentile is 4, the ninety-ninth is 8 and the worst week is 17. Of the 590 users receiving more than five, 78.4% had upgraded, invited somebody, connected something or hit a limit that week, against 12.1% across the whole base. High intent is what fires the triggers, so the messaging system is structurally loudest at exactly the customers you least want to irritate, and better targeting cannot fix it because the targeting is working.

Engagement by Message

Most of what reads as a copy problem is a queue problem

Engagement with the primary action, split by where the message landed in that user's rolling week.

MessageTeamPer weekMedian positionOverallIn the first threeFourth and laterDismissed under 2s
M-10 Approaching your limitBilling410244.6%47.1%21.0%12.3%
M-01 Welcome tourProduct1,180138.1%38.4%9.2%9.4%
M-16 Weekly capacity reportProduct4,900inbox27.4%n/an/an/a
M-12 What is newProduct3,150211.2%15.9%3.0%38.2%
M-09 Review data sharingSecurity34058.9%29.6%4.1%51.4%
M-11 Upgrade nowGrowth2,24045.2%14.8%2.6%55.1%
M-04 Move to the team planLifecycle2,88034.1%6.8%1.3%62.3%
M-13 Rate this featureProduct1,61061.9%7.4%1.1%71.2%

The row that changes the meeting

M-09 is a security notice. It asks a user who has just granted an integration scope to check what they shared, and its median position in the user's week is fifth, where it engages at 8.9%. On the rare occasions it lands in the first three it engages at 29.6%. The most consequential message in the register is queuing behind an upsell, and no rewrite fixes that.

Where the cap number comes from, and what it costs

Across the base, engagement by position runs 34.2%, 21.8%, 12.4%, 6.1%, 3.0% and 1.4% from the sixth onward, while dismissal inside two seconds climbs from 11.3% to 68.1%. Cap at three and you suppress 4,004 of 21,384 messages a week, which is 18.7% of volume affecting 13.6% of users, and it costs 153 of 4,800 engaged actions, which is 3.2%. Lifecycle and Growth absorb 79% of the cut between them.

What is in the pack

01

Message Register

The inventory rebuilt by replay rather than by asking teams what they ship. Owning team, delivery system, surface, trigger condition, audience, its own frequency rule, and whether it references any other message. That last column is almost always no on every row, which is the problem in one field.

02

Frequency Cap by User

Messages per user per rolling week at the percentiles, never the mean, with the worst individual week written out timestamp by timestamp. Plus what those users were doing, because high intent is what fires the triggers.

03

Engagement by Message

Engagement and two-second dismissal by where a message lands in the user's week, not by which message it is. This is what separates a badly written message from a well-written one in a bad slot, and it usually rescues several.

04

Frequency Policy

The cap, the curve the number came from, what it costs in suppressed volume against lost engaged actions, and which team absorbs the suppression. Also the arithmetic on why a per-session cap alone is not a cap, and the standing exceptions, since a notice about a defect somebody is already living with jumps the queue rather than waiting for a slot.

05

Messaging Standard

Surfaces ranked by what they cost the user, six priority bands, and what every message has to carry before it ships. The GOV.UK Design System reaches the same place from the other direction: show only the highest priority one when messages cannot be combined.

06

Copy Guidance, plus a weekly sweep

How the words work once a message has won its slot. Plus an automation that re-derives which messages are actually live, groups cap breaches by the system that bypassed the check, and flags any action-required message whose median position has slipped past third.

How it works

  1. 1

    Send the systems, not the list

    Every path that can put something in front of a user: the messaging tool, anything hardcoded in the product, marketing automation, surveys, billing, security. Plus ninety days of event data for the active base.

  2. 2

    Triggers get replayed

    River evaluates every message's trigger conditions against the real events, respecting each message's own frequency rule, and produces messages per user per week alongside an inventory that is usually larger than the one anybody had.

  3. 3

    Percentiles and position

    The distribution is read at the ninetieth and ninety-ninth rather than the mean, and engagement is split by where each message landed, which is how a queue problem stops being mistaken for a copy problem.

  4. 4

    The cap arrives priced

    Two or three candidate caps, each with suppressed volume, users affected, engaged actions lost, and the team that absorbs it. Then it runs weekly, because both the inventory and the distribution drift upward.

Frequently asked questions

Every guide already says to cap frequency across campaigns. What is different?

They all stop at recommending a number. None of them measures what a user currently receives, so nobody knows what the cap changes. That measurement is one replay of every trigger against real events. At Halberton it found a mean of 1.7 messages a week and a worst individual week of 17.

Why not just use one or two messages per session?

Multiply it out first. A ninetieth-percentile Halberton user had 3.1 sessions per working day, so 15.5 sessions a week, which makes a two-per-session cap a permission to send 31 a week. That is nearly double the worst case that already existed. The rolling window is the real cap.

How do you find messages nobody remembers shipping?

By replay rather than by asking. A request for everyone's campaigns returns the list people remember, which is short by exactly the messages causing trouble. Halberton's teams believed 31 were live and 47 were: sixteen hardcoded in the app or sitting in a lifecycle tool nobody had opened in fourteen months.

How do you convince six teams to give up their slot?

By pricing the cap rather than asserting it. Three a week at Halberton suppressed 18.7% of all volume and cost 3.2% of engaged actions, because the volume being cut sits where the fourth message engages at 6.1% and the sixth at 1.4%. Lifecycle and Growth absorbed 79% of it, stated up front.

Is a low-engagement message just badly written?

Usually not. Split its engagement by where it landed. Every message at Halberton performed three to seven times better in the first three positions, and the security notice engaged at 8.9% overall against 29.6% when it landed early. Rewriting that one would have achieved nothing at all.

What happens to a message that loses the slot?

It moves to an ambient surface with its original copy rather than being discarded. Halberton's inbox capacity report was the highest-engaging item in the entire register at 27.4%, and it interrupted nobody. Discarding is a legitimate choice for some messages, but it should be a stated one.

Are there rules we have to meet regardless of the cap?

Yes. WCAG success criterion 2.2.4 requires that interruptions can be postponed or suppressed by the user except in an emergency, so a modal with no clear way out fails an accessibility standard as well as annoying somebody. A dismissal that is not remembered is the same failure.

Find out what one user actually receives

Send every system that can interrupt a user and ninety days of event data. The first thing back is the distribution, and the worst week in it written out in full.

Count what one user gets