Documentation
Quickstart
FusionWA provides WhatsApp Business Cloud API infrastructure for SaaS companies and software houses. Your customers connect their own WhatsApp Business accounts through Meta Embedded Signup — you get the API and webhooks.
SDK Widget
Customers connect on their own. 3 lines of HTML.
REST API
Send messages and manage connections from your backend.
Webhooks
Receive messages and status updates in real time.
Coexistence
Eligible customers keep WhatsApp Business on their phone.
Step 1 — Create an account and application
Register at fusionwa.com/register. Then go to Applications and create your first App. Copy the API Key and API Secret.
Step 2 — Embed the connection widget
From your backend, request a short-lived widget token for the customer who is about to see the widget (server-to-server, with your API Secret — never in the browser):
curl -X POST https://fusionwa.com/api/v1/widget/token \
-H "x-fusionwa-api-key: fwa_live_..." \
-H "x-fusionwa-api-secret: <your-secret>" \
-H "Content-Type: application/json" \
-d '{ "customerId": "user_123" }'
# Response: { "token": "eyJ...", "expiresAt": "2026-10-25T18:00:00.000Z" }Pass that token to the widget in your frontend. The token is bound to this one customer and expires in 30 minutes — request a fresh one each time the widget is rendered.
<script src="https://fusionwa.com/sdk/v1.js"></script>
<div id="fusionwa-widget"></div>
<script>
FusionWA.init({
token: "eyJ...", // from POST /api/v1/widget/token, generated on your backend
containerId: "fusionwa-widget",
});
</script>The SDK renders a secure iframe hosted by fusionwa.com, so you do not need to register your own domain with Meta. If your site uses a Content Security Policy, allow https://fusionwa.com in both script-src andframe-src.
Step 3 — Customer connects through Meta Embedded Signup
Your customer clicks "Connect WhatsApp". Meta Embedded Signup opens in a popup. They authorize their WhatsApp Business account. FusionWA receives and stores the connection securely.
For eligible WhatsApp Business App numbers, Meta may offer the Coexistence flow — see the Coexistence section for details.
Step 4 — Configure your webhook
In the dashboard under Applications, set a webhook URL. FusionWA will forward every incoming message and status update to your endpoint in real time.
Step 5 — Send your first message
curl -X POST https://fusionwa.com/api/v1/messages/send \
-H "X-FusionWA-API-Key: fwa_live_..." \
-H "Content-Type: application/json" \
-d '{
"to": "+39 02 1234567",
"templateName": "hello_world",
"languageCode": "it"
}'
# Response: { "messageId": "wamid.abc123..." }SDK Widget
The fastest way to integrate FusionWA. Paste the snippet into your software: the widget manages the full Meta Embedded Signup flow and WhatsApp Business connection on behalf of your customer.
Initialization
FusionWA.init({
token: "eyJ...", // from POST /api/v1/widget/token (server-to-server, your backend)
containerId: "fusionwa-widget", // id of the div to render into
});Widget states
NOT_SUBSCRIBEDCustomer not yet activated. Shows "Activate" button.SUBSCRIBED_UNCONNECTEDActivated but not yet connected. Shows "Connect WhatsApp" button.CONNECTEDWhatsApp Business connected. Shows number and status.customerId and the widget token
Use your internal customer identifier — any unique string per App — when requesting a token from POST /api/v1/widget/token. AppUsers are created automatically on first widget activation. The same customerId is used in server-to-server API calls (with your API Secret) to reference that customer's connection.
token is safe to expose client-side: it is short-lived (30 minutes) and bound to one specific customer — unlike the raw customerId, it cannot be reused to read or connect a different customer's number. Generate it on your backend with your API Secret, never in the browser.WhatsApp Business App Coexistence
Coexistence is a Meta feature that allows a WhatsApp Business App number to be connected to the WhatsApp Business Cloud API simultaneously. When active, the customer continues receiving messages on their phone while the Cloud API (FusionWA) can also send and receive on the same number.
Who it applies to
Coexistence applies to eligible customers who already have an active WhatsApp Business App installation on a mobile device. Not every account is eligible — Meta determines eligibility during the Embedded Signup flow.
The Coexistence flow
When a customer connects via FusionWA and Meta offers Coexistence:
- Customer clicks "Connect WhatsApp" in your software.
- Meta Embedded Signup popup opens.
- Customer selects their existing WhatsApp Business account.
- Meta may prompt the customer to scan a QR code on their phone (this is Meta's process, not FusionWA's).
- Customer scans the QR in their WhatsApp Business App when requested.
- Connection completes — the number is now live on both the phone app and the Cloud API.
After connecting with Coexistence
The customer continues using WhatsApp Business on their phone normally — sending and receiving from the app as before. The Cloud API (your software via FusionWA) can also send messages and receives all incoming messages via webhook simultaneously.
Coexistence heartbeat
FusionWA automatically manages the Coexistence heartbeat with Meta. No additional configuration is required on your end.
// No extra configuration needed.
// FusionWA automatically manages the Coexistence
// heartbeat with Meta.
//
// The number stays active on both the mobile app
// and the Cloud API simultaneously.REST API
Full machine-readable spec: /openapi.yaml (OpenAPI 3.1 — import into Postman or Insomnia).
Widget endpoints (called from the browser, inside the SDK) take a signed token — no API Key or Secret in the browser. Server-to-server endpoints require both x-fusionwa-api-key and x-fusionwa-api-secret.
Create a widget token
Call this from your backend before rendering the widget for a given customer — see SDK Widget.
POST /api/v1/widget/token
x-fusionwa-api-key: fwa_live_...
x-fusionwa-api-secret: <your-secret>
{ "customerId": "user_123" }
# Response
{ "token": "eyJ...", "expiresAt": "2026-10-25T18:00:00.000Z" }Send a template message
POST /api/v1/messages/send
x-fusionwa-api-key: fwa_live_...
x-fusionwa-api-secret: <your-secret>
{
"externalCustomerId": "user_123",
"toPhoneNumber": "+393331234567",
"template": {
"name": "booking_confirmation",
"language": "it",
"bodyParams": ["Mario Rossi", "15 set 10:00"]
}
}
# Response
{ "status": "success", "messageId": "wamid.abc123..." }Send a free-form text message
POST /api/v1/messages/send
x-fusionwa-api-key: fwa_live_...
x-fusionwa-api-secret: <your-secret>
{
"externalCustomerId": "user_123",
"toPhoneNumber": "+393331234567",
"message": "La tua prenotazione è confermata."
}
# Only valid within a 24-hour customer-initiated windowCheck connection status
This is what the SDK widget calls internally — you generally do not need to call it yourself. It takes the same token used for FusionWA.init().
GET /api/v1/widget/status?token=eyJ...
# Response — one of:
{ "status": "NOT_SUBSCRIBED" }
{ "status": "SUBSCRIBED_UNCONNECTED", "facebookAppId": "...", "facebookConfigId": "..." }
{ "status": "CONNECTED", "phoneNumber": "+39 333 123 4567", "wabaId": "...", "phoneNumberId": "..." }List approved templates
GET /api/v1/templates?externalCustomerId=user_123
x-fusionwa-api-key: fwa_live_...
x-fusionwa-api-secret: <your-secret>
# Response
{
"status": "success",
"templates": [
{ "name": "booking_confirmation", "status": "APPROVED", "language": "it", "category": "UTILITY", "variableCount": 2 }
]
}Download received images
Call GET /api/v1/media/{mediaId}?externalCustomerId=YOUR_CUSTOMER_ID from your backend with both x-fusionwa-api-key and x-fusionwa-api-secret. Store image.id from incoming, echo or history payloads when available; the message ID is not a media ID.
The endpoint returns JPEG, PNG or WebP bytes (maximum 5 MiB), after checking the application, customer and connected phone. Serve these bytes through your own authenticated, tenant-scoped endpoint. Never expose the secret in frontend code. Responses are private and not cached.
Media can expire or become unavailable; this endpoint is not a permanent archive and cannot recover old messages whose media ID was not saved. Limit: 120 requests per minute per application. See the OpenAPI specification for error responses.
Webhooks
FusionWA forwards every WhatsApp event to your endpoint in real time. Configure the URL in Applications → Webhook in the dashboard.
Zero storage: payloads are forwarded only, never stored.
Payload structure
{
"type": "message",
"from": "+39 02 1234567",
"customerId": "user_123",
"timestamp": "2025-01-15T10:30:00Z",
"message": {
"id": "wamid.abc...",
"type": "text",
"text": { "body": "Hello" }
}
}Event types
messageIncoming message from a customerstatusMessage delivery status update (sent / delivered / read)template_statusMeta template approval status changeReceive events in your backend
// Express
app.post("/webhook/whatsapp", express.json(), (req, res) => {
const { type, from, customerId, message } = req.body;
if (type === "message" && message?.type === "text") {
console.log(`[${customerId}] ${from}: ${message.text.body}`);
// store, process, reply...
}
res.sendStatus(200); // respond 200 within 10s or the webhook is retried
});WhatsApp Templates
Templates enable proactive outbound messages (notifications, reminders, confirmations). They must be approved by Meta before use.
Create and submit a template
POST /api/v1/templates
{
"customerId": "user_123",
"name": "order_confirmed",
"category": "UTILITY",
"language": "it",
"components": [
{
"type": "BODY",
"text": "Your order {{1}} is confirmed. Delivery: {{2}}."
}
]
}The template is submitted to Meta for review. Status becomes APPROVED typically within a few hours. You receive a template_status webhook event when the status changes.
Error Reference
200Success400Invalid request body401Missing or invalid API Key403App revoked or customerId not authorized404Customer not found or not connected422Validation error (see response body)429Rate limit exceeded500Internal error — retry with backoffFor AI Coding Assistants
Use this block as initial context in your prompt when integrating FusionWA with Claude Code, Codex, Cursor, or GitHub Copilot:
# FusionWA Integration Context
Read https://fusionwa.com/llms.txt for the full machine-readable specification.
## SDK Widget
# 1. Server-to-server, with your API Secret, before rendering the widget:
POST /api/v1/widget/token { customerId } -> { token, expiresAt }
# 2. In the browser, with that token:
<script src="https://fusionwa.com/sdk/v1.js"></script>
<div id="fusionwa-widget"></div>
<script>
FusionWA.init({ token, containerId })
</script>
## REST API
Base URL: https://fusionwa.com/api/v1
Auth: X-FusionWA-API-Key: fwa_live_... (+ X-FusionWA-API-Secret for server-to-server calls)
POST /api/v1/widget/token
body: { customerId }
returns: { token, expiresAt }
POST /api/v1/messages/send
body: { to, templateName, languageCode, components[] }
returns: { messageId }
GET /api/v1/widget/status?token=
returns: { status: "INACTIVE"|"CONNECTED", phoneNumber }
GET /api/v1/templates?customerId=
returns: [{ name, status, language, category }]
## Webhooks
Configure URL in dashboard. Payload: { type, from, customerId, timestamp, message }
Respond 200 within 10s. type: "message" | "status" | "template_status"
## Security rules
- token (from POST /api/v1/widget/token): client-safe, but short-lived and scoped to one customer — generate it on your backend
- apiKey: client-safe only as part of server-to-server calls (with the secret); never alone in the browser
- API Secret: server-only (never expose)
- customerId: your internal ID, any unique string — only trusted when it comes from a server-to-server call (apiKey+secret) or from a verified widget token, never a raw value sent by the browser
- Do NOT store messages — forward to your own DB if needed
- Do NOT invent endpoints — use only the documented ones aboveDownload the machine-readable version: /llms.txt