Docs

API: clientes, conversaciones y mensajes

Buscar y editar clientes, ver qué chats esperan a una persona, leer su historial, tomarlos o devolverlos al bot, escribirles por el WhatsApp del negocio (texto, fotos, archivos, notas de voz y ubicación) y bajar los archivos de los chats.

Revisado el 28 de octubre de 2026

El cliente

Un cliente nace la primera vez que le escribe al negocio por WhatsApp, o cuando alguien lo crea a mano en el panel. Lo identifica su teléfono.

json
{
  "id": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
  "object": "customer",
  "name": "María Pérez",
  "phone": "584140000000",
  "id_document": "V-00000000",
  "address": "Av. Principal de Los Palos Grandes, casa 12",
  "notes": "Prefiere que la llamen antes de llegar.",
  "name_confirmed": true,
  "bot_muted": false,
  "created_at": "2026-07-02T21:14:09.330Z",
  "updated_at": "2026-09-29T17:58:12.412Z"
}
CampoTipoQué es
iduuidId del cliente.
objecttextoSiempre customer.
nametexto | nullSu nombre, si lo dio o lo cargó el negocio.
phonetextoSolo dígitos con código de país: 584140000000.
id_documenttexto | nullCédula o RIF, si lo dio.
addresstexto | nullSu dirección guardada.
notestexto | nullNotas internas del negocio sobre el cliente. El cliente no las ve.
name_confirmedbooleanotrue si el nombre lo dio el cliente o lo puso el negocio. false si name es solo el de su perfil de WhatsApp, que el bot no da por bueno (por eso a veces se lo pregunta).
bot_mutedbooleanotrue si el cliente pidió que el bot no le responda: lo atiende solo una persona. Si el cliente vuelve a pedir que el bot le conteste, pasa a false solo.
created_atfechaCuándo se creó.
updated_atfechaÚltimo cambio.

Dentro de un pedido, una cita, un pago, un viaje o un mensaje, el cliente viene resumido: { id, name, phone }.

Buscar clientes

GET/customers

Del más nuevo al más viejo, paginado. Filtros opcionales:

ParámetroQué filtra
phoneTeléfono exacto. Se quedan solo los dígitos, así que +58 414-000.00.00 y 584140000000 valen igual; tiene que llevar el código de país.
qBusca el texto dentro del nombre, el teléfono o el documento, sin distinguir mayúsculas.
updated_afterLos que cambiaron en esa fecha o después, del más viejo al más nuevo (ver sincronización incremental).
curl
curl "https://api.atiendefacil.com/api/v1/customers?phone=584140000000" \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI"
GET/customers/{id}

Un cliente.

Un cliente nace cuando escribe, al crear un pedido, una cita o un viaje con un customer_phone que no existía, o al darlo de alta con POST /customers (abajo). Para enterarte de los nuevos, de los cambios y de los borrados usa los webhooks customer.created, customer.updated (trae en changes qué cambió: name, phone, id_document, address, notes o bot_muted) y customer.deleted (trae el phone y el name que tenía; al borrar un cliente se borran también sus pedidos y citas).

Si tu sistema ya conoce a los clientes del negocio, no hace falta esperar a que el asistente les pida el nombre y la cédula: con el webhook customer.lookup, AtiendeFácil te pregunta por el teléfono de quien escribe y guarda lo que respondas antes de contestarle (ver Consultar un cliente en tu sistema).

Editar un cliente

PATCH/customers/{id}

Cambia los datos de un cliente, como la ficha del cliente en el panel. Manda solo lo que quieras cambiar; lo que no mandas se queda como está.

CampoQué es
nameNombre, de 1 a 160 caracteres. Poner un nombre lo marca como confirmado (name_confirmed: true) y el bot deja de preguntárselo. Con null se borra.
id_documentCédula o RIF. Se valida igual que en el panel (cédula V o E de 6 a 8 números, o RIF con su dígito verificador) y se guarda formateado: v12345678 queda V-12345678. Si no es válido: 400 invalid_id_document, con el motivo en el mensaje. Si mandas el mismo que ya tenía, no se revisa. Con null se borra.
addressDirección, hasta 500 caracteres. null lo borra.
notesNotas internas, hasta 2.000 caracteres. null las borra.
bot_mutedtrue: el bot deja de responderle a este cliente y lo atiende solo una persona. false: vuelve a responderle.
curl
curl -X PATCH https://api.atiendefacil.com/api/v1/customers/c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{ "name": "María Pérez", "id_document": "V00000000", "notes": "Cliente mayorista" }'
  • Responde 200 con el cliente ya actualizado.
  • El teléfono no se cambia por la API. Cualquier campo que no esté en la tabla responde 400 invalid_body, igual que un valor demasiado largo.
  • Sale customer.updated si de verdad cambió algo, y queda en la auditoría: «editó un cliente desde la API».

Dar de alta y borrar clientes

POST/customers

Da de alta un cliente por su teléfono, para cargar la cartera de tu sistema sin esperar a que cada uno escriba. Responde 201 con el cliente.

CampoObligatorioQué es
phoneSíCon o sin código de país: 04140000000 se guarda como 584140000000. Si no es un teléfono: 400 invalid_phone.
nameNoHasta 160 caracteres. Queda como nombre confirmado: el bot no se lo pregunta.
id_documentNoCédula o RIF, validado y formateado como en PATCH (V-00000000, J-00000000-0). Si no es válido: 400 invalid_id_document.
addressNoHasta 500 caracteres.
notesNoNotas internas, hasta 2000 caracteres.
curl
curl -X POST https://api.atiendefacil.com/api/v1/customers \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{ "phone": "04140000000", "name": "María Pérez", "id_document": "V00000000" }'
  • No duplica. Si ya hay un cliente con ese teléfono responde 200 (no 201) con el que existe. De lo que mandes solo le pone el name, si traes uno distinto; el documento, la dirección y las notas de un cliente que ya existe se cambian con PATCH /customers/{id}.
  • Darlo de alta no le escribe ni le abre una conversación: a un cliente solo se le puede mandar un mensaje cuando ya escribió él.
  • Sale customer.created (o customer.updated si ya existía y cambió el nombre), con data.source de tu clave.
  • Si lo que quieres es que el cliente quede identificado justo cuando escribe por primera vez, sin cargar toda la cartera, mira customer.lookup.
DELETE/customers/{id}

Borra el cliente y, con él, sus pedidos, sus citas y su conversación. No se puede deshacer. Responde { "id": "…", "object": "customer", "deleted": true } y sale customer.deleted. Si vuelve a escribir, nace como cliente nuevo.

Números bloqueados

A un número bloqueado el asistente no le contesta. Es la misma lista de Ajustes → Bloqueados del panel y la app. Un cambio en la lista se aplica en un minuto o menos.

PeticiónQué hace
GET /blocklistLos bloqueados, del más reciente al más viejo (hasta 500, sin paginar). Cada uno: { id, object: "blocked_number", phone, reason, created_at }.
POST /blocklistBloquea un número: phone y, opcional, reason (hasta 200 caracteres). Si ya estaba bloqueado, le cambia el motivo. Responde 201.
DELETE /blocklist/{id}Lo desbloquea. El id es el del bloqueo, no el del cliente.

Para que el asistente deje de contestarle a un cliente sin bloquear el número, usa bot_muted en PATCH /customers/{id}.

Conversaciones

Una conversación es el chat de un cliente con el negocio. Por la API se ve en qué está cada una (si el bot la atiende, si espera a una persona, si alguien la tomó), se lee su historial y se puede tomar o devolver al bot, igual que desde Chats en el panel y la app (ver Chats).

json
{
  "id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
  "object": "conversation",
  "state": "chatting",
  "needs_agent": true,
  "agent_reason": "¿Hacen tortas por encargo para 40 personas?",
  "needs_agent_since": "2026-09-29T18:41:02.300Z",
  "awaiting_delivery_quote": false,
  "taken_over": false,
  "taken_over_at": null,
  "archived": false,
  "customer": {
    "id": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
    "name": "María Pérez",
    "phone": "584140000000"
  },
  "last_message_at": "2026-09-29T18:41:01.870Z",
  "created_at": "2026-07-02T21:14:09.402Z",
  "updated_at": "2026-09-29T18:41:02.300Z"
}
CampoTipoQué es
iduuidId de la conversación. Es el conversation_id de pedidos y mensajes.
objecttextoSiempre conversation.
statetextoEn qué anda el bot con el cliente: chatting, taking_order, awaiting_payment, in_review, scheduling o closed. Es orientativo; para saber si hace falta alguien, mira los campos de abajo.
needs_agentbooleanoEl chat espera a una persona: el cliente pidió hablar con alguien o el bot no pudo seguir. Mientras tanto, el bot no le responde. En el panel sale como «Requiere agente».
agent_reasontexto | nullLo que preguntó el cliente o por qué se detuvo el bot.
needs_agent_sincefecha | nullDesde cuándo espera.
awaiting_delivery_quotebooleanoEspera que el negocio le ponga el costo del envío a su pedido (ver Cotizar el envío). No cuenta como needs_agent.
taken_overbooleanoAlguien tomó el chat (desde el teléfono del negocio o por la API): el bot no responde en él hasta que se devuelva.
taken_over_atfecha | nullCuándo se tomó.
archivedbooleanoEl negocio lo archivó.
customerobjetoResumen del cliente: { id, name, phone }.
last_message_atfecha | nullCuándo fue el último mensaje.
created_atfechaCuándo empezó.
updated_atfechaÚltimo cambio.

Listar y ver

GET/conversations

Del último mensaje más reciente al más viejo, paginado. Filtros opcionales:

ParámetroQué filtra
needs_agent=trueSolo las que esperan a una persona.
taken_over=trueSolo las tomadas a mano.
customer_idLas de un cliente. Si no es un uuid: 400 invalid_id.
qBusca por el nombre o el teléfono del cliente, sobre todas las conversaciones. El nombre, por cualquier trozo y sin distinguir mayúsculas; el teléfono se compara solo por sus dígitos, sin ceros a la izquierda, y hacen falta al menos 3 (q=0414-123 encuentra 58414123…). Hasta 80 caracteres. Se combina con los demás filtros y con page y limit.
updated_afterLas que cambiaron desde esa fecha, de la más vieja a la más nueva (ver sincronización incremental).
GET/conversations/{id}

Una conversación.

GET/conversations/{id}/messages

El historial del chat, del mensaje más nuevo al más viejo, paginado con page y limit. Cada mensaje tiene la forma de El mensaje: los del cliente, los del bot y los del equipo.

curl
# Los chats que esperan a una persona
curl "https://api.atiendefacil.com/api/v1/conversations?needs_agent=true" \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI"

# Los mensajes de uno, del más nuevo al más viejo
curl "https://api.atiendefacil.com/api/v1/conversations/e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b/messages?limit=50" \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI"

Tomar el chat y devolverlo al bot

POST/conversations/{id}/takeover

Toma el chat: el bot deja de responder en esa conversación (en las demás sigue igual) hasta que se devuelva. No caduca sola. Es lo mismo que pasa cuando alguien le escribe al cliente desde el teléfono del negocio. Si ya estaba tomado, no cambia nada. Sale conversation.taken_over.

POST/conversations/{id}/resume

Se lo devuelve al bot: levanta a la vez la espera de una persona y la toma a mano, como el botón Reanudar del panel. Sale conversation.bot_resumed. Si la conversación espera la cotización del envío responde 409 awaiting_delivery_quote: eso se resuelve con POST /orders/{id}/delivery-quote.

curl
# Tomar el chat: el bot se calla en esta conversación
curl -X POST https://api.atiendefacil.com/api/v1/conversations/e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b/takeover \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI"

# Devolvérselo al bot
curl -X POST https://api.atiendefacil.com/api/v1/conversations/e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b/resume \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI"

Las dos responden 200 con la conversación actualizada.

POST/conversations/{id}/read

La marca como leída: apaga el globo de mensajes sin leer en el panel y la app, como al abrir el chat. No cambia quién la atiende. Responde la conversación.

Cuándo espera a una persona

  • Cuando el cliente pide hablar con alguien o el bot no puede seguir, la conversación pasa a needs_agent: true y sale el webhook conversation.needs_agent, con el motivo en data.reason.
  • Se resuelve contestándole con POST /messages (eso ya levanta la espera y sale conversation.bot_resumed: el bot atiende lo siguiente que escriba el cliente) o con /resume. Si prefieres que el bot no se meta mientras hablas con el cliente, tómalo antes con /takeover.
  • Si nadie la atiende, la espera caduca sola: cuando el cliente vuelve a escribir pasadas 12 horas, el bot retoma y sale conversation.bot_resumed.

Enviar un mensaje

POST/messages

Le manda un texto a un cliente por el WhatsApp del negocio. Sale como mensaje del equipo, igual que si alguien lo escribiera desde el chat del panel, y queda en esa conversación a la vista de todos.

CampoObligatorioQué es
customer_idUno de los dosEl id del cliente.
phoneUno de los dosSu teléfono (se quedan solo los dígitos). Si mandas los dos, manda customer_id.
textSíEl mensaje, de 1 a 4.000 caracteres. Se le quitan los espacios de los extremos.
curl
curl -X POST https://api.atiendefacil.com/api/v1/messages \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{ "phone": "584140000000", "text": "¡Hola María! Tu pedido ya salió." }'

Responde 201 con el mensaje:

json
{
  "id": "f5a6b7c8-d9e0-4f1a-8b2c-3d4e5f6a7b8c",
  "object": "message",
  "conversation_id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
  "direction": "outbound",
  "sender": "agent",
  "type": "text",
  "text": "¡Hola María! Tu pedido ya salió.",
  "location": null,
  "file": null,
  "has_media": false,
  "customer": {
    "id": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
    "name": "María Pérez",
    "phone": "584140000000"
  },
  "created_at": "2026-09-29T18:20:31.004Z"
}

Las reglas

  • Ventana de 24 horas (solo canal oficial). Si el negocio está conectado por la API oficial de Meta, WhatsApp solo deja escribir libremente dentro de las 24 horas siguientes al último mensaje del cliente. Pasado eso, responde 409 outside_service_window. Conectado por código QR no hay ventana.
  • Solo texto. Las fotos, los archivos y las notas de voz van por POST /conversations/{id}/media y la ubicación por POST /conversations/{id}/location. Por esta ruta no salen plantillas de WhatsApp: para avisar a un cliente fuera de la ventana usa POST /whatsapp/send con template (ver Empezar con la API).
  • 30 mensajes por minuto por clave. No es una herramienta de envíos masivos.
  • Cada llamada manda un mensaje. Si la petición se corta sin respuesta, revisa el chat antes de repetirla o el cliente lo recibirá dos veces.

Errores

RespuestaCuándo
400 invalid_bodyFalta text, está vacío o pasa de 4.000 caracteres, o no hay ni customer_id ni phone.
404 no_conversationEse cliente nunca le escribió al negocio (o no existe).
409 outside_service_windowCanal oficial y más de 24 horas desde el último mensaje del cliente.
409 whatsapp_not_connectedEl WhatsApp del negocio no está conectado; el mensaje no salió.
402 meta_no_creditCanal oficial y el saldo no cubre el mensaje (10.000 tokens cada uno); no salió.
429Más de 30 mensajes en un minuto.

Enviar fotos, archivos y audio

POST/conversations/{id}/media

Manda una foto, un documento, una nota de voz o un vídeo en una conversación que ya existe, por el WhatsApp del negocio. Hace lo mismo que el botón de adjuntar del chat en el panel y la app: el archivo queda en el hilo, a la vista de todo el equipo.

CampoObligatorioQué es
kindSíimage, document, audio o video.
base64Uno de los dosEl archivo en base64, con o sin el prefijo data:…;base64,. Hasta 25 MB de archivo.
urlUno de los dosLa dirección https:// de donde bajar el archivo, en vez de mandarlo en base64. Hasta 25 MB. Ver «Enviar desde una URL».
mime_typeCon base64, síEl tipo del archivo: image/jpeg, application/pdf, audio/webm… Con url es opcional: si falta, vale el que declare tu servidor al entregarlo.
file_nameNoEl nombre con el que lo ve el cliente (en un documento es lo que sale en el chat). Hasta 200 caracteres.
captionNoEl texto que va al lado del archivo. Hasta 1.024 caracteres.
curl
# Una foto con texto al lado (base64 con o sin el prefijo data:)
curl -X POST https://api.atiendefacil.com/api/v1/conversations/e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b/media \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "image", "mime_type": "image/jpeg", "file_name": "factura-1042.jpg",
        "caption": "Aquí tu factura", "base64": "/9j/4AAQSkZJRgABAQ…" }'

# Una nota de voz grabada en el navegador (WebM/Opus): se convierte sola
curl -X POST https://api.atiendefacil.com/api/v1/conversations/e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b/media \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "audio", "mime_type": "audio/webm;codecs=opus", "base64": "GkXfo59ChoEBQveBAULygQRC84EIQoKE…" }'

Responde 201 con el mensaje ya guardado, con has_media: true:

json
{
  "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "object": "message",
  "conversation_id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
  "direction": "outbound",
  "sender": "agent",
  "type": "image",
  "text": "Aquí tu factura",
  "location": null,
  "file": { "name": "factura-1042.jpg", "mime_type": "image/jpeg" },
  "has_media": true,
  "customer": {
    "id": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
    "name": "María Pérez",
    "phone": "584140000000"
  },
  "created_at": "2026-09-29T18:22:05.118Z"
}

Enviar desde una URL

Si el archivo ya está publicado en tu sistema, manda url y AtiendeFácil lo baja en el momento:

curl
# Un PDF que ya está en tu sistema: se baja de la url y sale al chat
curl -X POST https://api.atiendefacil.com/api/v1/conversations/e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b/media   -H "Authorization: Bearer af_live_TU_CLAVE_AQUI"   -H "Content-Type: application/json"   -d '{ "kind": "document", "mime_type": "application/pdf", "file_name": "poliza-1042.pdf",
        "caption": "Aquí tu póliza", "url": "https://mi-sistema.com/polizas/1042.pdf?firma=…" }'
  • Tiene que ser https://, en un dominio público, y entregar el archivo directamente: no se siguen redirecciones ni se mandan cabeceras de acceso.
  • Se baja una sola vez, al recibir la petición, con 15 segundos de margen. Lo que sale al chat es una copia: puedes borrar el original después.
  • Si la dirección solo vale con un inicio de sesión, usa un enlace firmado que caduque o manda el archivo en base64.

Qué se hace con cada tipo

  • Fotos (image): en JPEG, PNG, WebP o GIF. Se dejan en JPEG o PNG y se achican si hace falta para que WhatsApp las acepte, como las que se mandan desde el panel. En file vuelve el tipo con el que salió. Otro formato de imagen mándalo como document.
  • Notas de voz (audio): WhatsApp solo las muestra como nota de voz en Ogg/Opus, así que todo lo demás se convierte antes de mandarlo: la grabación del navegador (WebM/Opus), M4A/AAC, MP3, WAV… Si el audio no se puede convertir, responde 415 audio_format_unsupported y no sale nada.
  • Documentos y vídeos (document, video): salen tal cual. El vídeo, en MP4.

Las reglas

  • Las mismas que POST /messages: solo en conversaciones que ya existen, ventana de 24 horas en el canal oficial, y 30 envíos por minuto por clave.
  • No toma el chat ni levanta la espera de una persona: si la conversación tenía needs_agent: true, sigue así (igual que al mandar un adjunto desde el panel). Contesta con texto por POST /messages o usa /resume para levantarla.
  • Si WhatsApp no acepta el archivo, el mensaje no se guarda: no queda en el hilo nada que el cliente no haya recibido.

Errores

RespuestaCuándo
400 invalid_bodyFalta kind o el archivo (base64 o url, uno de los dos); falta mime_type; el base64 o la url no son válidos o el archivo llegó vacío; o una imagen no es JPEG, PNG, WebP ni GIF.
404 not_foundLa conversación no existe o es de otro negocio.
409 outside_service_windowCanal oficial y más de 24 horas desde el último mensaje del cliente.
409 whatsapp_not_connectedEl WhatsApp del negocio no está conectado; no salió nada.
402 meta_no_creditCanal oficial y el saldo no cubre el mensaje (10.000 tokens cada uno); no salió nada.
413 file_too_largeEl archivo pasa de 25 MB.
415 audio_format_unsupportedEl audio no se pudo convertir a nota de voz.
415 format_unsupportedEl cliente escribe por Instagram, que solo acepta documentos en PDF.
422 file_not_fetchedNo se pudo bajar el archivo de la url: no respondió a tiempo, respondió con un error o con una redirección. El mensaje dice cuál.
502 media_not_sentWhatsApp no aceptó el archivo. Puedes reintentar en un momento.
429Más de 30 envíos en un minuto.

Enviar una ubicación

POST/conversations/{id}/location

Manda un punto del mapa en una conversación que ya existe, como el botón de ubicación del chat. La del negocio está en GET /me (location); puedes mandar esa u otra.

CampoObligatorioQué es
latSíLatitud, número entre -90 y 90.
lngSíLongitud, número entre -180 y 180.
nameNoEl nombre del sitio, hasta 200 caracteres.
addressNoLa dirección escrita, hasta 300 caracteres.
curl
# La ubicación del negocio (sale en GET /me → location)
curl -X POST https://api.atiendefacil.com/api/v1/conversations/e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b/location \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{ "lat": 10.4806, "lng": -66.9036, "name": "Pizzería La Esquina", "address": "Av. Bolívar, local 3, Caracas" }'

Responde 201 con el mensaje (type: "location", el punto en location y en text el nombre o, si no hay, la dirección). Mismas reglas que los adjuntos. Errores: 400 invalid_body (faltan las coordenadas o se salen de rango), 404 not_found, 409 outside_service_window, 409 whatsapp_not_connected, 402 meta_no_credit y 429.

Descargar archivos

GET/messages/{id}/media

El archivo de un mensaje: la foto, el documento o la nota de voz que mandó el cliente, o lo que mandó el negocio. Sirve para cualquier mensaje con has_media: true.

curl
# Bajar el archivo de un mensaje (del cliente o del negocio) a disco
curl https://api.atiendefacil.com/api/v1/messages/a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d/media \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -o factura-1042.jpg
  • Responde 200 con los bytes del archivo, no con JSON: Content-Type con su tipo, Content-Length y Content-Disposition: attachment con su nombre (si no tiene, uno armado con el id del mensaje y la extensión).
  • Nunca redirige: el archivo sale siempre de esta misma dirección, con tu clave en Authorization como el resto de la API.
  • Las notas de voz están en Ogg/Opus, que es como las manda WhatsApp.
  • 404 media_not_found: el mensaje no tiene archivo, o ya se borró. Los archivos de los chats se borran a los 2 días, como en el panel (ver Chats); el mensaje sigue en el hilo, con file, pero con has_media: false. Si te hace falta guardarlo, bájalo antes. 404 not_found: el mensaje no existe o es de otro negocio.

El mensaje

Es lo que devuelven POST /messages, POST /conversations/{id}/media y /location, y lo que traen los webhooks message.received (cada mensaje que escribe un cliente) y message.sent (cada mensaje que le manda el negocio: el asistente, el equipo o tu integración).

CampoTipoQué es
iduuidId del mensaje.
objecttextoSiempre message.
conversation_iduuidEl chat al que pertenece.
directiontextoinbound (lo escribió el cliente) u outbound (salió del negocio).
sendertextocustomer (el cliente), assistant (la IA), agent (alguien del equipo o tu integración) o system.
typetextotext, audio, image, location, video, document o sticker.
texttextoEl texto. En una foto o un documento, lo que escribió al lado (puede ir vacío).
locationobjeto | null{ lat, lng } si compartió una ubicación.
fileobjeto | null{ name, mime_type } si el mensaje lleva un archivo. Se queda aunque el archivo ya se haya borrado.
has_mediabooleanotrue si hay archivo que bajar con GET /messages/{id}/media. Pasa a false cuando el archivo se borra, a los 2 días.
customerobjetoResumen del cliente.
created_atfechaCuándo se guardó.

Para recibir lo que escriben los clientes al momento, usa el webhook message.received, y para lo que les contesta el negocio, message.sent (ver Webhooks). Para leer el historial de un chat, GET /conversations/{id}/messages (ver Conversaciones).

Preguntas frecuentes

¿El bot sigue respondiendo después de que escribo por la API?

Tu mensaje cuenta como una respuesta del equipo, igual que escribir desde el chat del panel: si el chat esperaba a una persona, deja de esperar y el bot atiende lo siguiente que escriba el cliente. Si quieres que el bot no se meta mientras hablas tú, toma el chat antes con POST /conversations/{id}/takeover y devuélveselo con /resume al terminar. Un cliente con bot_muted: true no recibe respuestas del bot mientras siga así.

¿Puedo escribirle a un cliente que hace una semana no escribe?

Conectado por QR, sí. Con la API oficial de Meta no: fuera de las 24 horas WhatsApp no lo permite y responde 409 outside_service_window.

¿No encontraste lo que buscabas?

Escríbenos y te responde una persona del equipo.