Notifications that are worth opening
We use AI to help write and time our push notifications, inside strict limits. Here are the rules that keep them useful: real dishes from real menus, no invented discounts, no shawarma at breakfast, and never more than one a day.
Push notifications are the easiest way to annoy people. Every app has sent you the vague “We miss you!” or the discount that turns out not to exist. Do that a few times and the customer switches notifications off, and then even the useful ones, like “your rider has arrived”, stop getting through.
We wanted notifications from Laayo to feel like a friend who knows what you like and what is open right now. That is a high bar, so we built the system around limits first and cleverness second.
Two kinds of message#
Laayo sends two kinds of promotional notification.
Campaigns are planned by our team. In the admin panel, someone picks an audience (for example, people who ordered once and never came back), writes a message or asks for a suggested one, chooses the channels (push, in-app inbox, SMS or email) and schedules it. Nothing goes out on its own. Before launch the panel shows how many people will receive it and what any SMS will cost, and a test send goes to our own phones first.
AI notifications run on their own through the day. A small service looks at what is happening on Laayo right now (which restaurants are open, what is selling, what time of day it is) and drafts messages for groups of customers. A second lane writes personal messages for individual customers based on their own taste. Both are written by a language model, but the model is boxed in by rules that live in our code, not in its instructions.
Rule one: the dish has to be real#
Our first personal messages had an embarrassing flaw. The model was given two lists: what was selling well across town, and each customer’s favourite restaurant. Sometimes it combined them, recommending a dish from one restaurant “at” another restaurant that did not make it at all. We caught ourselves promoting shawarma at a place with no shawarma on the menu.
So we took that decision away from the model. Our code now picks the dish first, using plain rules:
- The dish is on the restaurant’s own menu, approved and available.
- The restaurant is active, not temporarily closed, and serves that dish at this time of day.
- If you mostly order vegetarian food, the pick is vegetarian.
- We do not suggest a dish we already suggested to you recently.
Only after that does the model see anything, and its only job is to write the words. Then we check its sentence: if it does not name the chosen dish, or if it names any other restaurant, the message is thrown away. It is better to send nothing than to send something untrue.
Rule two: no invented discounts#
Language models love to sound generous. Left alone, they add a percentage off or “free delivery today” to everything. So by default our AI notifications are not allowed to mention any discount at all, and a filter catches and discards drafts that slip one in anyway.
When we do want a message to mention an offer, it can only quote a real, active coupon, word for word, with its real conditions. No made-up minimum orders, no imaginary codes.
Rule three: the right food at the right time#
An early message wished someone “good morning” and suggested a heavy dinner dish. Technically correct, practically ridiculous. Each part of the day now comes with guidance on what fits, from tea and breakfast in the morning to proper meals at lunch and dinner. The model must pick from what suits the time, the greeting has to match the clock, and in the morning a favourite heavy dish can only be mentioned as an idea “for lunch later”.
Rule four: fewer, not more#
This is the rule that matters most. Our limits, set in code:
- At most one AI notification per person per day, across both lanes. If you received a group message, you will not also get a personal one.
- A weekly cap shared with campaigns, so a busy campaign week means fewer AI messages, not more.
- A minimum gap between sends and a rest period for each audience group, so nobody gets a burst.
- SMS has quiet hours at night, and a spend cap.
The service paces itself through the day. If it is behind its plan, it is allowed to send when something genuinely fits. It is never allowed to send just to hit a number.
Every message opens a specific, checked place in the app: the restaurant it mentions, search, your favourites, your wallet. A link to something that does not exist is rejected before sending.
How we know it is working#
Counting everyone who ordered after receiving a message flatters every campaign, because many of those people would have ordered anyway. So we are adding holdout groups: a small slice of each audience that matches but does not get the message. Comparing the two tells us whether a campaign actually brought in orders. The same approach is coming to always-on messages such as welcome and win-back flows.
What’s next#
We want customers to have more control: a simple setting for “fewer suggestions” or “only offers”, and the ability to mute suggestions from a specific restaurant. We also want restaurants to see when a notification featured their dish, so they can make sure it is ready when the orders arrive.