Lynkist Developers

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.

ScopeEndpoint
messages:sendPOST /messages/template

Base path: /api/v1/public/messages

Prerequisites

  1. The tenant must have an active, connected WABA (WhatsApp Business Account). If not, the endpoint returns 404 No active WABA account for this tenant.
  2. The template_name must exist and be in approved status under that WABA. See Templates for the approval flow.
  3. The recipient phone number must be in E.164 format: the country code, then the number, e.g. +919999999999. See Phone number format.
  4. The wallet must hold enough credit for the message, else the send returns 402.

POST /messages/template — Send a template message

curl -X POST https://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

FieldTypeRequiredNotes
tostringYesRecipient phone in E.164 (+<country><number>). See Phone number format.
template_namestringYesApproved template name.
languagestringNoTemplate locale, e.g. en_US, hi. Must match the approved language exactly. Required only when the template is approved in more than one language.
componentsobject[]NoVariable substitutions. Required only if the template has variables.

Phone number format

to is an E.164 number: the country code, then the subscriber number, at most 15 digits.

You sendResult
+919999999999Accepted.
919999999999Accepted. The + is optional.
+91 99999-99999Accepted. Spaces, hyphens, dots and brackets are removed.
9999999999400 — no country code. Lynkist does not guess the country.
+91abc9999999999400 — letters are not allowed.
+9184599983838383400 — not a valid number for any country.

The error is 400 VALIDATION_ERROR, and error.details names the to field. The response returns to in E.164 with the +.

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:

EventMeans
message.sentLynkist handed the message to Meta.
message.deliveredMeta delivered to the recipient device.
message.readRecipient opened the message (if read receipts on).
message.failedMeta reported a permanent failure (carries error code).

message_id is Meta's wamid — quote it when correlating with webhook events or Meta support.

In the dashboard inbox

A template sent through the API shows in the chat with an API label and the name of the key that sent it. It does not move the chat to the top of your team's inbox: a chat that holds only API, campaign or integration messages is listed under More → Automated, and joins the inbox when the customer replies. So a high-volume integration (order updates, OTPs) never buries the chats your team must answer.

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

Statuserror.codeWhen
400VALIDATION_ERRORA missing field, malformed JSON, a to that is not E.164, or a template approved in more than one language sent without language.
400INVALID_TEMPLATE_PARAMETERS, TEMPLATE_REJECTEDThe components do not match the template.
400RECIPIENT_UNDELIVERABLE, MEDIA_REJECTED, MESSAGE_SEND_FAILEDWhatsApp rejected this message.
401INVALID_API_KEY, API_KEY_EXPIREDThe API key is missing, wrong, revoked or expired.
402INSUFFICIENT_WALLET_BALANCENot enough wallet credit. Top up the wallet, then retry.
403INSUFFICIENT_SCOPEThe key lacks messages:send.
403SENDING_NOT_ALLOWEDThe workspace or its plan cannot send WhatsApp messages.
404TEMPLATE_NOT_FOUNDNo approved template with that name (in that language, when you sent one).
404WHATSAPP_NOT_CONNECTEDNo active WhatsApp account is connected. Connect one in the dashboard first.
409DISPLAY_NAME_NOT_APPROVED, CUSTOMER_SERVICE_WINDOW_CLOSED, TEMPLATE_UNAVAILABLE, ACCOUNT_RESTRICTED, WHATSAPP_PAYMENT_REQUIRED, NUMBER_NOT_REGISTERED, WHATSAPP_CONNECTION_ERRORWhatsApp refused the send, and something must be fixed first. error.message says what.
429RATE_LIMIT_EXCEEDEDYour rate limit. Wait for Retry-After.
429SEND_RATE_LIMITEDWhatsApp is limiting sends from this number. Wait, then retry.
502, 503UPSTREAM_ERROR, WHATSAPP_UNAVAILABLEA failure on our side or at WhatsApp. Retry with backoff.
504SEND_OUTCOME_UNKNOWNWhatsApp did not confirm in time; the message can still arrive. Do not resend automatically.

Every send error has retryable (true or false) beside code and message.

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. language must match the approved language exactly. A template approved in en_US is not found under en (404) — list GET /templates to see exact values. Leave language out when the template has only one approved language.
  • Variable count mismatch. Sending fewer parameters than the template has {{n}} placeholders rejects with 400.
  • Sending to opted-out numbers. The send returns 200 OK, then arrives as message.failed via 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.failed with code 131056 means you hit Meta's marketing cap.
Messages API | Lynkist Developers