The bot fires up to 5–6 messages for a single reel. This is the full catalogue of every message it
sends, what it looks like today, and the consolidated 2–3-DM ideal state.
Hard cap of 2–3 logical DMs per shared item, down from 6. Most saves are one.
Result DM — always exactly 1
Instant on the fast path, ~30s on the slow path. Clean header (flag · country · theme · count), spots list, one CTA. No country-number or milestone crammed in.
Moment DM — at most 1
Fires only when there's a real moment. A single priority pick: milestone > new-country > nudge. Insight folds into the milestone closer, never its own bubble. Survives the fast path.
Carousel keeps its ack
got it. pulling all the photos. + one result DM = 2 DMs. 3 only if a milestone also fires.
Voice — whole catalogue terse
Lowercase "Poke" voice across all ~60 messages: onboarding, conversational, quick-replies, save flow, failures, guests.
2How an Instagram DM actually renders
This matters for reading the mockups below, and it's a real platform constraint, not a style choice.
A plain text message
Carries the long content — the places list, the header, the closer. Up to ~1000 chars. It cannot have a tappable button.
A "special" message (button / generic template)
This is the only way to attach a tappable web_url button (e.g. 📂 open Bali). But its text is tiny (an 80-char title) — it cannot hold the long places list.
So the button is always its own bubble. A result DM with a places list is physically two bubbles: the plain-text list, then a separate special message carrying just the CTA. That's why the mockups draw the button as a detached, dashed "special message" bubble — and why even one logical Result DM shows as two bubbles. (We're cutting the count of logical DMs / moments; the per-message text+button split is a fixed IG behaviour we're leaving as-is.)
The fan: places → insight (+500ms) → milestone (+1s) → new-country (+1s) → nudge (+2s). Milestone / new-country / nudge are not gated by webhookDmSent, so they fire even after the webhook already replied.
Transport
instagram.service.ts → sendRichMessage
Sends the text bubble, waits 500ms, then the CTA as a separate special-message bubble (forced by the constraint above).
4Before / after (logical DMs)
Case
Today
Ideal
Plain reel, places found
1–2
1
Reel + new country
up to 4
2
Reel + milestone
up to 4
2
Reel + new country + milestone + nudge
up to 6
2
First save ever
2–4
2
Itinerary reel
2–5
1–2
Carousel
2–6
2
Carousel + milestone
up to 6
3 (ceiling)
Same-user re-share
1
1
Cross-user cache hit
1–4
1
Bookmark / failure / reject
1
1
Part A · Save-flow cases
The heart of the fix. Each case as the user actually sees it — a plain-text bubble for the list, and the
dashed bubble is the separate "special message" that carries the button. Bubble counts noted under each phone.
A1 · Plain reel, 1–5 spots (fast path)
was up to 4 DMsnow 1 DM
Caption names the places, nothing special about the save.
CompassToday
instantFound them 👇
🇮🇩 India
🏖️ Padang Padang Beach
🍜 Warung Bu Mil
+ 2 more
All saved to your collection.
special messageQuick actions 👇📂 My Collection
+ milestone / new country / nudge when they apply — each is its own text bubble plus its own button bubble.
≈ 3–8 bubbles · up to 4 logical DMs
CompassIdeal
instant🇮🇩 Bali · 4 spots
🏖️ Padang Padang Beach
🍜 Warung Bu Mil
🛕 Uluwatu Temple
🌅 Single Fin Bar
special message📂 open Bali
2 bubbles · 1 logical DM
A3 · Reel into a new country
was up to 4 DMsnow 2 DMs
The new-country moment becomes one consolidated follow-up, not three.
CompassToday
instantFound them 👇
🇵🇹 Portugal
🍽️ Time Out Market
🚋 Tram 28
+ 2 more
special messageQuick actions 👇📂 My Collection
+1s🎉 Double digits! Open the app to see these on a map 🗺️
special message📂 My Collection
+1s🇵🇹 Country #12!
+2s24 spots across 12 countries but no trips yet. Ready to turn these into an itinerary?
special message🗺️ Create a Trip
≈ 7 bubbles · 4 logical DMs
CompassIdeal
instant🇵🇹 Lisbon · 4 spots
🍽️ Time Out Market
🚋 Tram 28
🏰 São Jorge Castle
🥐 Manteigaria
special message📂 open Portugal
~30s · moment🇵🇹 that's your 12th country.
special message🗺️ plan a trip
4 bubbles · 2 logical DMs
A4 · Save milestone · A5 · First save ever
2 DMs each
Milestones at 1 / 5 / 10 / 25 / 50 / 100 saves. One result + one moment.
CompassMilestone (25)
instant🇮🇩 Bali · 5 spots
🏖️ Padang Padang Beach
🍜 Warung Bu Mil
🛕 Uluwatu Temple
🌅 Single Fin Bar
🏝️ Nusa Penida
special message📂 open Bali
~30s · moment25 spots across 12 countries.
the trip builder has serious material now.
special message🗺️ plan a trip
4 bubbles · 2 logical DMs
CompassFirst save ever
instant🇹🇭 Bangkok · 3 spots
🍜 Jay Fai
🛺 Wat Arun
🌃 Chinatown Yaowarat
special message✨ Open Compass
~30s · momentyour first spot. this is where it begins.
Both follow-ups are plain text (no button), so each is a single bubble.
CompassCaption gave ≥2 spots
instantgot it. pulling all the photos.
later🇮🇹 got 3 spots from the caption. instagram didn't share the full carousel so i couldn't grab the rest. paste the link if you want me to dig deeper.
2 bubbles · 2 logical DMs
CompassNothing usable
instantgot it. pulling all the photos.
laterinstagram didn't share the full carousel with me. paste the reel link here and i'll handle it.
2 bubbles · 2 logical DMs
A13 · Same-user re-share · A14 · Cross-user cache hit
1 DM each · no moment
CompassYou already saved it
instantalready saved. you really want Bali huh.
🇮🇩 Bali · 4 spots
🏖️ Padang Padang Beach
🍜 Warung Bu Mil
+ 2 more
special message📂 open Bali
2 bubbles · 1 logical DM
CompassSomeone else's reel, cached
~instant · no AI calls🇮🇩 Bali · 7 spots
🏖️ Padang Padang Beach
🍜 Warung Bu Mil
+ 5 more
you really want this huh.
Here's how to save them (takes 10 seconds):
1️⃣ Tap the button below
2️⃣ The app will open automatically
3️⃣ You'll be signed in - that's it!
here's how to save them (takes 10 seconds):
1. tap the button below
2. the app will open automatically
3. you'll be signed in. that's it.
Confirmed + scanning
Connected! 🎉
Found your earlier reels - scanning {n} now. I'll message you the spots as they're ready.
connected.
scanning {n} earlier reels now. i'll message you the spots as they finish. from here on, just send any travel reel.
B3 · Conversational (linked user)
Trigger
Now
Ideal
Greeting — exploring
Welcome back! {n} spots in your collection and growing. Send another reel?
welcome back. {n} spots in your collection and growing. send another reel?
Greeting — building
Hey! {n} spots across {c} countries - your collection is getting serious. What's next?
hey. {n} spots across {c} countries. your collection is getting serious. what's next?
Greeting — power
Welcome back. {n} spots, {c} countries. What are we adding today?
welcome back. {n} spots, {c} countries. what are we adding today?
Welcome back (14d+)
Hey, been a while! Your collection has {n} spots across {c} countries. Ready to add more?
hey. been a while. your collection has {n} spots across {c} countries. ready to add more?
Destination Q (has saves)
{flag} {country}! You've got {n} spots saved there already. Want to see them, or adding more?
{flag} {country}. you've got {n} spots saved there already.
Destination Q (no saves)
{flag} {country} - I don't have anything saved for you there yet. Share a {country} reel and I'll start your collection.
{flag} {country}. nothing saved for you there yet. send a {country} reel and i'll start your collection.
Travel emoji only
Planning something? Share a travel reel!
planning something? send a travel reel.
Random / non-travel text
I'm built for travel! Share a reel with places you want to visit, or ask me a travel question.
i only do travel. send me a reel with places, restaurants, or experiences.
Voice message
I can't listen to voice messages yet - but share a reel or type a travel question and I've got you.
i can't listen to voice messages. send a reel or type a travel question instead.
B4 · Quick-reply buttons
Payload
Now
Ideal
Send a Reel
Share any travel reel or paste a link - Instagram, YouTube, doesn't matter.
send any travel reel. instagram, youtube, doesn't matter.
How it works
Here's how it works:
🎬 Share any travel reel or YouTube video
🔍 I find every place, restaurant, and hotel mentioned
📂 Everything goes into your collection, sorted by country
🗺️ Then build real trip plans from your saves
how it works:
🎬 send any travel reel
🔍 i find every place, restaurant, hotel mentioned
📂 everything goes into your collection by country
🗺️ then build trips from your saves
Already connected
You're already connected! ✅
Share a reel anytime and I'll grab the spots.
you're already connected. send a reel anytime.
Ask a question
Ask me anything about travel - destinations, best times to visit, visa info, you name it.
ask me anything about travel. destinations, best times, visa info, you name it.
Default fallback
Share a travel reel and I'll grab all the spots for you!
send a travel reel and i'll grab the spots.
B5 · Save acknowledgements (instant)
Trigger
Now
Ideal
Single save
Scanning for spots...
on it. pulling the spots.
Batch (all new)
{n} reels! Love the energy. Scanning all of them - I'll message you as they're done.
{n} reels. on it. i'll message you as they finish.
Batch (mixed)
Saved {new} new! ({dup} already in your collection)
saved {new} new. {dup} you already had.
First save ack
{flag} {country} - great taste.
Scanning every frame for places, restaurants, and hidden gems...
{flag} {country}. nice. scanning every frame for places, restaurants, and hidden gems.
Power-user ack
On it.
on it.
B7 · Carousels & failures
Trigger
Now
Ideal
Carousel working ack
Got it - let me pull all the photos. One sec! 📸
got it. pulling all the photos.
Carousel resolver failed
Hmm, Instagram didn't share the full carousel with me. Paste the reel link here and I'll handle it 👇
instagram didn't share the full carousel with me. paste the reel link here and i'll handle it.
Processing failed
Hmm, couldn't find specific spots in that one. Try a reel where someone names the places they visit - those work best.
couldn't pin specific spots in that one. reels that name the places work best. try this one:
Non-travel
Processed it, but I couldn't find travel spots in this one. I work best with reels that name specific places, restaurants, or hotels.
i couldn't find travel spots in this one. i work best with reels that name specific places, restaurants, or hotels. try this one:
Non-travel fast reject
That looks like a {type} reel - cool content, but I'm built for travel. Share one with places, restaurants, or experiences and I'll pull out everything.
that's a {type} reel. great content, wrong agent.
i only do travel. try this one:
No resolvable URL
Hmm - Instagram didn't include the reel URL with that share. Could you paste the link directly here?
instagram didn't include the reel URL with that share. paste the link here? looks like https://instagram.com/reel/...
Rate limited
Whoa, you're on a roll! Give me a sec to catch up.
you're on a roll. give me a sec to catch up.
B8 · Guest caps (3-reel / 25-DM limits)
Trigger
Now
Ideal
3rd free save nudge
That's your 3rd free save 🎉
Create your account to keep saving, see every spot I found, and build real trips from them.
that's your 3rd free save. create your account to keep going and see every spot i found.
Reel cap (4th+)
You've hit your 3 free saves! 🎬
Create your account to unlock unlimited saves and see everything I've found for you.
you've hit your 3 free saves. create your account to unlock unlimited and see everything i've found.
Message cap (~25 DMs)
Let's move this to the app - you'll see all your saved spots in one place and can keep chatting there.
let's move this to the app. you'll see all your saved spots in one place and can keep chatting there.
Guests also get a reveal-token button (✨ Open Compass) spliced into every result DM plus a one-line footer. Unchanged behaviour, terse copy.
Part C · The Moment DM
Exactly one fires per save, by priority. Insight folds into the milestone closer, never its own DM.
5 spots. a few more and i can start drafting trips.
🗺️ plan a trip
1
Milestone = 10
double digits. open the app to see these on a map.
🗺️ plan a trip
1
Milestone = 25
25 spots across {c} countries. the trip builder has serious material now.
🗺️ plan a trip
1
Milestone = 50
50 spots. you're building a proper guide at this point.
🗺️ plan a trip
1
Milestone = 100
100 spots. you might know more hidden gems than most travel bloggers.
🗺️ plan a trip
2
New country (no milestone)
{flag} that's your {N}th country.
🗺️ plan a trip
3
15+ saves, 0 trips
15 spots and still no trip. want me to draft one?
🗺️ plan a trip
3
Inactive 7d+, 5+ saves
been a week. your collection's still here when you're ready.
📂 your collection
—
none of the above
no second DM
—
Nudges keep the existing 1-per-24h KV cooldown (bot:nudge_sent:{userId}), so the nudge rarely wins the slot.
Part D · The single CTA button
Context
Button
URL
Milestone moment
🗺️ plan a trip
/collection/{country}?action=plan
First save ever
✨ Open Compass
/collection
Has a country
📂 open {Country}
/collection/{country}
No country
📂 your collection
/collection
Guest (any)
reveal-token URL spliced into the above
/reveal?token=…
Each CTA ships as a separate "special message" (see section 2). That's the one structural bubble we can't merge away while the button opens a web_url.
Part E · What changes in code
#
Change
1
Consumer (content-consumer.ts → sendProcessingFeedback): replace the 4 trailing sends with one priority-picked Moment DM via a pickMoment(...) helper. Keep the existing generators. Moment DM not gated by webhookDmSent; skipped for re-shares.
2
Result header: drop country #N and (N total).
3
bot-messages.ts: whole-catalogue terse rewrite (~60 messages), singleCtaButton, no AI-tell punctuation.
4
Transport (instagram.service.ts): unchanged (user decision) — the CTA stays its own special-message bubble.
5
Webhook (webhooks.routes.ts): no logic change; inherits the new copy.
6
Tests: update DM-copy snapshots; add a ≤3 logical sends per reel assertion.