Skip to main content
POST /v1/messages/send-bulk-template sends an approved WhatsApp template to a list of contacts at once. Because they’re templates, these sends work outside the 24-hour window: that’s the way to start a conversation with someone who hasn’t talked to you recently. Scope: messages:create.
The template must be approved on Meta beforehand. This call triggers an existing template; it doesn’t create or submit templates for approval.

Request fields

phone needs the country code, with no symbols: 5511999999999. name can’t be empty.

Send to a list of contacts

cURL
For large lists, send a file instead of the array: multipart/form-data with the CSV or XLSX in the file field and the remaining fields in the form. The file needs the name and phone columns:
Combining a file with dynamic components parameters is a case worth testing with a few contacts before using it in production. If the parameters aren’t applied as expected, contact support.

Template parameters

An approved template has placeholders ({{1}}, {{2}}) and blocks: header, body, and buttons. You fill this in via components, and the number and type of parameters must match exactly with the approved template, otherwise Meta rejects the send. Parameter types:
Image, video, and document go in the header via a public HTTPS URL. You don’t need to upload a file or prepare anything beforehand. The URL just needs to be reachable by Meta.

Examples by template type

Template: image header + Hello {{1}}! Check out our {{2}} sale.
Template: Your order #{{1}} has been confirmed. + a button pointing to .../track/{{1}}.The button’s index is a string and starts at "0":
Template: document header + Hello {{1}}, here's your invoice.
Template: Your {{1}} invoice for {{2}} is due on {{3}}.
The final formatting (R$ 150.50, 01/31/2026) is done by WhatsApp, based on the recipient’s device language.
Carousel templates aren’t accepted on this endpoint. Use the individual template send instead.

Who answers after the send

The template opens or reopens a conversation, and someone has to answer when the contact replies. These fields define who. For the assignment, owner_user_id and attendant_user_id accept three states, and two of them are easy to mix up:
  • a user ID assigns the conversation to that person
  • null is the explicit choice of “Unassigned”, and clears whoever was there
  • leaving the field out means you had no opinion, and the current assignment stays as it is
When both come filled in, the owner takes precedence: the conversation goes to owner_user_id.
keep_existing_assignment: true preserves the current assignment and ignores who_will_answer: the AI does not take the conversation over even if you ask for AGENT. If your send needs to activate the agent, do not send that field as true.The default is false, so existing API key integrations keep their behavior.

Contacts already in an active conversation

By default, a contact in an active conversation does not receive the template. They land in errors with the reason ACTIVE_CONVERSATION_BLOCKED, and the remaining contacts go through normally. The guard exists so the template does not run over an ongoing conversation, with the contact getting a campaign message in the middle of talking to a person. To send anyway, use send_to_active_conversations: true.

The response is partial

Processing is synchronous and an error on one contact doesn’t stop the others. The response carries the consolidated result:
A 200 does not mean everyone received it. Always read errorCount and errors. If you ignore this block, send failures go unnoticed. row points to the file’s row or the index in the contacts array.
Resending to those who failed is your integration’s responsibility: there’s no automatic retry.

Limits worth knowing

  • WhatsApp account tier: Meta limits the number of unique conversations started per day (1,000, 10,000, or 100,000, depending on your account’s tier). The send respects this cap: above it, sends start to fail.
  • Media size: image up to 5 MB (JPG, PNG), video up to 16 MB (MP4, 3GPP), document up to 100 MB (PDF, DOC/DOCX, XLS/XLSX, PPT/PPTX, TXT), audio up to 16 MB.
  • Media links: must be public HTTPS URLs, reachable by Meta. A URL behind a login or an internal network fails.
  • Batch size: send in batches of up to 1,000 contacts per request.

Best practices

1

Test with a single contact

Send it to your own number first and check the result on the device. A parameter out of order only shows up in the final message.
2

Normalize phone numbers beforehand

Use the international format with no symbols (5511999999999). An invalid number becomes a row in errors, not an exception.
3

Save the conversationId values

sentContacts returns the conversation created for each contact: that’s how you track the reply, via webhooks or GET /v1/messages/conversation/{id}.
4

Treat errors as a reprocessing queue

Resend only the rows in errors, after fixing the cause.

Next steps

Webhooks

Receive the replies from those reached by the send.

API reference

The full contract for this endpoint and the other sends.