A WhatsApp Business number connects to a profile like any account (acc_…), through Meta’s embedded signup: GET /v1/connect/whatsapp. When the WhatsApp Business account has several numbers, Marsial comes back with select=<sessionId> so you pick one, as with Facebook. A number already used in the WhatsApp Business app can stay there too.
WhatsApp takes no posts: it is for conversations.
WhatsApp’s two rules
Section titled “WhatsApp’s two rules”- The 24-hour window. For 24 hours after someone’s last message you reply with anything, from the inbox. Outside that window, or to write first, only an approved template works.
- The daily limit. A new number can write to 250 different people a day; Meta raises it as you use it without complaints.
Templates
Section titled “Templates”Meta reviews every template before you can use it (usually minutes, at most 24 hours). Each person’s details go in {{1}}, {{2}}… with an example of each.
curl -X POST "https://api.marsial.dev/v1/accounts/acc_3k9x2m/whatsapp/templates" \ -H "Authorization: Bearer $MARSIAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "order_ready", "category": "UTILITY", "language": "en", "components": [ { "type": "BODY", "text": "Hi {{1}}, your order {{2}} is ready.", "example": { "body_text": [ [ "Lucía", "#4821" ] ] } } ] }'const res = await fetch('https://api.marsial.dev/v1/accounts/acc_3k9x2m/whatsapp/templates', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "name": "order_ready", "category": "UTILITY", "language": "en", "components": [ { "type": "BODY", "text": "Hi {{1}}, your order {{2}} is ready.", "example": { "body_text": [ [ "Lucía", "#4821" ] ] } } ] }),});const { data } = await res.json();import os, requests
res = requests.post( "https://api.marsial.dev/v1/accounts/acc_3k9x2m/whatsapp/templates", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"}, json={ "name": "order_ready", "category": "UTILITY", "language": "en", "components": [ { "type": "BODY", "text": "Hi {{1}}, your order {{2}} is ready.", "example": { "body_text": [ [ "Lucía", "#4821" ] ] } } ] },)data = res.json()["data"]It starts as PENDING; GET /v1/accounts/{id}/whatsapp/templates shows when it becomes APPROVED (or REJECTED). The category decides what Meta charges: UTILITY (orders, bookings), MARKETING or AUTHENTICATION (codes).
Write first
Section titled “Write first”An approved template opens the conversation. The reply arrives in the inbox and as message.received, and you carry on with POST /v1/inbox/conversations/{id}/messages.
curl -X POST "https://api.marsial.dev/v1/inbox/conversations" \ -H "Authorization: Bearer $MARSIAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "accountId": "acc_3k9x2m", "phone": "+34 611 222 333", "template": { "name": "order_ready", "language": "en", "params": [ "Lucía", "#4821" ] } }'const res = await fetch('https://api.marsial.dev/v1/inbox/conversations', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "accountId": "acc_3k9x2m", "phone": "+34 611 222 333", "template": { "name": "order_ready", "language": "en", "params": [ "Lucía", "#4821" ] } }),});const { data } = await res.json();import os, requests
res = requests.post( "https://api.marsial.dev/v1/inbox/conversations", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"}, json={ "accountId": "acc_3k9x2m", "phone": "+34 611 222 333", "template": { "name": "order_ready", "language": "en", "params": [ "Lucía", "#4821" ] } },)data = res.json()["data"]Broadcasts
Section titled “Broadcasts”An approved template to many numbers, now or scheduled. Each {{n}} is filled per person with their name or phone, or with the same text for everyone.
curl -X POST "https://api.marsial.dev/v1/accounts/acc_3k9x2m/whatsapp/broadcasts" \ -H "Authorization: Bearer $MARSIAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Black Friday", "template": { "name": "black_friday_offer", "language": "en", "variables": [ { "field": "name" }, { "value": "BF30" } ] }, "phones": [ "+34611222333", "+34622333444" ], "scheduledFor": "2026-11-27T09:00:00+01:00" }'const res = await fetch('https://api.marsial.dev/v1/accounts/acc_3k9x2m/whatsapp/broadcasts', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "name": "Black Friday", "template": { "name": "black_friday_offer", "language": "en", "variables": [ { "field": "name" }, { "value": "BF30" } ] }, "phones": [ "+34611222333", "+34622333444" ], "scheduledFor": "2026-11-27T09:00:00+01:00" }),});const { data } = await res.json();import os, requests
res = requests.post( "https://api.marsial.dev/v1/accounts/acc_3k9x2m/whatsapp/broadcasts", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"}, json={ "name": "Black Friday", "template": { "name": "black_friday_offer", "language": "en", "variables": [ { "field": "name" }, { "value": "BF30" } ] }, "phones": [ "+34611222333", "+34622333444" ], "scheduledFor": "2026-11-27T09:00:00+01:00" },)data = res.json()["data"]GET /v1/accounts/{id}/whatsapp/broadcasts returns each broadcast with how many received, read or failed; POST …/broadcasts/{broadcastId}/cancel stops a scheduled one or what is left to send.
What it costs
Section titled “What it costs”Every message you send (every broadcast recipient) counts as a sent message. On top, Meta charges its fee for each template directly to your WhatsApp Business account. Replying within the 24-hour window carries no Meta fee.
In the dashboard, all of this is under Inbox → WhatsApp.
© 2026 Marsial by TWOFACES ·marsial.dev ·Dashboard ·soporte@twofacesco.com