Send a WhatsApp message
Send an approved template anytime, or free-form text, media, and interactive messages inside the 24-hour service window.
With a default business account connected and an approved
template, send WhatsApp through the unified messages API with
channel: "whatsapp". What you can send depends on the recipient's 24-hour service window.
Send a template message
Templates can be sent anytime, inside or outside the service window, which makes them how you
start a conversation. Reference the template by its handle or id and pass the values its schema
declares in templateData. Use the SDK's raw HTTP client for the WhatsApp request. It uses
your configured API key and accepts the endpoint's request body:
import { createClient } from "samva";
const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
const receipt = await samva.raw.client.post({
url: "/v1/messages",
body: {
channel: "whatsapp",
to: [{ phone: "+16175559876" }],
whatsapp: {
templateSlug: "order-update",
templateData: { firstName: "Alice", orderNumber: "ORD-12345" },
},
},
responseStyle: "data",
throwOnError: true,
});
console.log(receipt);To send one template to an audience or from a journey, see Broadcasts and journeys.
Send free-form text
Free-form text is only allowed inside the 24-hour window (opened when the customer last messaged you). Outside it, Samva rejects the send with a validation error telling you to use a template.
curl -X POST https://api.samva.dev/v1/messages \
-H "X-API-Key: samva_sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"channel": "whatsapp",
"to": [{ "phone": "+16175559876" }],
"whatsapp": { "text": "Thanks! Your order is on the way." }
}'Send media and interactive messages
Inside the window you can also send media, reply buttons, lists, call-to-action links, reactions,
locations, and contacts. Each is one more key under whatsapp, and a send carries exactly one:
curl -X POST https://api.samva.dev/v1/messages \
-H "X-API-Key: samva_sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"channel": "whatsapp",
"to": [{ "phone": "+16175559876" }],
"whatsapp": {
"media": { "type": "document", "url": "https://example.com/receipt.pdf", "caption": "Your receipt" }
}
}'See Interactive and media messages for every content type, how to upload files, and how to receive the customer's replies.
Send errors
A send can be refused before it reaches Meta:
| Situation | Error |
|---|---|
| Free-form content (anything but a template) outside the service window | Validation error: use a template. |
| The number is not registered yet (still pending verification) | Sender not ready: finish verification, then sync the account. |
| Meta banned the number | Forbidden: review the number in WhatsApp Manager, then sync it. |
| Meta disabled the WhatsApp business account | Forbidden: sending resumes when Meta reinstates the account. |
| A template send to a new customer past the business portfolio's messaging limit | Rate limited, with retryAfterSeconds until the next slot opens. |
| The template has no Meta-approved registration on the account | Conflict: submit the template and send once Meta approves it. |
templateData does not match the template's schema | Validation error, naming the fields to fix. |
| A marketing template to a number with no recorded opt-in | Validation error: record the opt-in first. |
Every number in a business portfolio shares one messaging limit. See Business account for what each state means.
Opt-in and opt-out
Samva records each number's WhatsApp consent on
its contact. A recipient who replies STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, or QUIT
(the whole message, in any case) is opted out of WhatsApp, and START or UNSTOP opts them back
in. Samva records the change with the source whatsapp_keyword. When a customer stops or resumes
marketing messages in WhatsApp itself, Meta reports it on the user_preferences webhook field and
Samva records it with the source whatsapp_preferences. Each change publishes
contact.consent_changed.
An opted-out number is never sent to: the message fails with errorCode SUPPRESSED and a
message.failed webhook. A template whose category is marketing also needs the number's recorded
opt-in. Without it, the request is refused with a 422 validation error, and a message that waited
in the queue while the opt-in was withdrawn fails with errorCode CONSENT_REQUIRED. Record the
opt-in with
PUT /v1/contacts/{id}/identities/{identityId}/consent/whatsapp.
Utility templates need no recorded opt-in.
Recipients
to is an array; each entry is a WhatsApp number or a stored contact:
| Form | Example |
|---|---|
| WhatsApp number | { "phone": "+16175559876" } |
| Existing contact reference | { "contactId": "contact_k9mvt4znw8rh7q2x" } |
Check the service window
Before a free-form send, you can check whether the window is open:
curl "https://api.samva.dev/v1/whatsapp/service-window/status?customerPhone=+16175559876&businessPhone=+16175550123" \
-H "X-API-Key: samva_sk_live_your_api_key"{
"serviceWindowId": "swin_a1b2c3",
"isServiceWindowActive": true,
"serviceWindowExpiresAt": "2026-06-23T10:30:00.000Z",
"isFreeEntryPointActive": false,
"freeEntryPointExpiresAt": null
}Delivery status
Track a send by its id: WhatsApp reports sent, delivered, read, and failed:
import { createClient } from "samva";
const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
const message = await samva.messages.get({ id: "msg_7q2xk9mvt4znw8rh" });
console.log("Status:", message.status);Related documentation
Message templates
Author WhatsApp message templates in TSX, publish them, submit them to Meta for review, and send them once approved.
Broadcasts and journeys
Send approved WhatsApp templates to an audience with campaigns or journey steps, fill parameters from each contact, and read skips and message costs.