================================================================================ API WHATSAPP - INTEGRACIÓN SIMPLIFICADA Para Sistemas Externos Autónomos ================================================================================ VERSIÓN: 3.0 (Simplificada) FECHA: 3 de febrero de 2026 PROPÓSITO: Integración directa para IAs y sistemas externos Base URL: https://api.ardumaker.com/api/v1/whatsapp/ ================================================================================ AUTENTICACIÓN ================================================================================ Método: Header con SID de la Cuenta de Twilio Cada request incluye el SID de la sub-cuenta de Twilio como header: Header requerido: Twilio-Account-Sid: ACxxxxxxxxxxxxxxxxxx Este SID identifica y valida: ✓ A qué cuenta pertenece el número ✓ Permisos de acceso ✓ Validación en el backend remoto Obtención del SID: 1. Ve a https://ardumaker.com/dashboard?tab=whatsapp 2. Seleciona tu numero de whatsapp y vas a la parte de API 3. Guarda ese SID 4. Úsalo en TODOS los requests siguientes ================================================================================ ENDPOINT 1: ENVIAR MENSAJE ================================================================================ URL: POST /{id_numero}/send/ Headers: Twilio-Account-Sid: ACxxxxxxxxxxxxxxxxxx Content-Type: application/json OPCIÓN A: Mensaje de texto simple ────────────────────────────────── Body (JSON): { "to": "+573001112233", "body": "Hola! ¿Cómo estás?" } Respuesta (200): { "success": true, "data": { "id": 42, "header": 3, "direction": "out", "message_type": "text", "body": "Hola! ¿Cómo estás?", "status": "sent", "twilio_message_sid": "SMxxxxxxxxxxxxxxxx", "media_count": 0, "media_items": [], "created_at": "2026-02-03T10:00:00Z" } } OPCIÓN B: Mensaje con archivo ────────────────────────────── Body (multipart/form-data): to: +573001112233 body: Mira este archivo media: (uno o varios) Respuesta (200): { "success": true, "data": { "id": 43, "header": 3, "direction": "out", "message_type": "image", "body": "Mira este archivo", "status": "sent", "twilio_message_sid": "SMyyyyyyyyyyyyyyy", "media_count": 1, "media_items": [ { "file_name": "archivo.jpg", "content_type": "image/jpeg", "public_url": "https://api.ardumaker.com/api/v1/whatsapp/media/token123" } ], "created_at": "2026-02-03T10:05:00Z" } } Errores posibles: 400: Campo "to" requerido 401: Header Twilio-Account-Sid inválido o no proporcionado 404: Número no encontrado 500: Error en servidor ================================================================================ ENDPOINT 2: LISTAR CONVERSACIONES (HEDERS) ================================================================================ URL: GET /{id_numero}/heders/ Headers: Twilio-Account-Sid: ACxxxxxxxxxxxxxxxxxx Query params (opcionales): ?page=1&page_size=20 Respuesta (200): { "success": true, "data": { "page": 1, "page_size": 20, "total": 5, "results": [ { "id": 3, "number": 1, "contact_number": "+573009999999", "contact_name": "Juan Pérez", "last_message_at": "2026-02-03T10:00:00Z", "last_message_preview": "¿Cómo estás?", "is_active": true, "created_at": "2026-02-02T09:00:00Z", "updated_at": "2026-02-03T10:00:00Z" }, { "id": 4, "number": 1, "contact_number": "+573008888888", "contact_name": "María García", "last_message_at": "2026-02-03T09:30:00Z", "last_message_preview": "Gracias!", "is_active": true, "created_at": "2026-02-01T14:00:00Z", "updated_at": "2026-02-03T09:30:00Z" } ] } } Campos importantes: - id: Usar en /chats/ para ver mensajes - contact_number: Teléfono del contacto - contact_name: Nombre (puede ser null) - last_message_preview: Últimas palabras del mensaje - is_active: Si la conversación está activa Errores posibles: 401: Header Twilio-Account-Sid inválido 404: Número no encontrado ================================================================================ ENDPOINT 3: VER MENSAJES DE UNA CONVERSACIÓN (CHATS) ================================================================================ URL: GET /{id_numero}/{id_header}/chats/ Headers: Twilio-Account-Sid: ACxxxxxxxxxxxxxxxxxx Query params (opcionales): ?page=1&page_size=20 Respuesta (200): { "success": true, "data": { "header": { "id": 3, "number": 1, "contact_number": "+573009999999", "contact_name": "Juan Pérez", "last_message_at": "2026-02-03T10:00:00Z", "last_message_preview": "¿Cómo estás?", "is_active": true }, "page": 1, "page_size": 20, "total": 3, "results": [ { "id": 43, "header": 3, "direction": "in", "message_type": "text", "body": "¿Cómo estás?", "status": "received", "twilio_message_sid": "SMzzzzzzzzzzzzzzz", "media_count": 0, "media_items": [], "created_at": "2026-02-03T10:00:00Z" }, { "id": 42, "header": 3, "direction": "out", "message_type": "text", "body": "Hola! Bien, ¿y tú?", "status": "delivered", "twilio_message_sid": "SMyyyyyyyyyyyyyyy", "media_count": 0, "media_items": [], "created_at": "2026-02-03T09:55:00Z" }, { "id": 41, "header": 3, "direction": "in", "message_type": "image", "body": "", "status": "received", "twilio_message_sid": "SMxxxxxxxxxxxxxxxx", "media_count": 1, "media_items": [ { "file_name": "foto.jpg", "content_type": "image/jpeg", "public_url": "https://api.ardumaker.com/api/v1/whatsapp/media/token456" } ], "created_at": "2026-02-03T09:50:00Z" } ] } } Campos importantes por mensaje: - id: ID del mensaje en BD - direction: "in" (recibido) o "out" (enviado) - message_type: "text", "image", "video", "audio", "document", etc - body: Contenido del texto (vacío si es solo media) - status: "received", "sent", "delivered", "read", "failed" - twilio_message_sid: ID único en Twilio (usar para responder) - media_items: Array de archivos (si los hay) Orden: Los mensajes vienen del más nuevo al más viejo (descendente por fecha) Errores posibles: 401: Header Twilio-Account-Sid inválido 404: Número o conversación no encontrada ================================================================================ WEBHOOKS: MENSAJES ENTRANTES ================================================================================ Twilio envía mensajes a tu endpoint automáticamente cuando: 1. Un contacto envía un mensaje a tu número 2. Un contacto envía un archivo/media URL a configurar en Twilio: POST https://tu-servidor.com/webhooks/whatsapp Payload que Twilio ENVÍA a tu servidor (Form-encoded): ───────────────────────────────────────────────────── To: +573001112233 // Tu número (el que recibe) From: whatsapp:+573009999999 // Número del remitente Body: Hola! Tengo una pregunta // Texto del mensaje MessageSid: SMxxxxxxxxxxxxxxxx // ID único del mensaje en Twilio ProfileName: Juan Pérez // Nombre del contacto (si disponible) NumMedia: 1 // Cantidad de archivos MediaUrl0: https://api.twilio.com/... // URL del archivo 0 MediaContentType0: image/jpeg // Tipo del archivo 0 Cómo procesar en tu backend: ─────────────────────────── 1. Valida que el mensaje viene de Twilio - Valida la firma X-Twilio-Signature (si es posible) 2. Extrae los datos: from_number = From.replace("whatsapp:", "") // +573009999999 to_number = To // +573001112233 body = Body // Hola! Tengo una pregunta message_sid = MessageSid // SMxxxxxxxxxxxxxxxx 3. Normaliza números: - Formato E.164 (ej: +573001112233) - Sin prefijo "whatsapp:" 4. Crea un registro en tu BD: - Guarda mensaje entrante - Asocia con conversación/usuario - Marca como "recibido" 5. Procesa media si la hay: if NumMedia > 0: for i in range(int(NumMedia)): media_url = request.form.get(f"MediaUrl{i}") content_type = request.form.get(f"MediaContentType{i}") # Descarga/procesa el archivo 6. Responde a Twilio: Status 200 OK (cualquier contenido) Twilio solo verifica que recibió 200 Ejemplo en Python: ────────────────── @app.route('/webhooks/whatsapp', methods=['POST']) def whatsapp_webhook(): to_number = request.form.get('To') from_number = request.form.get('From').replace('whatsapp:', '') body = request.form.get('Body', '') message_sid = request.form.get('MessageSid') profile_name = request.form.get('ProfileName') # Procesar mensaje print(f"Mensaje de {from_number}: {body}") # Procesar media num_media = int(request.form.get('NumMedia', 0)) for i in range(num_media): media_url = request.form.get(f'MediaUrl{i}') content_type = request.form.get(f'MediaContentType{i}') print(f"Archivo {i}: {content_type}") # Descargar/guardar archivo # Responder a Twilio return ('OK', 200) ================================================================================ WEBHOOKS: CAMBIOS DE ESTADO ================================================================================ Twilio envía cambios de estado cuando el mensaje: - Fue entregado - Fue leído - Falló el envío URL a configurar en Twilio: POST https://tu-servidor.com/webhooks/whatsapp-status Payload que Twilio ENVÍA (Form-encoded): ────────────────────────────────────── MessageSid: SMxxxxxxxxxxxxxxxx // ID del mensaje MessageStatus: delivered // Nuevo estado Timestamp: 1706880000 // Timestamp Unix Estados posibles: - sent: Enviado a Twilio - delivered: Entregado al teléfono - read: Leído por el contacto - failed: Falló el envío Cómo procesar en tu backend: ─────────────────────────── 1. Extrae los datos: message_sid = request.form.get('MessageSid') status = request.form.get('MessageStatus') 2. Busca el mensaje en tu BD por message_sid 3. Actualiza el estado: mensaje.status = status mensaje.save() 4. Responde a Twilio: Status 200 OK Ejemplo en Python: ────────────────── @app.route('/webhooks/whatsapp-status', methods=['POST']) def whatsapp_status_webhook(): message_sid = request.form.get('MessageSid') status = request.form.get('MessageStatus') # Buscar mensaje y actualizar mensaje = Mensaje.objects.get(twilio_message_sid=message_sid) mensaje.status = status mensaje.save() return ('OK', 200) ================================================================================ FLUJO COMPLETO DE INTEGRACIÓN ================================================================================ 1. ENVIAR MENSAJE ──────────────── POST /5/send/ Headers: Twilio-Account-Sid: ACxxxx Body: { "to": "+573009999999", "body": "Hola!" } Respuesta: Mensaje creado, status="sent", twilio_message_sid=SMxxxx 2. CONTACTO RESPONDE ──────────────────── Twilio envía webhook a tu endpoint: To: +573001112233 From: whatsapp:+573009999999 Body: Hola! Cómo estás? MessageSid: SMyyyy Tu servidor procesa el webhook y guarda el mensaje 3. CONSULTAR CONVERSACIONES ──────────────────────────── GET /5/heders/ Headers: Twilio-Account-Sid: ACxxxx Respuesta: Array de conversaciones con id=3 4. VER MENSAJES DE LA CONVERSACIÓN ────────────────────────────────── GET /5/3/chats/ Headers: Twilio-Account-Sid: ACxxxx Respuesta: Array de mensajes (tu mensaje + respuesta del contacto) 5. RESPONDER AL CONTACTO ──────────────────────── POST /5/send/ Headers: Twilio-Account-Sid: ACxxxx Body: { "to": "+573009999999", "body": "Bien, ¿y tú?", "in_reply_to": "SMyyyy" } Respuesta: Nuevo mensaje creado 6. TWILIO CONFIRMA ENTREGA ────────────────────────── Twilio envía webhook de estado: MessageSid: SMzzzz (el que acabas de enviar) MessageStatus: delivered Tu servidor actualiza el estado del mensaje ================================================================================ SEGURIDAD: VALIDACIÓN DEL SID ================================================================================ El header Twilio-Account-Sid sirve para: 1. Identificar la cuenta - Cada sub-cuenta tiene un SID único - ACxxxxxxxxxxxxxxxx es el identificador 2. Validar permisos - Backend verifica que el SID pertenece a esa compañía - Se rechaza si el SID no es válido 3. Aislar datos - Cada SID solo puede ver sus propios números - Cada SID solo puede acceder a sus propias conversaciones 4. En tu backend remoto: - Guarda el SID cuando configures la integración - Valida que el SID sea el esperado en cada request - Rechaza requests con SID diferente - Esto te protege contra accesos no autorizados Ejemplo de validación en tu backend: ──────────────────────────────────── EXPECTED_SID = "ACxxxxxxxxxxxxxxxxxx" // Guarda esto al configurar @app.route('/webhook', methods=['POST']) def process_webhook(): sid = request.headers.get('Twilio-Account-Sid') if sid != EXPECTED_SID: return ('Unauthorized', 401) # Procesar el webhook ... ================================================================================ CÓDIGOS DE ERROR ================================================================================ 200 OK ✓ Operación exitosa 201 CREATED ✓ Recurso creado 400 BAD REQUEST ✗ Datos inválidos Causa común: Campo "to" requerido pero falta 401 UNAUTHORIZED ✗ Header Twilio-Account-Sid inválido o no proporcionado Solución: Verifica que enviaste el header correcto 404 NOT FOUND ✗ Recurso no existe Causa: id_numero o id_header no existe 500 INTERNAL SERVER ERROR ✗ Error en el servidor Solución: Intenta más tarde ================================================================================ TIPOS DE MENSAJE (message_type) ================================================================================ text → Mensaje de texto simple image → Imagen (JPEG, PNG, GIF) video → Video (MP4, 3GP) audio → Audio (MP3, WAV, AAC) voice → Mensaje de voz (OGG) document → Documento (PDF, Word, etc) unknown → Tipo desconocido ================================================================================ ESTADOS DE MENSAJE (status) ================================================================================ received → Recibido del contacto sent → Enviado a Twilio (aún no confirmado) delivered → Entregado al teléfono del contacto read → Leído por el contacto failed → Falló el envío (no se puede recuperar) ================================================================================ ARCHIVOS MULTIMEDIA ================================================================================ Dónde se guardan: Backend descarga/procesa los archivos automáticamente Se guardan en: /media/whatsapp//// Cómo acceder: Cada media_item tiene "public_url" Ej: https://api.ardumaker.com/api/v1/whatsapp/media/token123 Límites: - Máximo 16 MB por archivo (límite de Twilio) - Máximo 10 archivos recomendado por mensaje - Formatos: JPEG, PNG, GIF, MP4, 3GP, MP3, WAV, PDF, etc ================================================================================ RESUMEN PARA IMPLEMENTACIÓN RÁPIDA ================================================================================ 1. Obtén el Twilio-Account-Sid (una sola vez) 2. Usa ese SID en TODOS los requests como header 3. Implementa 3 endpoints: - POST /{id}/send/ → Enviar mensaje - GET /{id}/heders/ → Ver conversaciones - GET /{id}/{header_id}/chats/ → Ver mensajes 4. Configura webhooks en Twilio para recibir automáticamente 5. Procesa los webhooks en tu servidor 6. Valida el SID en cada request ¡Eso es todo lo que necesitas! ================================================================================