Documentación de la API
Bienvenido a la API de Atendo. Desde aquí puedes gestionar conversaciones de WhatsApp, enviar mensajes, cambiar estados, asignar agentes y más, todo de forma programática.
Todos los endpoints requieren autenticación mediante un token Bearer y el header X-Tenant-ID. Lee la sección de Autenticación antes de comenzar.
Autenticación
La API usa tokens Bearer (Laravel Sanctum). Para obtener un token, haz login con tus credenciales. El token devuelto tiene una expiración de 24 horas.
/auth/login
Parámetros
| Campo | Tipo | Descripción | |
|---|---|---|---|
| string | requerido | Correo electrónico de la cuenta | |
| password | string | requerido | Contraseña de la cuenta |
curl -X POST "https://atendo.com.do/api/v1/auth/login" \ -H "Content-Type: application/json" \ -d '{"email":"tu@email.com","password":"tuContraseña"}'
const res = await fetch("https://atendo.com.do/api/v1/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: "tu@email.com", password: "tuContraseña", }), }); const { token, tenant } = await res.json();
$ch = curl_init("https://atendo.com.do/api/v1/auth/login"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode([ "email" => "tu@email.com", "password" => "tuContraseña", ]), ]); $data = json_decode(curl_exec($ch), true); $token = $data["token"]; $tenantId = $data["tenant"]["id"];
{
"token": "138|abc123...",
"expires_at": "2026-08-11T14:00:00.000000Z",
"user": {
"id": 1,
"name": "Gabriel Martinez",
"email": "tu@email.com"
},
"tenant": {
"id": 1,
"name": "Mi Empresa"
}
}
Headers requeridos
Todos los endpoints (excepto login) requieren los siguientes headers en cada petición:
| Header | Valor | Descripción |
|---|---|---|
| Authorization | Bearer {token} | Token obtenido en login |
| X-Tenant-ID | integer | ID del tenant (campo tenant.id del login) |
| Content-Type | application/json | Requerido en peticiones POST y PATCH |
Authorization: Bearer 138|abc123... X-Tenant-ID: 1 Content-Type: application/json
Errores
La API devuelve errores estándar HTTP. El cuerpo siempre es JSON.
errors describe qué campo falló./whatsapp-channels/{channel_id}/conversations
Retorna la lista paginada de conversaciones de un canal de WhatsApp. Soporta filtrado por estado y búsqueda.
Query params
| Parámetro | Tipo | Descripción | |
|---|---|---|---|
| status | string | opcional | open (defecto), assigned, closed, archived |
| limit | integer | opcional | Resultados por página. Máximo 100, defecto 20. |
| search | string | opcional | Busca por nombre, teléfono o último mensaje. |
| cursor | string | opcional | Cursor para paginación. Usa el valor next_cursor de la respuesta anterior. |
curl "https://atendo.com.do/api/v1/whatsapp-channels/1/conversations?status=open&limit=20" \ -H "Authorization: Bearer {token}" \ -H "X-Tenant-ID: 1"
const res = await fetch( "https://atendo.com.do/api/v1/whatsapp-channels/1/conversations?status=open", { headers: { "Authorization": "Bearer {token}", "X-Tenant-ID": "1" } } ); const { data, next_cursor, has_more } = await res.json();
$ch = curl_init("https://atendo.com.do/api/v1/whatsapp-channels/1/conversations?status=open"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {token}", "X-Tenant-ID: 1", ], ]); $data = json_decode(curl_exec($ch), true);
{
"data": [
{
"id": 58,
"status": "open",
"contact_phone": "+18492107660",
"contact_wa_id": "18492107660",
"contact_name": "Juan Pérez",
"assigned_to_user_id": null,
"assigned_to_name": null,
"last_message_preview": "Hola, necesito ayuda",
"last_message_at": "2026-08-10T14:26:53.000000Z",
"unread_count": 3,
"labels": []
}
],
"next_cursor": "eyJpZCI6NTd9",
"has_more": true
}
/whatsapp-channels/{channel_id}/conversations/{id}
Retorna los detalles de una conversación junto con sus mensajes y actividades.
Query params
| Parámetro | Tipo | Descripción | |
|---|---|---|---|
| limit | integer | opcional | Mensajes por página. Máximo 100, defecto 50. |
| after_id | integer | opcional | Solo retorna mensajes con ID mayor a este valor (para polling en tiempo real). |
curl "https://atendo.com.do/api/v1/whatsapp-channels/1/conversations/58" \ -H "Authorization: Bearer {token}" \ -H "X-Tenant-ID: 1"
const res = await fetch( "https://atendo.com.do/api/v1/whatsapp-channels/1/conversations/58", { headers: { "Authorization": "Bearer {token}", "X-Tenant-ID": "1" } } ); const { conversation, messages, activities } = await res.json();
{
"conversation": { /* objeto completo de la conversación */ },
"messages": [
{
"id": 1204,
"direction": "inbound",
"type": "text",
"content": "Hola, necesito ayuda",
"sent_at": "2026-08-10T14:26:46.000000Z"
}
],
"activities": [],
"has_more": false
}
/whatsapp-channels/{channel_id}/conversations/{id}
Actualiza el estado, agente asignado, nombre del contacto o etiquetas de una conversación. Puedes enviar solo los campos que quieres cambiar.
Body (JSON)
| Campo | Tipo | Descripción | |
|---|---|---|---|
| status | string | opcional | open, assigned, closed, archived |
| assigned_to_user_id | integer|null | opcional | ID del agente. null para desasignar. |
| assigned_to_name | string|null | opcional | Nombre del agente (se muestra en el historial). |
| contact_name | string|null | opcional | Actualiza el nombre del contacto. |
| labels | array | opcional | Array de IDs de etiquetas. |
| actor_name | string|null | opcional | Nombre que aparece en el registro de actividad. |
# Cerrar conversación curl -X PATCH \ "https://atendo.com.do/api/v1/whatsapp-channels/1/conversations/58" \ -H "Authorization: Bearer {token}" \ -H "X-Tenant-ID: 1" \ -H "Content-Type: application/json" \ -d '{"status":"closed","actor_name":"Sistema"}'
# Asignar a un agente curl -X PATCH \ "https://atendo.com.do/api/v1/whatsapp-channels/1/conversations/58" \ -H "Authorization: Bearer {token}" \ -H "X-Tenant-ID: 1" \ -H "Content-Type: application/json" \ -d '{"assigned_to_user_id":3,"assigned_to_name":"Ana Gómez"}'
# Cambiar nombre del contacto curl -X PATCH \ "https://atendo.com.do/api/v1/whatsapp-channels/1/conversations/58" \ -H "Authorization: Bearer {token}" \ -H "X-Tenant-ID: 1" \ -H "Content-Type: application/json" \ -d '{"contact_name":"Juan Carlos Pérez"}'
/whatsapp-channels/{channel_id}/conversations/{id}/reply
Envía un mensaje de texto a la conversación. El mensaje se entrega al contacto de WhatsApp y queda registrado en el historial.
WhatsApp solo permite enviar mensajes libres dentro de la ventana de 24 horas desde el último mensaje del contacto. Fuera de esa ventana debes usar una plantilla aprobada (/send-template).
Body (JSON)
| Campo | Tipo | Descripción | |
|---|---|---|---|
| text | string | requerido | Texto del mensaje. Máximo 4096 caracteres. |
| reply_to_id | integer | opcional | ID del mensaje al que responder (crea una cita). |
curl -X POST \ "https://atendo.com.do/api/v1/whatsapp-channels/1/conversations/58/reply" \ -H "Authorization: Bearer {token}" \ -H "X-Tenant-ID: 1" \ -H "Content-Type: application/json" \ -d '{"text":"¡Hola! En un momento te atendemos."}'
await fetch( "https://atendo.com.do/api/v1/whatsapp-channels/1/conversations/58/reply", { method: "POST", headers: { "Authorization": "Bearer {token}", "X-Tenant-ID": "1", "Content-Type": "application/json", }, body: JSON.stringify({ text: "¡Hola! En un momento te atendemos." }), } );
$ch = curl_init("https://atendo.com.do/api/v1/whatsapp-channels/1/conversations/58/reply"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {token}", "X-Tenant-ID: 1", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["text" => "¡Hola! En un momento te atendemos."]), ]); $response = json_decode(curl_exec($ch), true);
{
"id": 1205,
"direction": "outbound",
"type": "text",
"content": "¡Hola! En un momento te atendemos.",
"status": "sent",
"sent_at": "2026-08-10T15:00:00.000000Z"
}
/whatsapp-channels/{channel_id}/conversations/start
Crea o reutiliza una conversación con un número de WhatsApp. Si ya existe una conversación con ese número, la retorna. Si no existe, la crea con estado open.
Body (JSON)
| Campo | Tipo | Descripción | |
|---|---|---|---|
| phone | string | requerido | Número de WhatsApp del contacto. Ej: 18092345678 o +1 (809) 234-5678. Se normaliza automáticamente. |
| name | string | opcional | Nombre del contacto. |
curl -X POST \ "https://atendo.com.do/api/v1/whatsapp-channels/1/conversations/start" \ -H "Authorization: Bearer {token}" \ -H "X-Tenant-ID: 1" \ -H "Content-Type: application/json" \ -d '{"phone":"18092345678","name":"María López"}'
const res = await fetch( "https://atendo.com.do/api/v1/whatsapp-channels/1/conversations/start", { method: "POST", headers: { "Authorization": "Bearer {token}", "X-Tenant-ID": "1", "Content-Type": "application/json", }, body: JSON.stringify({ phone: "18092345678", name: "María López" }), } ); const { conversation, is_new } = await res.json();
{
"conversation": {
"id": 72,
"status": "open",
"contact_phone": "+18092345678",
"contact_name": "María López"
},
"messages": [],
"is_new": true
}
/whatsapp-channels
Retorna los canales de WhatsApp configurados en tu cuenta. El id del canal es el {channel_id} que usarás en todos los demás endpoints.
curl "https://atendo.com.do/api/v1/whatsapp-channels" \ -H "Authorization: Bearer {token}" \ -H "X-Tenant-ID: 1"
[
{
"id": 1,
"name": "Soporte Principal",
"display_phone_number": "+1 809 850 9222",
"status": "active"
}
]