Skip to content

[Question] InteractiveMessage with NativeFlowMessage Name="flow" → receiver shows "message not compatible" #1144

@neoand

Description

@neoand

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")

  1. Different flow_message_version ("3", "4")
  2. Different screen names ("WELCOME", "DETAILS", custom)
  3. With and without flow_action_payload.data / params
  4. With mode: "published" and a full flow_metadata block
    (flow_json_version, data_api_protocol, data_api_version,
    categories)
  5. MessageVersion as protobuf int32 field on
    NativeFlowMessage (vs embedding message_version inside the JSON)
  6. With and without ViewOnceMessageV2Extension wrapper
  7. Injecting a <biz> XMPP node via
    SendRequestExtra.AdditionalNodes:
    <biz>
      <interactive type="native_flow" v="1">
        <native_flow v="9" name="mixed"/>
      </interactive>
    </biz>
    
  8. Different flow_action ("navigate", "data_exchange")
  9. 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

  1. Is Name: "flow" known to be gated by a server-side capability not
    available to plain multi-device accounts?
  2. Is there any capability advertisement during connect/login that
    could signal "I can send native flows"?
  3. 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?
  4. 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions