WhatsApp Flows
A WhatsApp Flow that exchanges data with your own server (rather than being purely static) needs an Endpoint URI - a URL Meta calls directly whenever a user interacts with it. On Pywa Cloud, that’s just another path under your bot’s existing webhook URL - there’s nothing extra to configure on the platform side.
Setting it up
Section titled “Setting it up”Your bot’s webhook already lives at {PUBLIC_URL}/webhook/<your-bot-slug> (see
Connecting a WhatsApp Number). A Flow’s Endpoint URI is any path
under that same prefix - pick whatever suits your bot, e.g.:
https://<your control plane>/webhook/<your-bot-slug>/feedback_flowRegister exactly that as the Flow’s Endpoint URI in Meta’s Flow builder, and use the same path
(without the /webhook/<slug> prefix - Pywa Cloud strips it before forwarding) in your own code:
import osfrom pywa import WhatsApp, types
wa = WhatsApp( phone_id=os.environ["PYWA_PHONE_ID"], token=os.environ["PYWA_TOKEN"], verify_token=os.environ["PYWA_VERIFY_TOKEN"], business_private_key=os.environ["FLOW_PRIVATE_KEY"],)
@wa.on_flow_request("/feedback_flow")def feedback_handler(_: WhatsApp, req: types.FlowRequest): ...
@feedback_handler.on_initdef on_init(_: WhatsApp, req: types.FlowRequest): return req.respond(screen="SURVEY")
@feedback_handler.on_data_exchange(screen="SURVEY")def on_survey(_: WhatsApp, req: types.FlowRequest): return req.respond(screen="THANKS", data={"message": "Thanks for your feedback!"})That’s the whole platform-side story - Pywa Cloud’s ingress forwards POST /webhook/<slug>/feedback_flow to your bot as POST /feedback_flow, exactly matching what
on_flow_request("/feedback_flow") registered, whether your bot is deployed or you’re iterating
locally with pywa dev --tunnel - the routing is identical either
way.
The encryption key is yours to manage
Section titled “The encryption key is yours to manage”business_private_key above is not something Pywa Cloud generates, stores, or ever sees - you
generate an RSA key pair yourself, keep the private key as a normal (secret) environment variable
(pywa cloud env set FLOW_PRIVATE_KEY "$(cat private.pem)" --secret), and register the public half
with Meta yourself, using the token Pywa Cloud already injects:
wa.set_business_public_key(public_key=open("public.pem").read())Two independent layers of authentication
Section titled “Two independent layers of authentication”A Flow request that reaches your code has already passed two separate checks, and both have to be right:
- Signature validation, at Pywa Cloud’s ingress - Meta signs every Endpoint URI request with
X-Hub-Signature-256, exactly like your regular webhook traffic. This is verified before the request is ever forwarded to your bot, using the same WABA connection your bot is already assigned - nothing to configure here. - Payload decryption, in your own code - the request body itself is separately encrypted with
your
business_private_key;pywadecrypts it automatically once you provide the key. If decryption fails,pywa(not Pywa Cloud) returns the “signal that decryption failed” response Meta’s Flow client expects - check yourbusiness_private_keyfirst if you see this.