Send template message
Send an approved WhatsApp template to start a conversation with a customer. If a conversation is already open, the message is appended to it; otherwise, a new one is created.
This endpoint currently supports the WhatsApp channel only.
POST https://api.platica.mx/v1/messages/template Request body
{
"channelId": "wb-123456789",
"conversationId": "521234567890",
"responderAgentId": "agent_123",
"campaignId": "pV6PvG2lc8ChQb2VmETr",
"template": {
"name": "bienvenida_cliente",
"params": ["Juan", "Premium"],
"type": "image",
"file": "https://cdn.assets.com/welcome_image.jpg",
"buttons": [
{
"index": 0,
"parameters": [{ "type": "text", "text": "promo/enero" }]
}
]
},
"client": {
"name": "José",
"customFields": {
"placas": "ABC123",
"id_recepcion": "REC001"
}
}
} Body parameters
| Field | Type | Description | Required |
|---|---|---|---|
channelId | string | Sender channel. Prefer its prefixedChannelId (for example wb-123); the agent phone or legacy channelId remain accepted for compatibility | ✓ |
conversationId | string | Customer phone number in E.164 format without + | ✓ |
responderAgentId | string | Optional agent that will handle replies. The agent does not need to own the sender channel | — |
campaignId | string | ID of the campaign the message is attributed to. If omitted, it is logged under the api campaign | — |
template.name | string | Name of the template to use | ✓ |
template.params | array | Variables for the template body. The count must exactly match the number of variables defined in the template | — |
template.type | enum | Header type: image, document, video. Required if the template has a media header | — |
template.file | string | Public https:// URL of the header file. Required if template.type is present | — |
template.buttons | array | Dynamic variables for the template buttons. Maximum 3 items | — |
delay | number | Delay in milliseconds (3,000 – 86,400,000; maximum 24 hours) | — |
scheduleTime | string | Scheduled send date/time (ISO 8601). Maximum 30 days after the request | — |
client | object | Customer information to update (upsert) | — |
If the client object is sent, the customer profile is updated (or created if it does not exist), giving the AI agent immediate access to the new data.
Sender channel and responder
The channel and responder are independent:
channelIdchooses the number/channel used to send.responderAgentIdchooses the agent that handles replies.- When
responderAgentIdis omitted with a canonicalchannelId, the new conversation staysinitiatedand unassigned.channel.defaultAgentIdis inbound routing and is not inherited. - A legacy identifier (agent phone) still preserves that inferred agent.
- The message is still sent without an agent. When the contact replies, the
thread becomes
supervisedand stays without AI until an agent is assigned. - An existing active conversation for the same contact and channel keeps its current agent.
Each item in the array represents a dynamic template button. Static buttons (no variables) do not need to be included.
| Field | Type | Description | Required |
|---|---|---|---|
index | integer | 0-based index of the button in the template. Must point to an existing button | ✓ |
sub_type | enum | url, phone_number, quick_reply, otp. Inferred automatically from the type defined in the template if omitted | — |
parameters | array | Exactly one item with { "type": "text", "text": "<value>" } | ✓ |
Important rules:
- Indexes must not repeat within the same array
- For URL buttons,
textmust be a path segment (e.g."promo/enero"), not a full URL. Sending"https://..."returns a 400 error - If
sub_typeis omitted, it is inferred from the button type defined in the template (URL→url,OTP→otp,PHONE_NUMBER→phone_number)
"buttons": [
{
"index": 0,
"parameters": [{ "type": "text", "text": "promo/enero" }]
},
{
"index": 1,
"sub_type": "otp",
"parameters": [{ "type": "text", "text": "482910" }]
}
] Scheduling
Send the template immediately, after a delay, or at a specific time. You can then retrieve, reschedule, or cancel it by messageId .
Delayed send (delay):
- Minimum:
3000ms (3 seconds) - Maximum:
86400000ms (24 hours) - Example:
"delay": 5000sends the message in 5 seconds
Scheduled send (scheduleTime):
- ISO 8601 format
- Must be a future date
- Cannot be more than 30 days after the request
- Examples:
"2026-10-15T18:40:00"— Mexico time (UTC-6)"2026-10-15T18:40:00Z"— UTC time"2026-10-15T18:40:00-06:00"— With a specific time zone
You cannot use delay and scheduleTime in the same request.
| Field | Type | Description |
|---|---|---|
client.name | string | Customer full name |
client.firstname | string | First name |
client.lastname | string | Last name |
client.email | string | Email address |
client.birthdate | string | Date of birth (dd/mm/yyyy) |
client.gender | enum | Gender: male or female |
client.company | string | Company name |
client.country | enum | ISO 3166-1 alpha-2 code (e.g. MX, US) |
client.state | string | State of residence |
client.city | string | City of residence |
client.address | string | Full address |
client.postalCode | string | Postal code |
client.customFields | map | Key-value object with the custom fields defined in the workspace. See Custom fields for what's available. |
client.owners | array | Responsible-user emails |
Template validation
When the request is received, the API verifies that the payload is compatible with the template definition registered in WhatsApp. If there are mismatches, a 400 error is returned with the details of each issue.
Rules that are validated automatically:
- Param count: must exactly match the number of
{{1}},{{2}}... variables in the template body - Media header: if the template has an image, video, or document header,
template.typeandtemplate.fileare required andtypemust match the defined format - Button indexes: every
indexintemplate.buttonsmust correspond to an existing button in the template - URL button variables: the
textvalue must be a path segment, not a full URL
{
"message": "Template payload is incompatible with template configuration",
"template": "bienvenida_cliente",
"errors": [
{
"field": "template.params",
"message": "The number of params does not match the template definition",
"expected": 2,
"received": 1
},
{
"field": "template.type",
"message": "This template requires media header type",
"expected": "image",
"received": null,
"example": {
"type": "image",
"file": "https://example.com/your-media-file"
}
}
]
} Response
The response shape depends on whether the message is sent immediately, with a delay, or scheduled.
Immediate send:
{
"messageId": "msg_123xyz",
"status": "sent",
"timestamp": "2026-09-20T20:00:00Z"
} With delay (HTTP 202):
{
"messageId": "msg_123xyz",
"kind": "template",
"status": "delayed",
"timestamp": "2026-09-20T20:00:00Z",
"executeAt": "2026-09-20T20:00:05.000Z",
"delayMs": 5000
} With scheduleTime (HTTP 202):
{
"messageId": "msg_123xyz",
"kind": "template",
"status": "scheduled",
"timestamp": "2026-09-20T20:00:00Z",
"executeAt": "2026-10-16T00:40:00.000Z",
"scheduledTime": "2026-10-15T18:40:00"
} | Field | Type | Description |
|---|---|---|
messageId | string | ID of the created message. For scheduled sends, use it to retrieve, reschedule, or cancel the resource ; you do not need campaignId or runId |
kind | enum | template in HTTP 202 responses from this endpoint |
status | enum | sent, delayed, or scheduled |
timestamp | string | Date/time when the request was processed (ISO 8601 UTC) |
executeAt | string | Date/time when the message will be sent (ISO 8601 UTC). Only present in delayed or scheduled sends |
delayMs | number | Configured delay in milliseconds. Only present when delay was used |
scheduledTime | string | Original scheduleTime value sent in the request. Only present when scheduleTime was used |
Sends with delay or scheduleTime are accepted but have not been sent yet. See Scheduled Messages for their states and execution rules.
Blocked contact
A contact with status: "blocked" never receives template messages. The API
returns:
{
"code": 409,
"error": "Conflict - The resource state conflicts with this request",
"details": {
"code": "CONTACT_BLOCKED",
"clientId": "client_123",
"status": "blocked"
}
} The policy is checked again when delayed or scheduled messages execute.
WhatsApp rules
Templates are mandatory
You must use a pre-approved template to start a conversation. You cannot send free-form text messages until the customer replies.
24-hour window
Conversations stay active for 24 hours since the customer's last message.
If the customer does NOT reply:
- You send the template
- The customer does not reply
- You cannot send more messages until they reply
If the customer DOES reply:
- You send the template
- The customer replies
- You can send messages freely for 24 hours
- Each customer message resets the window
Delivery limitations
WhatsApp can block messages because of:
- The customer's country restrictions
- Numbers in WhatsApp experiments
- Customers who turned off marketing notifications
Checking status
Confirm your message was delivered:
- From the platform's inbox
- Using the Get Conversation endpoint