WWaSphere Docs
Features

Meta Cloud API

Run a session on the official Meta WhatsApp Cloud API alongside Baileys — enabling the provider, per-session credentials, the inbound webhook, capability differences, and opt-in send failover.

Meta Cloud API

WaSphere can run a session on the official Meta WhatsApp Cloud API instead of the Baileys web protocol — behind the same API surface, the same webhooks, and the same Inbox. Choose the engine per session: a support number on Baileys and a notifications number on Meta can run side by side in one workspace.

New in v1.2. The Meta provider ships dormantMETA_PROVIDER_ENABLED defaults to false. Existing Baileys deployments are completely unaffected until you opt in.

Which provider should you use?

Neither engine is strictly better — they have different trade-offs.

Baileys (default)Meta Cloud API
ConnectionQR scan from a normal WhatsApp accountMeta Business account + verified number
CostFreeMeta's per-conversation pricing
Ban riskReal — this is the unofficial protocolNone (it is the official API)
Free-form messagesAlwaysOnly inside the 24-hour customer-service window
TemplatesNot supportedSupported
PollsSupportedNot supported
GroupsSupportedNot supported
Presence / profile editsSupportedNot supported
View-onceSupportedNot supported
Interactive buttons & listsSupportedSupported
ReactionsSupportedSupported
Media uploadSupportedSupported

Choose Meta when you are sending transactional notifications to opted-in customers at scale and cannot tolerate the risk of a number being banned — and when you are willing to pay Meta and work inside the 24-hour window plus approved templates.

Stay on Baileys when you need groups, polls, presence, or view-once, when you want free-form conversation at any time, or when you simply do not want a Meta Business relationship. Polls in particular are Baileys-only, so order-confirmation flows built on poll votes must stay on Baileys.

Enabling the provider

The provider is gated by a single environment variable on the WA Server. Until it is true, every session resolves to Baileys and attempting to create a Meta session is rejected.

# WA Server environment
META_PROVIDER_ENABLED=true

# Meta Graph API version used for Cloud API calls.
# Bump this on Meta's ~6-month deprecation cadence.
GRAPH_VERSION=v22.0

# Public URL of the WA Server, e.g. https://wa.your-domain.com
# Only used to auto-fill the webhook callback URL in the New Session dialog.
WA_SERVER_PUBLIC_URL=https://wa.your-domain.com

If you create a session with provider: "meta" while the flag is off, the API returns:

400 Bad Request
Meta provider is disabled — set META_PROVIDER_ENABLED=true to create Meta sessions.

GRAPH_VERSION defaults to v22.0. Meta deprecates Graph API versions roughly every six months — pin it explicitly and review it when you upgrade WaSphere.

Creating a Meta session

A Meta session is created through the same endpoint as a Baileys one — there is no QR code, just credentials from your Meta app. In the dashboard, go to Sessions → New Session and pick Meta on the provider radio; the dialog includes a Test connection button and a copy-paste callback URL.

curl -X POST https://api.yourdomain.com/workspaces/{workspaceId}/proxy/api/sessions \
  -H "Authorization: Bearer wsk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "notifications",
    "provider": "meta",
    "metaPhoneNumberId": "1234567890123456",
    "metaWabaId": "9876543210987654",
    "metaAccessToken": "EAA...your-permanent-token",
    "metaVerifyToken": "a-string-you-choose",
    "metaAppSecret": "your-meta-app-secret"
  }'
FieldWhere to find it
metaPhoneNumberIdMeta → WhatsApp → API Setup → Phone number ID
metaWabaIdMeta → WhatsApp → API Setup → WhatsApp Business Account ID
metaAccessTokenA permanent system-user access token (not the temporary 24h one)
metaVerifyTokenAny string you choose — you paste the same value into Meta's webhook config
metaAppSecretMeta → App Settings → Basic → App Secret. Required in production.

Inbound webhook

Meta delivers inbound messages and status updates to a public endpoint on your WA Server:

GET  /api/meta/webhook/:sessionId    # verify-token handshake
POST /api/meta/webhook/:sessionId    # message + status delivery

Paste that URL (with your sessionId) and your chosen metaVerifyToken into Meta → WhatsApp → Configuration → Webhook. The GET handshake echoes Meta's hub.challenge back once the verify token matches.

This route is deliberately excluded from the normal X-Api-Token auth — Meta sends no token. It is secured instead by the verify-token handshake and by an X-Hub-Signature-256 HMAC computed over the raw request body with your app secret.

When NODE_ENV=production, a session with no stored app secret has its webhooks rejected outright with 403 Forbidden. Outside production an unsigned webhook is accepted with a loud warning, so local development works without an app secret — never run that way in production.

Verified events are translated into the same internal events the rest of WaSphere already consumes, so your existing webhook subscribers and the Inbox need no changes. Inbound media is downloaded by the WA Server just as it is for Baileys.

Checking what a session can do

Because the two engines differ, ask the session what it supports rather than hard-coding assumptions:

curl https://api.yourdomain.com/workspaces/{workspaceId}/proxy/api/sessions/notifications/capabilities \
  -H "Authorization: Bearer wsk_your_key"
{
  "provider": "meta",
  "capabilities": {
    "groups": false,
    "presence": false,
    "profileEdit": false,
    "polls": false,
    "templates": true,
    "flows": true,
    "interactiveButtons": true,
    "reactions": true,
    "viewOnce": false,
    "mediaUpload": true,
    "freeformAlways": false
  }
}

The dashboard uses this to hide unsupported actions. freeformAlways: false is the 24-hour-window rule: outside the window you must send an approved template.

Calling an unsupported operation returns a capability error rather than failing obscurely — sending a poll on a Meta session, for example, is rejected because Meta has no poll primitive.

Opt-in send failover

A session may nominate a backup provider. When a send fails on the primary engine for a reason that is plausibly transient, WaSphere retries the send once on the backup.

curl -X POST https://api.yourdomain.com/workspaces/{workspaceId}/proxy/api/sessions \
  -H "Authorization: Bearer wsk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "notifications",
    "provider": "meta",
    "fallbackProvider": "baileys"
  }'

Failover is off unless fallbackProvider is set, and it is only active when META_PROVIDER_ENABLED=true.

What retries. Timeouts, connection errors (ECONN*, ENOTFOUND, socket/network errors), "not connected"/disconnected states, 5xx responses, 429, and fetch failures.

What does not retry. Capability errors (a poll on Meta will never succeed on Meta) and 4xx client errors other than 429 — those would fail identically on the backup, so retrying only wastes time and duplicates nothing.

Every send result carries a via field — "primary" or "fallback" — so you can tell from the response, and from your logs, which engine actually delivered the message.

Failover crosses engines, so it also crosses capability sets. Pair it with sends both providers support (text, media, interactive) — a Baileys-only send has nothing useful to fall back to on Meta, and vice versa.

  • Sessions — lifecycle, status values, and per-session settings
  • Templates — sending approved templates, which requires this provider
  • Anti-ban controls — send pacing, which matters far more on Baileys
  • Webhooks — the events both providers emit

On this page