Context
I'm building a multi-instance WhatsApp client in Go using whatsmeow
(v0.0.0-20260410162419-b95d92207080). All standard messages work:
text, media, polls, replies, group operations, etc. Some
NativeFlowMessage button names also work — quick_reply and
cta_url render correctly on iOS and Android.
The problem is specifically with Name: "flow" (the native form button
typically associated with flow_id, flow_action,
flow_action_payload).
Symptoms
Recipient consistently sees:
"You received a message but it is not compatible with your version
of WhatsApp. Update WhatsApp" — on fully updated iOS and Android.
SendMessage() returns success (server ID assigned), delivery
receipts arrive. So the message reaches the recipient but the client
refuses to render it.
What I've tried (16+ variations, all failing for Name: "flow")
- Different
flow_message_version ("3", "4")
- Different
screen names ("WELCOME", "DETAILS", custom)
- With and without
flow_action_payload.data / params
- With
mode: "published" and a full flow_metadata block
(flow_json_version, data_api_protocol, data_api_version,
categories)
MessageVersion as protobuf int32 field on
NativeFlowMessage (vs embedding message_version inside the JSON)
- With and without
ViewOnceMessageV2Extension wrapper
- Injecting a
<biz> XMPP node via
SendRequestExtra.AdditionalNodes:
<biz>
<interactive type="native_flow" v="1">
<native_flow v="9" name="mixed"/>
</interactive>
</biz>
- Different
flow_action ("navigate", "data_exchange")
- Different
flow_token formats (plain UUID, N:flow_id:hex)
What works (same code path, only the button Name differs)
In the exact same InteractiveMessage / NativeFlowMessage
struct, changing only NativeFlowButton.Name:
| Button Name |
Renders on iOS/Android |
quick_reply |
✅ |
cta_url |
✅ |
flow |
❌ "message not compatible" |
single_select |
❌ "message not compatible" |
address_message |
❌ "message not compatible" |
The server seems to accept all of them (delivery receipts arrive); only
some render.
Minimal reproduction
flowParams := map[string]any{
"mode": "published",
"flow_message_version": "3",
"flow_token": "<flow_token>",
"flow_id": "<your_registered_flow_id>",
"flow_cta": "Open form",
"flow_action": "navigate",
"flow_action_payload": map[string]any{
"screen": "WELCOME",
"params": map[string]any{},
},
"flow_metadata": map[string]any{
"flow_json_version": "3.0",
"data_api_protocol": "v2",
"data_api_version": "v2",
},
}
bp, _ := json.Marshal(flowParams)
bpStr := string(bp)
msgVersion := int32(3)
msg := &waE2E.Message{
InteractiveMessage: &waE2E.InteractiveMessage{
Body: &waE2E.InteractiveMessage_Body{Text: proto.String("Test")},
InteractiveMessage: &waE2E.InteractiveMessage_NativeFlowMessage_{
NativeFlowMessage: &waE2E.InteractiveMessage_NativeFlowMessage{
Buttons: []*waE2E.InteractiveMessage_NativeFlowMessage_NativeFlowButton{{
Name: proto.String("flow"), // ← change to "quick_reply" and it works
ButtonParamsJSON: &bpStr,
}},
MessageVersion: &msgVersion,
},
},
},
}
bizNode := waBinary.Node{
Tag: "biz",
Content: []waBinary.Node{{
Tag: "interactive",
Attrs: waBinary.Attrs{"type": "native_flow", "v": "1"},
Content: []waBinary.Node{{
Tag: "native_flow",
Attrs: waBinary.Attrs{"v": "9", "name": "mixed"},
}},
}},
}
additional := []waBinary.Node{bizNode}
_, err := client.SendMessage(ctx, recipient, msg, whatsmeow.SendRequestExtra{
AdditionalNodes: &additional,
})
Hypothesis
Name: "flow" (and possibly address_message, single_select)
might require a server-side sender capability (e.g., WhatsApp Business
Account / Cloud API) that regular multi-device accounts don't have.
The server appears to accept and deliver the message, but the
receiving client validates sender capability before rendering and falls
back to the "incompatible" placeholder when that capability is absent.
I noticed getButtonTypeFromMessage in send.go adds an automatic
<biz> node for ButtonsMessage / ListMessage /
InteractiveResponseMessage, but not for InteractiveMessage with
NativeFlowMessage. I tried adding it manually via
AdditionalNodes (see code above) — it didn't change the outcome for
Name: "flow".
Questions for the community
- Is
Name: "flow" known to be gated by a server-side capability not
available to plain multi-device accounts?
- Is there any capability advertisement during connect/login that
could signal "I can send native flows"?
- Has anyone successfully sent a working Native Flow form
(Name: "flow" with a flow_id) from a regular whatsmeow-based
sender to a recipient outside any business platform?
- Is the
<biz> node structure documented anywhere, and is the
name="mixed" attribute the right one for native flow forms?
Any pointer or known-good wire capture would save the next person 16
experiments. Happy to share more data and contribute a documentation
patch once we have a confirmed answer.
Environment
- whatsmeow:
v0.0.0-20260410162419-b95d92207080
- Go: 1.26.3
- Recipient devices: iOS WhatsApp latest, Android WhatsApp latest
- Sender accounts: regular WhatsApp accounts (no WhatsApp Business
Account on either side)
Context
I'm building a multi-instance WhatsApp client in Go using whatsmeow
(
v0.0.0-20260410162419-b95d92207080). All standard messages work:text, media, polls, replies, group operations, etc. Some
NativeFlowMessagebutton names also work —quick_replyandcta_urlrender correctly on iOS and Android.The problem is specifically with
Name: "flow"(the native form buttontypically associated with
flow_id,flow_action,flow_action_payload).Symptoms
Recipient consistently sees:
"You received a message but it is not compatible with your version
of WhatsApp. Update WhatsApp" — on fully updated iOS and Android.
SendMessage()returns success (server ID assigned), deliveryreceipts arrive. So the message reaches the recipient but the client
refuses to render it.
What I've tried (16+ variations, all failing for
Name: "flow")flow_message_version("3","4")screennames ("WELCOME","DETAILS", custom)flow_action_payload.data/paramsmode: "published"and a fullflow_metadatablock(
flow_json_version,data_api_protocol,data_api_version,categories)MessageVersionas protobufint32field onNativeFlowMessage(vs embeddingmessage_versioninside the JSON)ViewOnceMessageV2Extensionwrapper<biz>XMPP node viaSendRequestExtra.AdditionalNodes:flow_action("navigate","data_exchange")flow_tokenformats (plain UUID,N:flow_id:hex)What works (same code path, only the button
Namediffers)In the exact same
InteractiveMessage/NativeFlowMessagestruct, changing only
NativeFlowButton.Name:quick_replycta_urlflowsingle_selectaddress_messageThe server seems to accept all of them (delivery receipts arrive); only
some render.
Minimal reproduction
Hypothesis
Name: "flow"(and possiblyaddress_message,single_select)might require a server-side sender capability (e.g., WhatsApp Business
Account / Cloud API) that regular multi-device accounts don't have.
The server appears to accept and deliver the message, but the
receiving client validates sender capability before rendering and falls
back to the "incompatible" placeholder when that capability is absent.
I noticed
getButtonTypeFromMessageinsend.goadds an automatic<biz>node forButtonsMessage/ListMessage/InteractiveResponseMessage, but not forInteractiveMessagewithNativeFlowMessage. I tried adding it manually viaAdditionalNodes(see code above) — it didn't change the outcome forName: "flow".Questions for the community
Name: "flow"known to be gated by a server-side capability notavailable to plain multi-device accounts?
could signal "I can send native flows"?
(
Name: "flow"with aflow_id) from a regular whatsmeow-basedsender to a recipient outside any business platform?
<biz>node structure documented anywhere, and is thename="mixed"attribute the right one for native flow forms?Any pointer or known-good wire capture would save the next person 16
experiments. Happy to share more data and contribute a documentation
patch once we have a confirmed answer.
Environment
v0.0.0-20260410162419-b95d92207080Account on either side)