Feature docs

WhatsApp system on Vareons: connection, buttons, flows, templates, and order confirmation

The WhatsApp system on Vareons is the Meta WhatsApp Business integration inside the dashboard: connect from /dashboard/whatsapp via Embedded Signup, then automatic order-confirmation templates with confirm/cancel/edit quick replies, manual sends from CRM and the orders page, with delivery and replies tracked on the order itself.

Last verified: 2026-09-19

The WhatsApp system is managed from /dashboard/whatsapp. The page is WhatsAppDashboardPage from features/dashboard-whatsapp with the Zustand useWhatsAppStore and the shared useWhatsAppConnected hook that gates the WhatsApp column on /dashboard/orders and send actions in CRM.

The buttons that actually exist on the page: a terms + privacy checkbox (gate before connect), a Connect WhatsApp button, a Disconnect button with a browser confirm scoped to one phoneNumberId, a Resync webhooks button, a Details button opening the phone/WABA/template diagnostics dialog, and a Settings button opening OrderConfirmationSettingsDialog (automatic enable toggle + ar/en language). On /dashboard/orders the WhatsApp confirmation column, the WhatsAppConfirmationBadge, and the send-message action appear only when at least one account has isConnected true.

Connect flow: load accounts via GET /api/whatsapp/accounts, then gates (terms + NEXT_PUBLIC_FACEBOOK_APP_ID and NEXT_PUBLIC_WHATSAPP_CONFIG_ID + HTTPS), then a manual redirect to dialog/oauth with redirect_uri equal to origin+pathname and state in sessionStorage, then Facebook returns to the same page with ?code=&state=, then POST /api/whatsapp/callback exchanges the code via GET /oauth/access_token before dbConnect, discovers the WABA via debug_token and granular_scopes, loads phone_numbers, upserts WhatsAppAccount keyed by merchantId+phoneNumberId, calls POST /{waba-id}/subscribed_apps with an optional override_callback_uri, then attempts both order_confirmation_ar and order_confirmation_en templates without failing Connect.

Order-confirmation flow: on public-checkout or manual dashboard order creation the selected template is sent fire-and-forget after persistence. Text is built by the single source lib/whatsapp/order-confirmation-body.js with five numbered parameters and sanitized spacing. Every successful send creates one WhatsAppOrderConfirmation row keyed by the unique outboundMessageId (automatic or manual source). A customer quick reply matches via message.context.id onto that exact row: confirm stores confirmed and protects active stock-reservation leases without changing data.status, edit stores edit_requested with no stock transition, cancel releases stock first via cancelOrderAndReleaseStock and only then moves a non-terminal data.status to cancelled on success. After each successful claim a deduped free-text acknowledgement is sent keyed by acknowledgementMessageId. Delivery states sent/delivered/read/failed apply monotonically with failed terminal.

Manual send POST /api/whatsapp/send accepts to, type text/template, and customerId with template.name and language, plus exactly one parameter source: full Meta components, the bodyParameters shorthand, or orderId for order-confirmation templates only where the server builds all five values from the tenant-scoped order. The CRM dialog (WhatsAppSenderDialog) counts {{n}} placeholders and renders one input per variable, and for order-confirmation templates the orders-list action passes the row order number automatically. Numbers are normalized to E.164 digits via toWhatsAppSendNumber (local 0 becomes default country code 20) before Graph, while matching uses whatsAppPhoneMatchFilter for format variants.

Hard prerequisites: Facebook JS SDK from connect.facebook.net, mandatory HTTPS (Facebook Login rejects http), a valid payment method on the WABA in Meta Business Manager or Graph returns 200 accepted with no real delivery, plus Currency, Time Zone, Address and Business verification, a Live/Published Meta app for real inbound webhooks, and a 24h free-text window that requires a recent webhook-created lastInboundAt (merchant outbound messages do not open it).

Services and files: lib/whatsapp/service.js and order-confirmation.js plus template-parameters.js, phone.js, send-debug.js, diagnostic-log.js, access-token.js (AES encryption), account-details.js, button-actions.js, delivery-status.js, inbound-message.js and webhook-*.js, plus pages/api/whatsapp endpoints (accounts, account-details, callback, disconnect, send, legacy connect, templates, order-confirmation-settings, subscribe-webhooks, webhook), and the admin POST /api/admin/whatsapp/deploy-order-confirmation-template from /admin/whatsapp-templates. The ActivityEvent log records connect, disconnect, webhook_resync, message_sent and confirmation settings without storing tokens, full phones, or message bodies. Unified [whatsapp/flow] diagnostics with a traceId follow every send via docker compose logs web.

Setup steps in Vareons

  1. Open the WhatsApp page and accept terms

    In the dashboard open /dashboard/whatsapp. Tick the terms and privacy checkbox (/dashboard/terms and /dashboard/privacy-policy). Without acceptance the connect button stays disabled, alongside the client-env and HTTPS gates.

  2. Connect through Facebook

    Click Connect WhatsApp to redirect to dialog/oauth with redirect_uri equal to the same page. Finish Meta login until you return with ?code=&state=, then the browser posts {code, redirect_uri} to POST /api/whatsapp/callback and cleans the URL. Confirm HTTPS plus NEXT_PUBLIC_FACEBOOK_APP_ID and NEXT_PUBLIC_WHATSAPP_CONFIG_ID.

  3. Complete Meta billing and verification

    In Meta Business Manager add a valid payment method on the WABA, and complete Currency, Time Zone, business address and Business verification. Without billing, Graph accepts messages as accepted with no delivery and shows Missing valid payment method alerts.

  4. Enable automatic order confirmation and language

    When an account is connected the settings button appears. Open it via useWhatsAppFetchSettings, then enable orderConfirmationEnabled and pick ar or en. Saving goes through GET/POST /api/whatsapp/order-confirmation-settings with the whatsapp:edit permission.

  5. Resync webhooks and inspect details

    Use Resync webhooks to run POST /{waba-id}/subscribed_apps with override_callback_uri from WHATSAPP_WEBHOOK_CALLBACK_URL or the tunnel origin. Open Details on each account card to check phone, WABA and template status via GET /api/whatsapp/account-details with phoneNumberId.

  6. Send from CRM and orders, then follow replies

    From the customer row in CRM open the WhatsApp dialog: pick a template, fill {{n}} variables, or send free text inside the 24h window. From /dashboard/orders use the WhatsApp column, the confirmation badge, and the send-message action. Confirm/cancel/edit replies land on the same order with stock protection or release.

Frequently asked questions

Where is the WhatsApp connect button and what enables it?

On /dashboard/whatsapp inside WhatsAppDashboardHeader or the empty state. It requires accepting terms, having NEXT_PUBLIC_FACEBOOK_APP_ID and NEXT_PUBLIC_WHATSAPP_CONFIG_ID, and opening the page over HTTPS.

What do disconnect, resync, details, and settings do?

Disconnect sets isConnected false for one number only via phoneNumberId without remote Meta revocation. Resync re-runs subscribed_apps with override_callback_uri. Details inspects phone, WABA and templates. Settings saves the automatic-confirmation toggle and language.

Why does Meta return accepted but the customer gets nothing?

The common cause is a missing valid payment method on the WABA. Graph accepts then blocks delivery. Fix it in Meta: payment method, currency, time zone, address and verification — not in Vareons code.

When does free text work and when is a template required?

Free text needs a WhatsAppChat with a recent webhook-created lastInboundAt inside 24 hours. Merchant messages do not open the window. Outside the window or to start a conversation, use an approved template.

Where is WhatsApp data stored and is it isolated per store?

In WhatsAppAccount, WhatsAppChat, WhatsAppMessage, WhatsAppTemplate and WhatsAppOrderConfirmation with merchantId/tenantId, upserted by merchantId+phoneNumberId. Every dashboard endpoint passes protectDashboardEndpoint with tenant isolation and whatsapp permissions.

What happens when the customer taps confirm, cancel, or edit?

The context.id matches that exact outboundMessageId row. Confirm stores confirmed and protects stock leases without changing order status. Edit stores edit_requested only. Cancel releases stock first, then cancels only a non-terminal order, then sends a text acknowledgement.

Troubleshooting

1) Connect disabled: accept terms, confirm HTTPS plus NEXT_PUBLIC_FACEBOOK_APP_ID and NEXT_PUBLIC_WHATSAPP_CONFIG_ID. 2) OAuth 36008: usually from FB.login; Vareons uses the manual dialog/oauth with a byte-identical redirect_uri registered in Valid OAuth Redirect URIs. 3) Accepted without delivery: add a WABA payment method and complete currency, time zone, address and verification in Meta. 4) No inbound messages: make sure the Meta app is Live, the Callback URL plus messages field are set, then use Resync webhooks with override_callback_uri. 5) Free-text failure: it needs a webhook-created lastInboundAt inside 24h; merchant messages do not open the window. 6) Recipient rejected 131030: numbers auto-normalize to E.164 digits (local 0 becomes 20); send a supported format and check debug.toSendNumber. 7) Template rejected 132000 or 132018: check the {{n}} count (five for order-confirmation templates) and stripped newlines/repeated spaces. 8) Customer button does not update the order: check NO_CONTEXT, UNKNOWN_ACTION, NO_MAPPING and DUPLICATE reasons in [whatsapp/flow] logs with the traceId.

Important limitations

• Dashboard disconnect is soft-only (isConnected=false) and does not revoke the Meta token remotely. • Unpublished apps usually receive only dashboard test webhooks. • Business-initiated delivery depends on Meta billing and template policies. • Free text is limited to a 24h window from a real webhook inbound. • [whatsapp/flow] diagnostics go to stdout/stderr via Docker json-file, not an app-written file.

Related links

تواصل معنا على واتساب