Portal Community

When to Use

Two interactive types in one operation: This node handles both button messages (up to 3 quick-reply buttons) and list messages (a menu opened by tapping a single button). The type is determined by which JSON schema you provide — buttonsJson for button type, sectionsJson + buttonText for list type.

Configuration

Connection

FieldRequiredDescription
accessTokenRequiredPermanent System User access token. Store in BizFirst Credentials Manager.
phoneNumberIdRequiredNumeric string ID of the sending WhatsApp Business phone number.
apiVersionOptionalMeta Graph API version. Defaults to v18.0.

Operation — Button Type

FieldRequiredDescription
toRequiredRecipient's phone number with country code, no + prefix.
bodyTextRequiredThe main message text displayed above the buttons. Maximum 1024 characters.
buttonsJsonRequiredJSON array string of button objects. Maximum 3 buttons. Each button: { "type": "reply", "reply": { "id": "btn_yes", "title": "Yes" } }. The id is returned in the webhook when the user taps; title is the display label (max 20 chars).
headerTextOptionalText displayed in bold above the body. Maximum 60 characters.
footerTextOptionalSmall grey text below the buttons. Maximum 60 characters. Use for disclaimers or context.

Operation — List Type

FieldRequiredDescription
toRequiredRecipient's phone number with country code, no + prefix.
bodyTextRequiredThe main message text displayed above the list button.
buttonTextRequiredLabel of the button that opens the list picker (e.g. "Choose a time slot"). Maximum 20 characters.
sectionsJsonRequiredJSON array string of section objects. Each section: { "title": "Morning", "rows": [{ "id": "slot_9am", "title": "9:00 AM", "description": "Available" }] }. Maximum 10 sections, 10 rows per section. Row id is returned in webhook on selection.
headerTextOptionalBold text above the body.
footerTextOptionalSmall grey footer text.

Sample Configuration — Button

{
  "resource": "message",
  "operation": "sendInteractiveButtons",
  "accessToken": "{{ $credentials.whatsAppToken }}",
  "phoneNumberId": "123456789012345",
  "to": "{{ $input.customerPhone }}",
  "headerText": "Order Confirmation",
  "bodyText": "Your order #{{ $input.orderId }} is ready to ship. Please confirm your delivery address:\n\n{{ $input.address }}",
  "buttonsJson": "[{\"type\":\"reply\",\"reply\":{\"id\":\"confirm_address\",\"title\":\"Address is correct\"}},{\"type\":\"reply\",\"reply\":{\"id\":\"update_address\",\"title\":\"Update address\"}},{\"type\":\"reply\",\"reply\":{\"id\":\"cancel_order\",\"title\":\"Cancel order\"}}]",
  "footerText": "Reply expires in 24 hours"
}

Sample Configuration — List

{
  "resource": "message",
  "operation": "sendInteractiveList",
  "accessToken": "{{ $credentials.whatsAppToken }}",
  "phoneNumberId": "123456789012345",
  "to": "{{ $input.patientPhone }}",
  "headerText": "Book Appointment",
  "bodyText": "Please select a time slot for your appointment with Dr. {{ $input.doctorName }}.",
  "buttonText": "View available slots",
  "sectionsJson": "[{\"title\":\"Tomorrow (Monday)\",\"rows\":[{\"id\":\"slot_mon_9\",\"title\":\"9:00 AM\",\"description\":\"45-minute consultation\"},{\"id\":\"slot_mon_11\",\"title\":\"11:00 AM\",\"description\":\"45-minute consultation\"},{\"id\":\"slot_mon_2\",\"title\":\"2:00 PM\",\"description\":\"45-minute consultation\"}]},{\"title\":\"Tuesday\",\"rows\":[{\"id\":\"slot_tue_10\",\"title\":\"10:00 AM\",\"description\":\"45-minute consultation\"},{\"id\":\"slot_tue_3\",\"title\":\"3:00 PM\",\"description\":\"45-minute consultation\"}]}]",
  "footerText": "We will send a confirmation once you select"
}

Validation Errors

ErrorCause
accessToken is requiredThe accessToken field is empty.
phoneNumberId is requiredThe phoneNumberId field is empty.
to is requiredThe to field is empty.
bodyText is requiredThe bodyText field is empty.
buttonsJson is requiredThe buttonsJson field is empty (button type).
Invalid JSON in buttons: ...The buttonsJson string is malformed JSON.
Invalid JSON in sections: ...The sectionsJson string is malformed JSON.
131009 (Meta)More than 3 buttons provided, or button title exceeds 20 characters.
131047 (Meta)24-hour customer service window expired.

Output

Success Port

FieldTypeDescription
messageIdstringUnique WhatsApp message ID (wamid). Store this to correlate with the webhook reply containing the user's button/list selection.
phoneNumberIdstringThe sending phone number ID.
tostringThe recipient phone number.
statusstring"sent" on success.
errorCodestringEmpty on success.
errorMessagestringEmpty on success.
rawResponsestringFull raw JSON from the Meta API.

Error Port

FieldTypeDescription
statusstring"failed" or "error".
errorCodestringMeta error code or BizFirst validation code.
errorMessagestringHuman-readable error description.
rawResponsestringRaw API error response.

Sample Output

{
  "messageId": "wamid.HBgLMTQxNTU1NTI2NzEVAgARGBIxRTI4QUY4NjhFMEI4NDgCCA",
  "phoneNumberId": "123456789012345",
  "to": "14155552671",
  "status": "sent",
  "errorCode": null,
  "errorMessage": null,
  "rawResponse": "{\"messaging_product\":\"whatsapp\",\"contacts\":[{\"input\":\"14155552671\",\"wa_id\":\"14155552671\"}],\"messages\":[{\"id\":\"wamid.HBgLMTQxNTU1NTI2NzEVAgARGBIxRTI4QUY4NjhFMEI4NDgCCA\"}]}"
}

Expression Reference

ValueExpressionNotes
Message ID{{ $output.sendInteractive.messageId }}Store this. When the user taps a button, the webhook delivers their selection with the context message ID — correlate to identify which interactive message they responded to.
Recipient{{ $output.sendInteractive.to }}For session tracking and audit logging.
Status{{ $output.sendInteractive.status }}Branch on "sent" vs "failed".
Error code{{ $output.sendInteractive.errorCode }}Log on the error port for diagnostics.

Node Policies & GuardRails

Policy AreaRecommendation
Credential storageStore the access token in BizFirst Credentials Manager. Never inline in configuration.
Button limitsMaximum 3 buttons per button-type message. Plan your UX carefully — if you need more than 3 options, use the list type instead. List supports up to 10 sections with 10 rows each (100 total options).
Button ID designUse descriptive, unique id values for buttons and list rows (e.g. confirm_address, not 1). The id is returned in the webhook payload and must be unambiguous for your workflow branching logic.
Title lengthButton titles are truncated to 20 characters in WhatsApp. Keep titles concise and test on a real device before deploying. List row titles support up to 24 characters; descriptions up to 72.
Webhook handlingUser responses arrive via a WhatsApp Webhook (inbound message event). Set up a WebhookTrigger node to receive responses and use the interactive.button_reply.id or interactive.list_reply.id to branch your workflow.
24-hour windowInteractive messages are free-form and subject to the 24-hour customer service window. They cannot be sent as standalone messages outside this window — re-engage with a text template first.
JSON validationValidate buttonsJson and sectionsJson in a Function node before passing to this operation. A single malformed character fails the entire message delivery.

Examples

NPS Rating — Button Type

{
  "resource": "message",
  "operation": "sendInteractiveButtons",
  "accessToken": "{{ $credentials.whatsAppToken }}",
  "phoneNumberId": "123456789012345",
  "to": "{{ $input.customerPhone }}",
  "headerText": "How did we do?",
  "bodyText": "We hope your experience with BizFirst was great! How would you rate your recent support interaction?",
  "buttonsJson": "[{\"type\":\"reply\",\"reply\":{\"id\":\"nps_great\",\"title\":\"Great\"}},{\"type\":\"reply\",\"reply\":{\"id\":\"nps_okay\",\"title\":\"Okay\"}},{\"type\":\"reply\",\"reply\":{\"id\":\"nps_poor\",\"title\":\"Poor\"}}]",
  "footerText": "Your feedback is anonymous"
}

A post-resolution workflow triggers this 10 minutes after a support case is closed. A WebhookTrigger listens for the reply; the selected button ID is matched in a Switch node that routes nps_poor to a human follow-up workflow.

Department Routing — List Type

{
  "resource": "message",
  "operation": "sendInteractiveList",
  "accessToken": "{{ $credentials.whatsAppToken }}",
  "phoneNumberId": "123456789012345",
  "to": "{{ $input.customerPhone }}",
  "bodyText": "Hello! How can we help you today? Please select the topic that best describes your enquiry.",
  "buttonText": "Choose a topic",
  "sectionsJson": "[{\"title\":\"Orders\",\"rows\":[{\"id\":\"topic_order_status\",\"title\":\"Track my order\",\"description\":\"Where is my order?\"},{\"id\":\"topic_order_cancel\",\"title\":\"Cancel an order\",\"description\":\"Request cancellation\"},{\"id\":\"topic_order_return\",\"title\":\"Return or refund\",\"description\":\"Initiate a return\"}]},{\"title\":\"Account\",\"rows\":[{\"id\":\"topic_account_login\",\"title\":\"Login issues\",\"description\":\"Password or access problems\"},{\"id\":\"topic_account_billing\",\"title\":\"Billing question\",\"description\":\"Invoices and payments\"}]}]",
  "footerText": "Available Mon-Fri 9AM-6PM"
}

An inbound WhatsApp message triggers this triage workflow. The customer's list selection is received via webhook and used in a Switch node to route the conversation to the appropriate team queue.

Appointment Confirmation — Button Type

{
  "resource": "message",
  "operation": "sendInteractiveButtons",
  "accessToken": "{{ $credentials.whatsAppToken }}",
  "phoneNumberId": "123456789012345",
  "to": "{{ $input.patientPhone }}",
  "headerText": "Appointment Reminder",
  "bodyText": "You have an appointment with {{ $input.doctorName }} tomorrow at {{ $input.time }}.\n\nWill you be attending?",
  "buttonsJson": "[{\"type\":\"reply\",\"reply\":{\"id\":\"appt_confirm\",\"title\":\"Yes, I will attend\"}},{\"type\":\"reply\",\"reply\":{\"id\":\"appt_cancel\",\"title\":\"Cancel appointment\"}}]",
  "footerText": "Cancel at least 2 hours before"
}

A ScheduledTrigger fires 24 hours before each appointment. Button reply appt_cancel triggers a cancellation sub-workflow that frees the slot and sends a rebooking link; appt_confirm sends a confirmation with directions.