feat: return WhatsApp Flow submissions unwrapped from template blocks - #340
Open
santiagocardo wants to merge 3 commits into
Open
feat: return WhatsApp Flow submissions unwrapped from template blocks#340santiagocardo wants to merge 3 commits into
santiagocardo wants to merge 3 commits into
Conversation
A message template can carry a FLOW button that launches a WhatsApp Flow.
When the contact submits it, the decoded nfm_reply JSON became the block's
user input and fell through to the catch-all clause, which wraps everything
as %{"__value__" => input, "index" => nil}.
That buried the submitted fields one level deeper than the equivalent
non-template flow: a journey had to reach for @ref_Template_1.__value__.email
where Io.Turn.WhatsAppSendFlow exposes @ref_Flow_1.email. Match the two so
journeys read flow results the same way regardless of how the flow was sent.
A submission is identified by the flow_token the contact's reply echoes back
from the outbound message. It cannot collide with the button clause above it,
which matches a binary rather than a map.
Also document why has_reply_button?/1 keeps a flow button in scope: it is what
suspends the journey so there is a result to route on at all.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A template's FLOW button is sent as a `sub_type: "flow"` button component whose single parameter has `type: "action"`, carrying `flow_action_data` — the data payload for the flow's first screen. parse_parameter/2 had no clause for it, so building a journey with such a template raised FunctionClauseError while loading the block config. Carry the payload through untouched: it may be a decoded JSON object, or a string holding an expression that is evaluated when the template is sent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Santiago <santiagocardo80@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
A WhatsApp message template can carry a FLOW button that launches a WhatsApp Flow (a form). When the contact submits it, the decoded reply JSON becomes the block's user input — but it fell through to
evaluate_outgoing/5's catch-all clause, which wraps everything as%{"__value__" => input, "index" => nil}.That buried the submitted fields one level deeper than the equivalent non-template flow:
Io.Turn.WhatsAppSendFlow@ref_Flow_1.emailIo.Turn.WhatsAppTemplateMessage@ref_Template_1.__value__.email❌This aligns the two, so journeys read flow results the same way regardless of how the flow was sent.
This is the first step of a larger piece of work in
engageto support routing and results for template flow buttons in journeys. The remaining work (aflow:optional parameter onsend_message_template()carrying the initial screen payload, which is what emits thesub_type: "flow"component and causes the journey to suspend) lands separately and depends on this release.Approach
One new clause in
WhatsAppTemplateMessage.evaluate_outgoing/5, returning the submission unwrapped:flow_token— the contact's reply always echoes back the token from the outbound message, so it's a reliable discriminator for a flow submission."template-btn-idx-" <> indexrather than a map. Existing button routing (both the prefixed and legacy-unprefixed forms) is untouched.has_reply_button?/1gains a comment noting it already admitssub_type: "flow"— a non-obvious dependency, since that predicate is what suspends the journey so there is a result to route on at all. No behaviour change.@block_doc'sreturns:updated to describe both result shapes.Version bumped 6.14.1 → 6.15.0 (backwards-compatible addition).
Verification
The file previously had coverage for
validate_config!/1only. Tests go 6 → 15, deliberately weighted toward the clauses that must not regress:evaluate_outgoing/5— flow submission returned unwrapped; plain-text reply still wrapped (fallback-exit path); prefixed button routing; legacy unprefixed button routing;nilpassthrough.evaluate_incoming/4— suspension for flow and quick-reply buttons, no suspension for url-only and button-less templates. This pins down the behaviour theengageside depends on.Confirmed end to end that the result actually lands where intended, by running
Block.evaluate_user_input/4against the new clause:Full suite: 201 tests, 0 failures.
mix format --check-formatted,mix credo --strict, andmix dialyzerall clean.🤖 Generated with Claude Code