Examples
Five WhatsApp node examples: interactive product catalog, sendAndWait order confirmation, media upload and share, group announcement with buttons, and template appointment reminder.
Example 1: Rich Product Catalog with Interactive List
{
"AccessToken": "{{ credentials.whatsapp_access_token }}",
"PhoneNumberId": "{{ credentials.whatsapp_phone_number_id }}",
"operation": "message/sendInteractiveList",
"To": "{{ vars.customer_phone }}",
"BodyText": "Welcome to FreshMart! Browse our menu and tap an item to order.",
"ButtonTitle": "View Menu",
"Sections": [
{
"title": "Fresh Produce",
"rows": [
{ "id": "prod_apples", "title": "Apples (1kg)", "description": "$3.50 — Crisp red apples, locally grown" },
{ "id": "prod_bananas", "title": "Bananas (bunch)", "description": "$2.20 — Ripe yellow bananas" },
{ "id": "prod_tomatoes", "title": "Tomatoes (500g)", "description": "$2.80 — Vine-ripened tomatoes" }
]
},
{
"title": "Dairy",
"rows": [
{ "id": "prod_milk", "title": "Full Cream Milk (2L)", "description": "$4.10 — Fresh from local farms" },
{ "id": "prod_cheese", "title": "Cheddar Cheese (200g)", "description": "$5.50 — Mature cheddar" }
]
}
]
}
Expected outcome: Customer taps "View Menu" and sees the scrollable list. When they select an item, a reply webhook arrives with
reply_list_row_id set to the selected row's ID (e.g. prod_milk). Use a sendAndWait or webhook trigger to capture the selection and build the order.Example 2: sendAndWait Order Confirmation Flow
{
"AccessToken": "{{ credentials.whatsapp_access_token }}",
"PhoneNumberId": "{{ credentials.whatsapp_phone_number_id }}",
"operation": "message/sendAndWait",
"To": "{{ vars.customer_phone }}",
"Text": "Hi {{ vars.customer_name }}, your order summary:\n\n{{ vars.order_items_formatted }}\n\n*Total: {{ vars.order_total }}*\n\nReply *YES* to confirm and proceed to payment, or *NO* to cancel.",
"TimeoutSeconds": 300
}
Behaviour: The workflow suspends after sending. On the success port,
reply_text contains the customer's reply. Use a Switch node to branch on the reply — route "YES" to the payment processing branch and "NO" to the cancellation branch. On the timeout port (5 minutes with no reply), route to an order expiry notification.Example 3: Media Upload and Share Document
Step 1 — Upload the file to WhatsApp:
{
"AccessToken": "{{ credentials.whatsapp_access_token }}",
"PhoneNumberId": "{{ credentials.whatsapp_phone_number_id }}",
"operation": "media/upload",
"FileData": "{{ vars.invoice_pdf_base64 }}",
"MimeType": "application/pdf",
"Filename": "Invoice_{{ vars.invoice_number }}.pdf"
}
Step 2 — Send the document using the returned media_id:
{
"AccessToken": "{{ credentials.whatsapp_access_token }}",
"PhoneNumberId": "{{ credentials.whatsapp_phone_number_id }}",
"operation": "message/sendDocument",
"To": "{{ vars.customer_phone }}",
"DocumentUrl": null,
"DocumentBase64": "{{ nodes.uploadInvoice.output.media_id }}",
"Filename": "Invoice_{{ vars.invoice_number }}.pdf",
"Caption": "Invoice #{{ vars.invoice_number }} — {{ vars.invoice_total_formatted }}. Due: {{ vars.invoice_due_date }}. Questions? Reply here."
}
Expected outcome: The PDF is uploaded once and sent to the customer with a readable filename and caption. Using media/upload before sending is preferred over direct URL delivery when the source file is dynamically generated rather than hosted on a public URL.
Example 4: Group Announcement with Interactive Buttons
Step 1 — Send buttons message to group:
{
"AccessToken": "{{ credentials.whatsapp_access_token }}",
"PhoneNumberId": "{{ credentials.whatsapp_phone_number_id }}",
"operation": "message/sendInteractiveButtons",
"To": "{{ vars.group_id }}",
"Header": "Policy Update — Action Required",
"BodyText": "A new data security policy has been issued. All staff must acknowledge receipt by {{ vars.deadline_date }}.\n\nDocument: {{ vars.policy_doc_url }}",
"Buttons": [
{ "id": "ack_read", "title": "I have read it" },
{ "id": "ack_questions", "title": "I have questions" },
{ "id": "ack_later", "title": "Remind me later" }
],
"Footer": "HR Department — {{ vars.policy_version }}"
}
Expected outcome: Group members see the announcement with three tappable buttons. Button tap replies arrive as webhook events with
reply_button_id set to the tapped button's ID. A parallel webhook trigger workflow captures these responses and records acknowledgements in your HR system.Example 5: Template Message for Appointment Reminder
{
"AccessToken": "{{ credentials.whatsapp_access_token }}",
"PhoneNumberId": "{{ credentials.whatsapp_phone_number_id }}",
"operation": "message/sendTemplate",
"To": "{{ vars.patient_phone }}",
"TemplateName": "appointment_reminder_v2",
"LanguageCode": "en_US",
"Components": [
{
"type": "header",
"parameters": [
{ "type": "text", "text": "{{ vars.clinic_name }}" }
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "{{ vars.patient_first_name }}" },
{ "type": "text", "text": "{{ vars.appointment_date }}" },
{ "type": "text", "text": "{{ vars.appointment_time }}" },
{ "type": "text", "text": "{{ vars.doctor_name }}" },
{ "type": "text", "text": "{{ vars.clinic_address }}" }
]
},
{
"type": "button",
"sub_type": "url",
"index": 0,
"parameters": [
{ "type": "text", "text": "{{ vars.reschedule_token }}" }
]
}
]
}
Expected outcome: Patient receives a structured appointment reminder with clinic name in the header, their name, date, time, doctor, and clinic address in the body, and a personalised "Reschedule" button linking to
https://clinic.example.com/reschedule/{{ vars.reschedule_token }}. This is sent outside the 24-hour window and requires an approved template — no free-form messaging policy applies.