WhatsApp API Platform
Template Messages

Send Template with a Dynamic-Link Button

POST
/api/v1/messages/send#template-dynamic-link

Send a template that has a dynamic-link URL button. Pass the button under buttons with button_type: dynamic_url and button_payload set to the full destination URL. The server proxies the URL through a tracked dynamic link and substitutes a per-recipient short slug into the template's dynamic-link button, so clicks are attributed to the recipient.

Only valid for template messages. button_index must point at a dynamic-link button in the approved template; otherwise the request returns 400.

Authorization

ApiKeyAuth
X-API-Key<token>

API key from your dashboard (Settings > API Keys)

In: header

Header Parameters

X-AGENT-ID?string

Optional. Attribute this send to a specific agent, overriding the agent bound to the API key. The agent must belong to your account and be active (role AGENT, parent = your account) — otherwise the request returns 400. Drives customer assignment, agent allotment limits, and the wallet ledger entry.

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://loading/api/v1/messages/send#template-dynamic-link" \  -H "X-AGENT-ID: agt_abc123" \  -H "Content-Type: application/json" \  -d '{    "to": "919876543210",    "message": {      "message_type": "template",      "template_name": "spring_sale",      "template_language": "en_US",      "body_variables": [        "John"      ],      "buttons": [        {          "button_type": "dynamic_url",          "button_index": 0,          "button_payload": "https://shop.example.com/sale?ref=spring"        }      ]    }  }'
{
  "success": true,
  "data": {
    "id": "clx9abc123def456"
  }
}
{
  "success": false,
  "message": "to field is required"
}
{
  "success": false,
  "message": "user not authenticated"
}
{
  "success": false,
  "message": "insufficient balance to send message"
}
{
  "success": false,
  "message": "template not found"
}
{
  "success": false,
  "message": "internal server error"
}

Send Template in a Campaign POST

Group API template sends into a campaign by passing `campaign_id`, any string you choose (an order batch, a date, your own campaign key). The first send with a new `campaign_id` creates a campaign in the dashboard's **Campaigns** list; every later send with the same id joins it. It gets the usual campaign reporting: sent, delivered, read, failed, button replies and the recipient list. - `campaign_name` names the campaign when it is created (default `Custom campaign`) and is ignored afterwards. - Template messages only. - A campaign is locked to the template and WhatsApp number of its first send. A different template under the same id returns `400 INVALID_CAMPAIGN`; use a new id instead. - Quick-reply buttons get a campaign payload (`bc:<id>:<index>`) so replies are attributed to the campaign, unless you pass that button yourself. - Billing is unchanged: each message is charged when sent, exactly like any API send. - The id is echoed back as `campaign_id` in the response and in `message.report` webhooks. Also works on `POST /api/v1/messages/send/bulk` (one campaign for the whole batch) and the GET send endpoint (`campaignId` / `campaignName` query parameters). Dashboard campaigns and API campaign ids are separate.

Send Template with a Flow Button POST

Send an approved template that carries a FLOW button. This is how a Flow starts a business-initiated conversation, outside the 24-hour session window. The template and its Flow are authored in Meta Business Manager; the template reaches this account through the usual template sync. Pass the button under `buttons` with `button_type: flow`, `button_payload` set to your flow_token, and optionally `flow_action_data` for the Flow's first screen. Both button fields are optional as far as WhatsApp is concerned — omit the button entirely and the Flow still opens, but with the token `"unused"`, leaving you nothing to correlate the submission against. `button_index` defaults to the position of the FLOW button in the approved template, so you normally do not need to set it. Sending a `flow` button against a template that has none returns `400`.