Messages
Send pre-approved WhatsApp template messages through the tenant's active WABA.
The Messages API sends WhatsApp messages. Only pre-approved template messages are sendable via the Public API — free-form session messaging is dashboard-only because it requires careful policy enforcement (24-hour reply window, opt-out tracking) Lynkist does not yet expose to API integrators.
| Scope | Endpoint |
|---|---|
messages:send | POST /messages/template |
Base path: /api/v1/public/messages
Prerequisites
- The tenant must have an active, connected WABA (WhatsApp Business Account). If not, the
endpoint returns
404 No active WABA account for this tenant. - The
template_namemust exist and be inapprovedstatus under that WABA. See Templates for the approval flow. - The recipient phone number must be in E.164 format (e.g.
+919999999999).
POST /messages/template — Send a template message
curl -X POST https://api.lynkist.io/api/v1/public/messages/template \
-H "Authorization: Bearer $LYNKIST_API_KEY" \
-H "Idempotency-Key: 7c4d2e1a-…" \
-H "Content-Type: application/json" \
-d '{
"to": "+919999999999",
"template_name": "order_confirmation",
"language": "en_US",
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Anurag" },
{ "type": "text", "text": "ORD-1042" }
]
}
]
}'Request body
| Field | Type | Required | Notes |
|---|---|---|---|
to | string | Yes | Recipient phone in E.164 (+<country><number>). |
template_name | string | Yes | Approved template name. |
language | string | No (default en) | Template locale, e.g. en_US, hi. Must match an approved variant. |
components | object[] | No | Variable substitutions. Required only if the template has variables. |
components shape
The array follows Meta's Cloud API conventions. Each entry targets one component on the
approved template (header, body, or button) and supplies parameters that fill its {{n}}
placeholders in order.
[
{
"type": "header",
"parameters": [
{ "type": "image", "image": { "link": "https://example.com/banner.jpg" } }
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Anurag" },
{ "type": "text", "text": "ORD-1042" },
{ "type": "text", "text": "https://example.com/track/ORD-1042" }
]
},
{
"type": "button",
"sub_type": "url",
"index": "0",
"parameters": [
{ "type": "text", "text": "ORD-1042" }
]
}
]Parameter type values: text, currency, date_time, image, document, video, payload.
See Meta's parameter reference
for the full grammar.
Response
200 OK
{
"message_id": "wamid.HBgM…",
"status": "accepted",
"to": "+919999999999"
}status is always "accepted" on success — it means the WhatsApp upstream accepted the message
for delivery. It does not mean delivered. To track real delivery, subscribe to the
message.* webhook events:
| Event | Means |
|---|---|
message.sent | Lynkist handed the message to Meta. |
message.delivered | Meta delivered to the recipient device. |
message.read | Recipient opened the message (if read receipts on). |
message.failed | Meta reported a permanent failure (carries error code). |
message_id is Meta's wamid — quote it when correlating with webhook events or Meta support.
Idempotency
Send Idempotency-Key: <uuid-v4> on every request. Retries with the same key replay the cached
response instead of sending a duplicate message — the entry lives 24 hours. See
API Reference → Idempotency.
Errors
| Status | When |
|---|---|
400 | Body missing required fields, malformed JSON, or non-E.164 phone |
401 | Missing / invalid API key |
403 | Key lacks messages:send |
404 | No active WABA account for this tenant — connect a WABA via the dashboard first |
422 | Validation error (Pydantic) — bad components shape |
429 | Rate limit hit |
502 / 504 | Meta upstream error or timeout — safe to retry |
A message.failed webhook may still arrive even on a 200 OK — that means Meta accepted but
then could not deliver (unreachable number, opted out, template paused, etc.). The webhook
payload includes Meta's numeric error code.
Common pitfalls
- Language mismatch.
languagemust match the language of an approved variant. A template approved inen_USwill not send underen— listGET /templatesto see exact values. - Variable count mismatch. Sending fewer
parametersthan the template has{{n}}placeholders rejects with422. - Sending to opted-out numbers. The send returns
200 OK, then arrives asmessage.failedvia webhook. Track opt-outs in your contact custom fields. - Marketing rate limits. Meta enforces per-WABA marketing tiers separately from Lynkist's
rate limits. A
message.failedwith code131056means you hit Meta's marketing cap.