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
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:
Examples by template type
Header with image
Header with image
Template: image header +
Hello {{1}}! Check out our {{2}} sale.Document in the header
Document in the header
Template: document header +
Hello {{1}}, here's your invoice.Currency amount and date
Currency amount and date
Template: The final formatting (
Your {{1}} invoice for {{2}} is due on {{3}}.R$ 150.50, 01/31/2026) is done by WhatsApp, based on the recipient’s
device language.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
nullis 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
owner_user_id.
Contacts already in an active conversation
By default, a contact in an active conversation does not receive the template. They land inerrors 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: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.