Skip to main content
Messaging & Automation

Building a Native WhatsApp Forms Engine on PrismWA

By Sameer Joshi

A visual builder and runtime for Meta's WhatsApp Flows - letting teams design multi-screen forms, power them with live data, and collect structured responses without a single Meta dashboard visit.

Platform

Web & WhatsApp

Duration

6 weeks

14

Drag-and-drop field types

3

Ways to power a screen with live data

0

Duplicate submissions, by design

Project overview

PrismWA now ships a complete native WhatsApp Forms feature: a visual builder, Meta's encrypted Data Exchange handshake, three ways to power live screens, and automatic, de-duplicated submission capture - with no hand-written Flow JSON and no manual data wiring.

Platform

Web & WhatsApp

Duration

6 weeks

Type

Messaging & Automation

Stack

10 technologies

The challenge

Teams running WhatsApp-based surveys, lead capture, or bookings had two bad options: build a web form and paste a link into the chat (which breaks the point of being "in WhatsApp" and loses a chunk of respondents at the tap-out), or hand-author raw WhatsApp Flow JSON and manage it through Meta's Flow Builder and Graph API directly - a process with no visual preview, a strict component schema, and a public-key encryption protocol that has to be implemented correctly before Meta will even let you publish.

Sending a plain link instead of a native form loses respondents at the app-switch

Meta's raw Flow JSON format has no visual editor and fails validation on small mistakes

Every dynamic screen (a dropdown fed by an API, a personalized message) requires implementing Meta's Data Exchange encryption protocol from scratch

Republishing a flow for a trivial config change re-triggers Meta's full validation and review pipeline unless you specifically avoid it

Submitted responses arrive back as a WhatsApp webhook payload, not structured data - someone still has to wire that into a CRM or spreadsheet

What we set out to do

  • 01

    Give workspace admins a drag-and-drop builder for native WhatsApp forms - no JSON required

  • 02

    Implement Meta's Data Exchange protocol (RSA + AES-GCM handshake) so screens can carry live data

  • 03

    Support three ways to power a dynamic screen: static options, an external API, or a custom function

  • 04

    Publish flows to Meta with a smart diff, so unchanged flows don't get needlessly resent for review

  • 05

    Capture every submission automatically and forward it to an external system with field mapping

How we solved it

01

Visual Flow Builder with Live Phone Preview

Built a screen-by-screen editor with a drag-and-drop component palette covering 14 field types - headings, text inputs, dropdowns, radio and checkbox groups, date pickers, opt-ins, photo/document pickers, plus If/Switch branching and a footer action - alongside a phone-frame preview that renders the screen exactly as it will appear in WhatsApp as it's being built.

Key decision

A real component palette mapped 1:1 to Meta's Flow JSON schema, instead of a generic form builder translated after the fact

Result

Admins design and preview multi-screen forms entirely inside PrismWA, with no hand-written JSON

02

Meta's Data Exchange Encryption Handshake

Every WhatsApp Flow with dynamic screens needs a public/private RSA key pair: PrismWA generates a 2048-bit RSA key pair per flow, uploads the public key to Meta, and keeps the private key server-side. When a user's phone requests a screen, Meta encrypts the request with a fresh AES-128-GCM key wrapped in our RSA public key; PrismWA unwraps it, decrypts the payload, computes the response, and re-encrypts it with the same AES key under a flipped initialization vector, exactly as Meta's protocol requires.

Key decision

Implement the full crypto handshake (RSA-OAEP unwrap → AES-128-GCM decrypt → IV-flip re-encrypt) rather than relying on an unmanaged third-party library

Result

Every dynamic screen - dropdowns, conditional text, personalized fields - round-trips safely through Meta's encrypted channel

03

Three Ways to Power a Dynamic Screen

A screen's live data can come from three sources, configured per-screen with no code: a static list, an external API call (with input field mapping and a JMESPath expression to reshape the response), or a sandboxed custom JavaScript function with access to the user's phone number, form data, and encrypted secrets. Functions run through a safe fetch with SSRF protection (blocking private IPs and cloud metadata endpoints) and a hard timeout.

Key decision

JMESPath for declarative response reshaping, with an escape hatch to custom functions for anything JMESPath can't express

Result

A dropdown can pull live inventory from an ERP, or a screen can greet a user by name, without a new flow version

04

Smart Publish, Submission Capture, and Forwarding

Publishing hashes the transformed flow JSON and skips the Meta round-trip entirely if nothing actually changed (e.g., only a data-source URL was edited) - so config tweaks don't re-trigger Meta's validation and asset upload pipeline unless the form itself changed. Completed forms arrive as a WhatsApp webhook (nfm_reply), get de-duplicated by flow token, stored as structured submissions (with any uploaded photos/documents downloaded automatically), and optionally forwarded to an external API with configurable field-name mapping and per-submission resend.

Key decision

Hash-based change detection before touching Meta's API, plus flow-token dedup on the inbound webhook

Result

Zero duplicate submissions, fewer unnecessary Meta republishes, and structured data ready for a CRM without custom glue code

05

Challenge: Getting the Encryption Handshake Byte-Exact

Problem: Meta's Data Exchange protocol fails silently on the device ("Something went wrong") if the AES-GCM auth tag handling or IV-flip is even slightly off, with almost no error detail returned.

Result

Solution: Isolated the crypto steps (RSA-OAEP unwrap, GCM decrypt with the auth tag, GCM re-encrypt with a bitwise-flipped IV) into a single well-tested service, validated against Meta's health-check ping action before shipping real screens.

06

Challenge: Routing Between Screens When routing_model Goes Missing

Problem: Meta's publish step transforms our stored flow JSON for its own copy without writing the transformed routing_model back to our database, so the stored flow often has an empty routing model - which broke "next screen" navigation during data exchange.

Result

Solution: Added a fallback that walks the screens array in order when the routing model has no entry for the current screen, so navigation keeps working regardless of what Meta did or didn't persist.

07

Challenge: Running User-Authored Functions Safely

Problem: Letting admins write custom JavaScript to power a screen means running arbitrary code inside the same process as the API - a real sandbox escape risk if function authorship were ever opened beyond trusted roles.

Result

Solution: Scoped functions to an AsyncFunction with a controlled ctx (secrets, form data, a safe fetch with SSRF blocking and a timeout), restricted authorship to OWNER/ADMIN roles, documented the risk until this needs a real VM sandbox.

Measurable impact

14

Drag-and-drop field types available in the builder (text, choice, date, media, and branching components)

3

Independent ways to power a dynamic screen (static, external API + JMESPath, custom function)

2048-bit

RSA key size generated per flow for Meta's Data Exchange encryption

0

Duplicate submissions, enforced via flow-token dedup on the inbound webhook

Tech stack

NNestJSNNext.js (App Router)MMeta WhatsApp Flows / Graph APIPPostgreSQLPPrismaRRSA-OAEP + AES-128-GCM (Node crypto)JJMESPathBBullMQRRedisTTypeScript

What we learned

This project showed that Meta's WhatsApp Flows platform is genuinely capable - multi-screen native forms with conditional logic, live data, and media capture - but the protocol underneath (encrypted data exchange, strict JSON schema, opaque validation errors) is unforgiving to build against directly. Wrapping it in a visual builder and a runtime that owns the crypto handshake turned a protocol only a backend engineer could touch into something any workspace admin can design.

  • 01

    Meta's Data Exchange protocol is well-documented but unforgiving - get the encryption handshake exactly right or debug blind

  • 02

    Hashing the transformed output before republishing avoids needless Meta API calls and keeps config tweaks fast

  • 03

    Giving admins a custom-function escape hatch (not just static config) covers the long tail of "one more API" requests without new engineering work each time

  • 04

    Server-authored code execution is only safe with an explicit trust boundary - restricting it to trusted roles is a stopgap, not a permanent answer

Ready to build something that matters?

We solve problems that don't have Stack Overflow answers. Let's talk.

Book a Discovery Call