Six things nobody has planned yet, drawn so you can rule them
Everything the lane had a plan for is built and live. What is left is six items that were
never designed — named in the state file, boarded, and until now only prose. Each one below
is drawn, its states shown, and ends in a fork with a recommendation.
How to use this. Nothing here is built. Read an item, look at the drawing,
and answer its fork — a number is enough. Where a drawing shows the flow canvas it is in
that screen's own measured ManyChat palette, because that screen is the one ruled exemption
from the token sheet.
The six:
The AI node — the brief is given; this draws it, and asks the one thing the brief does not settle
The brief is captured — manychat-capability-catalog.md:443-455,
screenshot 104. Two textboxes and one button, deliberately minimal,
and explicitly not the Actions-node Claude form with its model picker and system
prompt. Drawn below exactly as captured. The brief settles the form and does not
settle what the button does — that is the fork, and it is the whole item.
The panel, as captured
AI stepClose panel
Tell AI what to do ?
Set a goal for the conversation
Give AI context ?
Share all the info
✦ Generate
Verbatim from the capture: the placeholders, the two tooltip discs, one blue button with a sparkle. No model picker, no temperature, no system prompt.
On the canvas
Whichever channel started it
AI step
Book a viewing for a 2-bed in Dubai Marina
Then
The node prints its GOAL, the way a message node prints its words — the card is the thing, not a label saying "AI step" over an empty box.
Every state
Empty
Set a goal for the conversation
✦ Generate
Generate is dead until there is a goal.
Ready
Book a viewing for a 2-bed in Dubai Marina
✦ Generate
A goal is enough; context is optional.
Working
Book a viewing for a 2-bed in Dubai Marina
Generating…
The one state that costs money. It must say so before it starts.
Refused
Book a viewing…
The model could not be reached. Nothing was changed.
✦ Try again
Errors are data: the draft is untouched.
What the brief does not say, and the app already has an answer for:
this repo already ships an ai_handover node kind, titled
"AI agent", whose executor RELEASES the thread to our own agent and ends the flow's
turn. ManyChat's AI Step, with a "Generate" button, reads like something else entirely:
a thing that AUTHORS steps onto the canvas. Those are not the same node and should not
wear the same name.
The fork — what does Generate do?
It hands the conversation to our agent, and the two boxes are its brief. "Generate" becomes "Hand over", the goal and context become the agent's mandate for this thread, and the node is the ai_handover we already execute. Nothing new in the engine; the node finally gets the panel it never had. Recommended — it is the only reading where the node works the day it ships.
It writes steps onto the canvas for you. A build-time author: press it and messages, questions and conditions appear, which you then edit. Much larger, spends money at authoring time, and needs an undo story for a button that rewrites your graph.
Both, as two different nodes. Honest to ManyChat, and doubles the surface for a node neither of us has watched anyone use.
B
Four match modes can be seen and removed, never created
half a feature
The engine matches six ways. The wizard makes two. The other four render as a dashed row
with a Remove, so a trigger that already has one can be read and unmade — but nothing in
the app can make one. Frames 32-34 offer no control for it.
And it has never met real data: the one prod flow carries no trigger at all.
Today — what a fifth mode looks like
When a comment…
contains · investcontains · buy
begins with bookRemove
Readable. Removable. Not creatable — there is no control that makes this row.
Proposed — the mode is a dropdown on the row
When a comment…
contains▾
is · contains · contains whole word · begins with · doesn't contain · is about
invest ×buy ×+ word
+ Add another condition
One dropdown, the engine's own six, in ManyChat's own words. The chips are already built; only the mode picker is new.
The one that is not like the others:is_about is
AI intent, not text matching — it costs a model call per inbound message. If it appears in
the same dropdown as "contains" it will be chosen as casually, on every trigger, by someone
who cannot see the bill.
The fork
All six in one dropdown, with is_about carrying a cost note. One control, the engine's full vocabulary, and the expensive one says it is expensive. Recommended.
Five in the dropdown; is_about is its own choice earlier in the wizard. ManyChat does this — "Detect words" and "Recognize intention" are two cards before the config. Truer to the walk, one more step for the common case.
Leave it. Two modes cover every live keyword we have. The dashed row stays as a reader for anything authored elsewhere.
C
Three wizard forks, built on the recommendation and live
flip or confirm
Each of these shipped on the recommendation because the lane could not stop for an answer.
They are live now. Each is a small edit to flip, and none is a rewrite.
1 — headings
Live: a question
Say what a comment must contain
contains · invest
Alternative: a statement
A comment must contain
contains · invest
Flipping is the q: strings in trigger-wizard.ts plus one test.
2 — include / exclude
Live: one list, a mode per row
contains · investdoesn't contain · job
Alternative: two boxes
Must containinvestMust not containjob
Two boxes read faster; one list is what the engine actually stores.
3 — the unwalked sub-forms
kind
ours
evidence
Live comments
keywords only
matches capture 139
Contact events
13 events + conditions
matches capture
Story replies
scope, filter, delay
3 of 4
The missing fourth is "React with ❤️ in response". It is in the schema, absent from the wizard, and unactionable — there is no reaction call on the Instagram client. Building the control without the capability recreates the trap we just removed elsewhere.
The fork
Confirm all three as they are. Nothing changes; the block leaves the state file. Recommended — every one shipped on the reading the walk supports.
Flip the headings to statements. One string change per step, plus a test.
Split include / exclude into two boxes. A panel change only; the stored shape is untouched.
Build the ❤️ reaction anyway, which means first finding out whether Meta exposes a reaction on a story reply at all. Unknown today.
D
Ref URL, QR code and Ads triggers
greyed with a reason
Deferred 2026-07-24 with "don't abandon it". They sit in the trigger picker today,
greyed, with the honest reason on them. What has never been drawn is what they look like
when they are not greyed.
Today — in the picker, greyed
Ref URL, QR code or Ad Needs link tracking and Meta Ads Manager wiring. Boarded, not built.
Proposed — the Ref URL config
This flow starts when someone opens
ig.me/m/silveroakglobal?ref=marina-2bed
The ref is yours to name. It comes back on the first message and is what tells the flows apart.
Copy linkDownload QR
The QR is the same link as an image — one trigger, two ways to hand it out, which is why they are one picker row and not three.
What each actually needs. Ref URL: nothing new from Meta — the ref rides
the first inbound message, so this is a trigger kind plus a link builder. QR: the same,
plus an image. Ads is the odd one — it needs Ads Manager wiring and one
Ads trigger per automation is a Meta cap we hit during the walk.
The fork
Build Ref URL + QR now, leave Ads greyed. They share one implementation and need nothing from Meta we do not have. Ads keeps its honest reason. Recommended.
All three together, and accept that Ads brings Ads Manager with it.
Keep all three greyed until a campaign actually needs a trackable link.
E
One human is two contacts, and the flow cannot tell
data, not paint
Ruled 2026-08-03: a flow adapts per run and can branch on channel. The branch shipped this
week. The consequence did not.Contact is keyed
(channel, channelUserId), so the same person reaching us on
Instagram and on WhatsApp is two rows — and a tag, a field or a CRM write on one is
invisible to the other.
What happens today
step
Instagram run
WhatsApp run
Asks the budget, saves it
budget = 2M
budget unknown
Condition: budget over 1M?
Yes branch
No branch — asks again
Tags hot-lead
tagged
not tagged
Writes the CRM lead
one lead
a second lead
The customer answers the same question twice and appears in the CRM twice. Nothing is broken in code — the key simply says these are two people.
Fields and tags hang off the PERSON. Each channel keeps its own thread, window and consent, because Meta's rules are per channel.
What joins them
signal
confidence
Same phone number captured
certain
Same email captured
certain
Operator joins them by hand
certain
Same name + same day
a guess — never
Only a captured identifier or a human joins two rows. Nothing is merged on a resemblance.
The fork
A Person row above Contact, joined only on a captured phone or email, or by hand. Fields, tags and CRM identity move up; threads, windows and consent stay per channel. Additive, and the only option that stops the double CRM lead. Recommended.
Copy fields across on a match, leave the rows separate. Cheaper, and drifts the moment one side changes.
Leave it and say so on screen. The canvas warns when a flow has triggers on two channels. Honest, costs nothing, fixes nothing.
F
Flow delivery analytics — the strip with no source
promised, unrecorded
Sent, Delivered, Opened, Clicked was promised for the flow screen. Nothing records it:
FlowRunEvent stores no wamid, so a flow
send cannot be joined to the delivery webhook that already arrives and is already stored.
The published view today shows Runs, Going on now and a per-step table — real numbers, and
not these.
The honest limit today
step
reached
errors
Message · "Thanks for…"
22
1
Question · budget
15
0
Reached counts the people who got to a step and errors counts the times it failed. Nothing records deliveries, opens or clicks inside a flow, so those are not shown.
That sentence is on the screen now. It is true, and it is the reason this item exists.
Proposed — what one column costs
step
reached
sent
delivered
Message · "Thanks for…"
22
22
21
Question · budget
15
15
15
Sent and Delivered are one field away. The status webhook already
arrives, is already keyed by wamid, and is already stored.
Add wamid to the run event when a send succeeds and the
join exists. Opened and Clicked are not: Meta gives no open signal
on these surfaces, and a click needs the campaign link-wrapper a flow does not use.
The fork
Sent + Delivered only, and drop Opened/Clicked from the promise. One nullable column, one join, two honest numbers — and the screen stops implying two it can never fill. Recommended.
Add Clicked too, which means routing flow links through the campaign wrapper and changing what the customer sees in the URL.
Leave the strip unbuilt and keep the sentence that says why.