Mensajes y adjuntos
Enviá un mensaje desde una conversación existente y seguí su estado de entrega. Usá las variables de la introducción y reemplazá 4821 por el ID público de tu conversación de prueba.
Enviar texto
curl --fail-with-body --request POST \
"$CRM_BASE_URL/api/v1/accounts/$CRM_ACCOUNT_ID/conversations/4821/messages" \
--header "api_access_token: $CRM_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "content": "Hola Ana, recibimos tu consulta.", "message_type": "outgoing", "private": false }'
Guardá el id del mensaje devuelto. La respuesta confirma que el CRM creó el mensaje; no garantiza que el cliente ya lo recibió. La entrega al canal se procesa por separado.
Agregar una nota privada
La misma ruta permite escribir información interna que no se envía al contacto:
curl --fail-with-body --request POST \
"$CRM_BASE_URL/api/v1/accounts/$CRM_ACCOUNT_ID/conversations/4821/messages" \
--header "api_access_token: $CRM_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "content": "Pedido ERP-2048 confirmado.", "message_type": "outgoing", "private": true }'
No confundas una nota privada del hilo con una nota del contacto (POST .../contacts/{contact_id}/notes). Las notas privadas también pueden llegar a tus webhooks; descartalas si tu integración solo debe procesar mensajes públicos.
Adjuntar un archivo
Usá attachments[] con multipart/form-data. curl -F genera el encabezado correcto; no agregues Content-Type: application/json:
curl --fail-with-body --request POST \
"$CRM_BASE_URL/api/v1/accounts/$CRM_ACCOUNT_ID/conversations/4821/messages" \
--header "api_access_token: $CRM_API_TOKEN" \
--form "message_type=outgoing" \
--form "private=false" \
--form "content=Te comparto el presupuesto." \
--form "attachments[]=@./presupuesto.pdf;type=application/pdf"
El archivo debe existir en tu máquina. La aceptación depende del canal, formato y tamaño. Consultá el estado final y el motivo del error si el proveedor rechaza el envío.
Los mensajes leídos por API pueden incluir attachments[].data_url. Estas URLs pueden permitir acceso sin el token del CRM: tratá los enlaces y los exports como datos privados. No asumas que las URLs del proveedor o sus miniaturas duran para siempre.
WhatsApp: plantillas y ventana de atención
En WhatsApp oficial, un mensaje libre depende de la ventana de atención del canal. Consultá can_reply en la conversación como orientación y verificá el resultado del envío. Fuera de la ventana, usá una plantilla aprobada para esa conexión.
Ejemplo para WhatsApp nativo, con una plantilla de texto que tiene una variable. Reemplazá nombre, idioma y valores por los aprobados en tu cuenta:
curl --fail-with-body --request POST \
"$CRM_BASE_URL/api/v1/accounts/$CRM_ACCOUNT_ID/conversations/4821/messages" \
--header "api_access_token: $CRM_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"content": "Hola Ana, tu pedido está listo.",
"message_type": "outgoing",
"private": false,
"template_params": {
"name": "pedido_listo",
"category": "UTILITY",
"language": "es_AR",
"processed_params": { "1": "Ana" }
}
}'
No cambies message_type a template para elegir una plantilla de WhatsApp: en este request es outgoing y la configuración va en template_params. Este ejemplo no cubre plantillas con botones o encabezados multimedia ni conectores externos Channel::Api, cuyo contrato puede ser diferente.
Leer y seguir la entrega
curl --fail-with-body \
"$CRM_BASE_URL/api/v1/accounts/$CRM_ACCOUNT_ID/conversations/4821/messages" \
--header "api_access_token: $CRM_API_TOKEN"
Leé payload[] y buscá el ID que guardaste. Para mensajes más antiguos usá la paginación de exportar historial. Suscribite también a message_updated para recibir cambios cuando el canal los emita.
status |
Cómo interpretarlo |
|---|---|
sent |
Registrado para envío / enviado según el canal. No prueba recepción. |
delivered |
El canal informó entrega. |
read |
El canal informó lectura. No todos los canales la reportan. |
failed |
Hubo un fallo; revisá content_attributes.external_error por API o external_error en el webhook cuando esté presente. |
Algunas actualizaciones de conectores se guardan sin emitir message_updated. Si el estado es importante para tu negocio, reconciliá periódicamente por API y no dependas exclusivamente del webhook.
Evitar envíos duplicados
No se documenta un header de idempotencia para crear mensajes. Si el request termina en timeout, el mensaje pudo haberse creado. No repitas el POST automáticamente: verificá el hilo y tu registro de envíos primero. Conservá una referencia local entre tu operación y el ID del mensaje cuando recibas la respuesta.