{"openapi":"3.1.0","info":{"title":"whatsapi.sh — la forma amigable de usar la API de WhatsApp","version":"1.0.0","description":"Endpoints REST directos sobre tus propios números de WhatsApp vinculados. Cada operación de aquí también es una tool MCP (ve x-mcp-tools), autenticada por la misma clave `wa_`. ¿Escribes TypeScript o JavaScript? `npm i @whatsapi.sh/sdk` te da un método tipado por cada endpoint de aquí."},"servers":[{"url":"http://127.0.0.1:3334"}],"tags":[{"name":"Numbers","description":"Agrega un número a tu cuenta, vincúlalo por código QR o código de vinculación y sigue el estado de la conexión."},{"name":"Messages","description":"Texto, multimedia, ubicaciones, tarjetas de contacto — a un chat o en lote, con indicador de escritura y acuses de lectura."},{"name":"Queue","description":"Mensajes retenidos para que las conversaciones que un número inicia salgan con minutos de diferencia, y no todas de golpe — mira qué está en cola o cambia de idea sobre alguno."},{"name":"OTP","description":"Entrega un código de un solo uso y comprueba lo que el usuario escribió — el código vuelve en la respuesta."},{"name":"Webhooks","description":"Un endpoint de eventos firmados por cuenta: configúralo, envía una prueba, rota su secreto, consulta su registro de entregas."},{"name":"Usage","description":"Envíos medidos en el mes de facturación, divididos por clave de API."}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Una clave de API `wa_` del panel."}}},"paths":{"/messages":{"post":{"operationId":"send_message_post","summary":"Envía un mensaje por WhatsApp","tags":["Messages"],"description":"Envía un mensaje de texto a un número por WhatsApp. Consume un mensaje de la cuota mensual de la cuenta.\n\n**Un mensaje a alguien a quien este número nunca le escribió no sale de inmediato.** Se acepta, se cobra y ENTRA EN COLA: la respuesta trae `status: \"queued\"` y `scheduledAt`, y sale 15–25 minutos después, de a uno. Es a propósito. Una ráfaga de mensajes en frío es la forma más rápida de que baneen un número de WhatsApp, y espaciarlos es lo que separa un número que dura de uno que no. `message.sent` llega al webhook de la cuenta cuando sale, con el id de WhatsApp. Usa `skipQueue: true` para un mensaje que la persona está esperando ahora mismo, o desactiva la cola del número con update_number.\n\nResponder a quien escribió primero nunca entra en cola ni gasta límite. Un mensaje en frío también gasta una de las CONVERSACIONES NUEVAS del día — el techo anti-baneo que se informa en `newConversationsRemaining`. Una respuesta `sent` trae el id de mensaje de WhatsApp; los acuses de entrega y de lectura llegan después, en el webhook de la cuenta, no aquí.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."},"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"text":{"type":"string","minLength":1,"maxLength":4096,"description":"El cuerpo del mensaje."},"linkPreview":{"description":"Muestra una tarjeta de vista previa del primer link del texto. Por defecto: activado.","type":"boolean"},"skipQueue":{"description":"Envía al instante, en vez de tomar un lugar en la cola del número. Solo para un mensaje que la persona está esperando en este momento — una respuesta, un código, la confirmación de algo que acaba de hacer. La prospección enviada así es justo lo que hace que WhatsApp banee números, que es la razón por la que existe la cola. Por defecto: desactivado.","type":"boolean"},"humanize":{"description":"Si envía como una persona: conectarse, abrir lo último que te escribió y mostrar “escribiendo…” durante lo que tardaría en escribirse el mensaje. Activado por defecto, lo que suma unos segundos a la llamada — `false` lo envía directo y al instante. Configura cada parte por número con update_number.","type":"boolean"}},"required":["to","text"],"additionalProperties":false}}}},"responses":{"201":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]},"example":{"data":{"id":"9c1f0b7e-8b1a-4a2e-9b0a-1f2e3d4c5b6a","to":"+5511988887777","from":"a1b2c3d4-0000-0000-0000-000000000000","status":"queued","scheduledAt":"2026-08-24T19:18:00.000Z","timestamp":"2026-08-24T19:00:00.000Z","quotaRemaining":4999,"newConversationsRemaining":19,"creditRemaining":0}}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/messages/media":{"post":{"operationId":"send_media_post","summary":"Envía una foto, video, nota de voz o archivo","tags":["Messages"],"description":"Envía multimedia por WhatsApp desde una URL pública o datos en base64. El `type` elige la burbuja que ve el destinatario: `image` y `video` aceptan descripción, `document` es un adjunto nombrado por `fileName`, y `audio` se convierte en nota de voz cuando el archivo es ogg/opus — cualquier otro formato de audio va como adjunto reproducible, porque WhatsApp no renderiza una nota de voz que no sea opus. Los archivos tienen un tope de 16MB, y para cualquier cosa grande `url` le gana a `base64`. Consume un mensaje.\n\nComo todo envío: el multimedia para alguien a quien este número nunca le escribió ENTRA EN COLA en vez de salir, y vuelve con `status: \"queued\"` y `scheduledAt`. El archivo se descarga cuando llega su turno, así que la `url` tiene que seguir accesible para entonces.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."},"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"type":{"type":"string","enum":["image","video","audio","document"],"description":"Qué tipo de burbuja de multimedia enviar."},"url":{"description":"URL https pública del archivo — la mejor opción. Descargamos en streaming y reenviamos los bytes, así que el tamaño del archivo nunca viaja en tu solicitud.","type":"string","format":"uri"},"base64":{"description":"El archivo en base64, crudo o como URI `data:<mime>;base64,…`. Base64 infla la solicitud un tercio, así que prefiere `url` para cualquier cosa que no sean archivos pequeños.","type":"string","minLength":1},"mimeType":{"description":"Sobrescribe el content type detectado, p. ej. `image/webp`.","type":"string","minLength":3},"caption":{"description":"Texto mostrado bajo la multimedia. Solo imágenes y videos — WhatsApp no tiene campo de descripción para documentos ni audio.","type":"string","maxLength":1024},"fileName":{"description":"Documentos: el nombre de archivo que WhatsApp muestra en la burbuja.","type":"string","minLength":1,"maxLength":255},"seconds":{"description":"Notas de voz: la duración, para que la burbuja muestre el largo correcto.","type":"integer","exclusiveMinimum":0,"maximum":600},"skipQueue":{"description":"Envía al instante, en vez de tomar un lugar en la cola del número. Solo para un mensaje que la persona está esperando en este momento — una respuesta, un código, la confirmación de algo que acaba de hacer. La prospección enviada así es justo lo que hace que WhatsApp banee números, que es la razón por la que existe la cola. Por defecto: desactivado.","type":"boolean"},"humanize":{"description":"Si envía como una persona: conectarse, abrir lo último que te escribió y mostrar “escribiendo…” durante lo que tardaría en escribirse el mensaje. Activado por defecto, lo que suma unos segundos a la llamada — `false` lo envía directo y al instante. Configura cada parte por número con update_number.","type":"boolean"}},"required":["to","type"],"additionalProperties":false}}}},"responses":{"201":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/messages/image":{"post":{"operationId":"send_media_post","summary":"Envía una foto, video, nota de voz o archivo","tags":["Messages"],"description":"Envía multimedia por WhatsApp desde una URL pública o datos en base64. El `type` elige la burbuja que ve el destinatario: `image` y `video` aceptan descripción, `document` es un adjunto nombrado por `fileName`, y `audio` se convierte en nota de voz cuando el archivo es ogg/opus — cualquier otro formato de audio va como adjunto reproducible, porque WhatsApp no renderiza una nota de voz que no sea opus. Los archivos tienen un tope de 16MB, y para cualquier cosa grande `url` le gana a `base64`. Consume un mensaje.\n\nComo todo envío: el multimedia para alguien a quien este número nunca le escribió ENTRA EN COLA en vez de salir, y vuelve con `status: \"queued\"` y `scheduledAt`. El archivo se descarga cuando llega su turno, así que la `url` tiene que seguir accesible para entonces.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."},"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"url":{"description":"URL https pública del archivo — la mejor opción. Descargamos en streaming y reenviamos los bytes, así que el tamaño del archivo nunca viaja en tu solicitud.","type":"string","format":"uri"},"base64":{"description":"El archivo en base64, crudo o como URI `data:<mime>;base64,…`. Base64 infla la solicitud un tercio, así que prefiere `url` para cualquier cosa que no sean archivos pequeños.","type":"string","minLength":1},"mimeType":{"description":"Sobrescribe el content type detectado, p. ej. `image/webp`.","type":"string","minLength":3},"caption":{"description":"Texto mostrado bajo la multimedia. Solo imágenes y videos — WhatsApp no tiene campo de descripción para documentos ni audio.","type":"string","maxLength":1024},"fileName":{"description":"Documentos: el nombre de archivo que WhatsApp muestra en la burbuja.","type":"string","minLength":1,"maxLength":255},"seconds":{"description":"Notas de voz: la duración, para que la burbuja muestre el largo correcto.","type":"integer","exclusiveMinimum":0,"maximum":600},"skipQueue":{"description":"Envía al instante, en vez de tomar un lugar en la cola del número. Solo para un mensaje que la persona está esperando en este momento — una respuesta, un código, la confirmación de algo que acaba de hacer. La prospección enviada así es justo lo que hace que WhatsApp banee números, que es la razón por la que existe la cola. Por defecto: desactivado.","type":"boolean"},"humanize":{"description":"Si envía como una persona: conectarse, abrir lo último que te escribió y mostrar “escribiendo…” durante lo que tardaría en escribirse el mensaje. Activado por defecto, lo que suma unos segundos a la llamada — `false` lo envía directo y al instante. Configura cada parte por número con update_number.","type":"boolean"}},"required":["to"],"additionalProperties":false}}}},"responses":{"201":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/messages/video":{"post":{"operationId":"send_media_post","summary":"Envía una foto, video, nota de voz o archivo","tags":["Messages"],"description":"Envía multimedia por WhatsApp desde una URL pública o datos en base64. El `type` elige la burbuja que ve el destinatario: `image` y `video` aceptan descripción, `document` es un adjunto nombrado por `fileName`, y `audio` se convierte en nota de voz cuando el archivo es ogg/opus — cualquier otro formato de audio va como adjunto reproducible, porque WhatsApp no renderiza una nota de voz que no sea opus. Los archivos tienen un tope de 16MB, y para cualquier cosa grande `url` le gana a `base64`. Consume un mensaje.\n\nComo todo envío: el multimedia para alguien a quien este número nunca le escribió ENTRA EN COLA en vez de salir, y vuelve con `status: \"queued\"` y `scheduledAt`. El archivo se descarga cuando llega su turno, así que la `url` tiene que seguir accesible para entonces.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."},"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"url":{"description":"URL https pública del archivo — la mejor opción. Descargamos en streaming y reenviamos los bytes, así que el tamaño del archivo nunca viaja en tu solicitud.","type":"string","format":"uri"},"base64":{"description":"El archivo en base64, crudo o como URI `data:<mime>;base64,…`. Base64 infla la solicitud un tercio, así que prefiere `url` para cualquier cosa que no sean archivos pequeños.","type":"string","minLength":1},"mimeType":{"description":"Sobrescribe el content type detectado, p. ej. `image/webp`.","type":"string","minLength":3},"caption":{"description":"Texto mostrado bajo la multimedia. Solo imágenes y videos — WhatsApp no tiene campo de descripción para documentos ni audio.","type":"string","maxLength":1024},"fileName":{"description":"Documentos: el nombre de archivo que WhatsApp muestra en la burbuja.","type":"string","minLength":1,"maxLength":255},"seconds":{"description":"Notas de voz: la duración, para que la burbuja muestre el largo correcto.","type":"integer","exclusiveMinimum":0,"maximum":600},"skipQueue":{"description":"Envía al instante, en vez de tomar un lugar en la cola del número. Solo para un mensaje que la persona está esperando en este momento — una respuesta, un código, la confirmación de algo que acaba de hacer. La prospección enviada así es justo lo que hace que WhatsApp banee números, que es la razón por la que existe la cola. Por defecto: desactivado.","type":"boolean"},"humanize":{"description":"Si envía como una persona: conectarse, abrir lo último que te escribió y mostrar “escribiendo…” durante lo que tardaría en escribirse el mensaje. Activado por defecto, lo que suma unos segundos a la llamada — `false` lo envía directo y al instante. Configura cada parte por número con update_number.","type":"boolean"}},"required":["to"],"additionalProperties":false}}}},"responses":{"201":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/messages/audio":{"post":{"operationId":"send_media_post","summary":"Envía una foto, video, nota de voz o archivo","tags":["Messages"],"description":"Envía multimedia por WhatsApp desde una URL pública o datos en base64. El `type` elige la burbuja que ve el destinatario: `image` y `video` aceptan descripción, `document` es un adjunto nombrado por `fileName`, y `audio` se convierte en nota de voz cuando el archivo es ogg/opus — cualquier otro formato de audio va como adjunto reproducible, porque WhatsApp no renderiza una nota de voz que no sea opus. Los archivos tienen un tope de 16MB, y para cualquier cosa grande `url` le gana a `base64`. Consume un mensaje.\n\nComo todo envío: el multimedia para alguien a quien este número nunca le escribió ENTRA EN COLA en vez de salir, y vuelve con `status: \"queued\"` y `scheduledAt`. El archivo se descarga cuando llega su turno, así que la `url` tiene que seguir accesible para entonces.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."},"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"url":{"description":"URL https pública del archivo — la mejor opción. Descargamos en streaming y reenviamos los bytes, así que el tamaño del archivo nunca viaja en tu solicitud.","type":"string","format":"uri"},"base64":{"description":"El archivo en base64, crudo o como URI `data:<mime>;base64,…`. Base64 infla la solicitud un tercio, así que prefiere `url` para cualquier cosa que no sean archivos pequeños.","type":"string","minLength":1},"mimeType":{"description":"Sobrescribe el content type detectado, p. ej. `image/webp`.","type":"string","minLength":3},"caption":{"description":"Texto mostrado bajo la multimedia. Solo imágenes y videos — WhatsApp no tiene campo de descripción para documentos ni audio.","type":"string","maxLength":1024},"fileName":{"description":"Documentos: el nombre de archivo que WhatsApp muestra en la burbuja.","type":"string","minLength":1,"maxLength":255},"seconds":{"description":"Notas de voz: la duración, para que la burbuja muestre el largo correcto.","type":"integer","exclusiveMinimum":0,"maximum":600},"skipQueue":{"description":"Envía al instante, en vez de tomar un lugar en la cola del número. Solo para un mensaje que la persona está esperando en este momento — una respuesta, un código, la confirmación de algo que acaba de hacer. La prospección enviada así es justo lo que hace que WhatsApp banee números, que es la razón por la que existe la cola. Por defecto: desactivado.","type":"boolean"},"humanize":{"description":"Si envía como una persona: conectarse, abrir lo último que te escribió y mostrar “escribiendo…” durante lo que tardaría en escribirse el mensaje. Activado por defecto, lo que suma unos segundos a la llamada — `false` lo envía directo y al instante. Configura cada parte por número con update_number.","type":"boolean"}},"required":["to"],"additionalProperties":false}}}},"responses":{"201":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/messages/document":{"post":{"operationId":"send_media_post","summary":"Envía una foto, video, nota de voz o archivo","tags":["Messages"],"description":"Envía multimedia por WhatsApp desde una URL pública o datos en base64. El `type` elige la burbuja que ve el destinatario: `image` y `video` aceptan descripción, `document` es un adjunto nombrado por `fileName`, y `audio` se convierte en nota de voz cuando el archivo es ogg/opus — cualquier otro formato de audio va como adjunto reproducible, porque WhatsApp no renderiza una nota de voz que no sea opus. Los archivos tienen un tope de 16MB, y para cualquier cosa grande `url` le gana a `base64`. Consume un mensaje.\n\nComo todo envío: el multimedia para alguien a quien este número nunca le escribió ENTRA EN COLA en vez de salir, y vuelve con `status: \"queued\"` y `scheduledAt`. El archivo se descarga cuando llega su turno, así que la `url` tiene que seguir accesible para entonces.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."},"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"url":{"description":"URL https pública del archivo — la mejor opción. Descargamos en streaming y reenviamos los bytes, así que el tamaño del archivo nunca viaja en tu solicitud.","type":"string","format":"uri"},"base64":{"description":"El archivo en base64, crudo o como URI `data:<mime>;base64,…`. Base64 infla la solicitud un tercio, así que prefiere `url` para cualquier cosa que no sean archivos pequeños.","type":"string","minLength":1},"mimeType":{"description":"Sobrescribe el content type detectado, p. ej. `image/webp`.","type":"string","minLength":3},"caption":{"description":"Texto mostrado bajo la multimedia. Solo imágenes y videos — WhatsApp no tiene campo de descripción para documentos ni audio.","type":"string","maxLength":1024},"fileName":{"description":"Documentos: el nombre de archivo que WhatsApp muestra en la burbuja.","type":"string","minLength":1,"maxLength":255},"seconds":{"description":"Notas de voz: la duración, para que la burbuja muestre el largo correcto.","type":"integer","exclusiveMinimum":0,"maximum":600},"skipQueue":{"description":"Envía al instante, en vez de tomar un lugar en la cola del número. Solo para un mensaje que la persona está esperando en este momento — una respuesta, un código, la confirmación de algo que acaba de hacer. La prospección enviada así es justo lo que hace que WhatsApp banee números, que es la razón por la que existe la cola. Por defecto: desactivado.","type":"boolean"},"humanize":{"description":"Si envía como una persona: conectarse, abrir lo último que te escribió y mostrar “escribiendo…” durante lo que tardaría en escribirse el mensaje. Activado por defecto, lo que suma unos segundos a la llamada — `false` lo envía directo y al instante. Configura cada parte por número con update_number.","type":"boolean"}},"required":["to"],"additionalProperties":false}}}},"responses":{"201":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/messages/batch":{"post":{"operationId":"send_batch_post","summary":"Envía un texto a varios destinatarios","tags":["Messages"],"description":"Envía el mismo texto a hasta 25 destinatarios en una llamada — un mensaje, y un mensaje de cuota, por destinatario. La respuesta informa cada destinatario por separado: un lote donde unos llegan y otros no es el caso normal, no un error. Si algo sistémico corta la tanda (el número pierde la sesión, la cuota o el límite de tasa se niegan), los destinatarios restantes vuelven como `skipped` en vez de intentarse. Envíalos de nuevo cuando eso se resuelva.\n\n**Mientras el número espacia sus envíos, que es lo predeterminado, un lote no envía: encola.** Cada destinatario vuelve como `queued` con su propio `scheduledAt`. Los mensajes salen de a uno: con minutos de diferencia para quien el número nunca le escribió, con segundos para quien ya conversa con él. Veinticinco mensajes llegando en un segundo es justo el patrón por el que WhatsApp restringe números, así que el endpoint ya no hace eso. Sigue `message.sent` en el webhook de la cuenta para los ids, `GET /v1/queue` para lo que sigue esperando, y desactiva el espaciado del número si de verdad los quieres todos de golpe. Solo en planes pagos.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"minItems":1,"maxItems":25,"type":"array","items":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."},"description":"Destinatarios, hasta 25. Cada uno recibe su propio mensaje; sin duplicados."},"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"text":{"type":"string","minLength":1,"maxLength":4096,"description":"El cuerpo del mensaje — el mismo para todos."},"skipQueue":{"description":"Envía al instante, en vez de tomar un lugar en la cola del número. Solo para un mensaje que la persona está esperando en este momento — una respuesta, un código, la confirmación de algo que acaba de hacer. La prospección enviada así es justo lo que hace que WhatsApp banee números, que es la razón por la que existe la cola. Por defecto: desactivado.","type":"boolean"},"humanize":{"description":"Si envía como una persona: conectarse, abrir lo último que te escribió y mostrar “escribiendo…” durante lo que tardaría en escribirse el mensaje. Activado por defecto, lo que suma unos segundos a la llamada — `false` lo envía directo y al instante. Configura cada parte por número con update_number.","type":"boolean"}},"required":["to","text"],"additionalProperties":false}}}},"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/messages/location":{"post":{"operationId":"send_location_post","summary":"Envía una ubicación","tags":["Messages"],"description":"Envía una ubicación por WhatsApp. Consume un mensaje y — como todo envío — entra en cola en vez de salir cuando abre una conversación que este número nunca tuvo.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."},"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"latitude":{"type":"number","minimum":-90,"maximum":90},"longitude":{"type":"number","minimum":-180,"maximum":180},"name":{"description":"Etiqueta mostrada en el pin.","type":"string","maxLength":200},"skipQueue":{"description":"Envía al instante, en vez de tomar un lugar en la cola del número. Solo para un mensaje que la persona está esperando en este momento — una respuesta, un código, la confirmación de algo que acaba de hacer. La prospección enviada así es justo lo que hace que WhatsApp banee números, que es la razón por la que existe la cola. Por defecto: desactivado.","type":"boolean"},"humanize":{"description":"Si envía como una persona: conectarse, abrir lo último que te escribió y mostrar “escribiendo…” durante lo que tardaría en escribirse el mensaje. Activado por defecto, lo que suma unos segundos a la llamada — `false` lo envía directo y al instante. Configura cada parte por número con update_number.","type":"boolean"}},"required":["to","latitude","longitude"],"additionalProperties":false}}}},"responses":{"201":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/messages/contact":{"post":{"operationId":"send_contact_post","summary":"Envía una tarjeta de contacto","tags":["Messages"],"description":"Envía una tarjeta de contacto (vCard) por WhatsApp, para que el destinatario guarde el número con un toque. Consume un mensaje y — como todo envío — entra en cola en vez de salir cuando abre una conversación que este número nunca tuvo.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."},"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Nombre visible en la tarjeta."},"vcard":{"type":"string","minLength":1,"description":"La carga útil del vCard, p. ej. `BEGIN:VCARD\\nVERSION:3.0\\n…\\nEND:VCARD`."},"skipQueue":{"description":"Envía al instante, en vez de tomar un lugar en la cola del número. Solo para un mensaje que la persona está esperando en este momento — una respuesta, un código, la confirmación de algo que acaba de hacer. La prospección enviada así es justo lo que hace que WhatsApp banee números, que es la razón por la que existe la cola. Por defecto: desactivado.","type":"boolean"},"humanize":{"description":"Si envía como una persona: conectarse, abrir lo último que te escribió y mostrar “escribiendo…” durante lo que tardaría en escribirse el mensaje. Activado por defecto, lo que suma unos segundos a la llamada — `false` lo envía directo y al instante. Configura cada parte por número con update_number.","type":"boolean"}},"required":["to","name","vcard"],"additionalProperties":false}}}},"responses":{"201":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/otp":{"post":{"operationId":"send_otp_post","summary":"Envía un código de verificación","tags":["OTP"],"description":"Genera un código de 6 dígitos, lo envía por WhatsApp y te lo devuelve. Devolverlo es deliberado: compruébalo tú, o entrégaselo a check_otp y no guardes nada. Los códigos expiran en 5 minutos por defecto y a un número solo se le puede mandar un código nuevo una vez por minuto. Consume un mensaje.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."},"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"template":{"description":"Cuerpo del mensaje. Debe contener `{code}`; `{minutes}` se expande al plazo de validez. Por defecto: “{code} is your verification code. It expires in {minutes} minutes.”","type":"string","minLength":1,"maxLength":1000},"expiresInSeconds":{"description":"Cuánto tiempo vale el código. Por defecto 300 (5 minutos).","type":"integer","minimum":60,"maximum":3600}},"required":["to"],"additionalProperties":false}}}},"responses":{"201":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/otp/check":{"post":{"operationId":"check_otp_post","summary":"Comprueba un código de verificación","tags":["OTP"],"description":"Verifica un código enviado con send_otp y lo consume, para que nunca sirva dos veces. Un código incorrecto o expirado responde 400 OTP_INVALID — un error a propósito, no un `verified: false`, para que una comprobación faltante en quien llama nunca pueda leerse como éxito. Cinco intentos errados matan el código. Gratis.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."},"code":{"type":"string","minLength":4,"maxLength":10,"description":"El código que escribió el usuario."}},"required":["to","code"],"additionalProperties":false}}}},"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/numbers":{"get":{"operationId":"list_numbers_get","summary":"Lista tus números de WhatsApp","tags":["Numbers"],"description":"Todos los números de WhatsApp de la cuenta con su estado de conexión. Empieza aquí cuando no sepas desde qué número enviar, o para ver si alguno necesita volver a vincularse. Gratis.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}},"post":{"operationId":"create_number_post","summary":"Agrega un número de WhatsApp","tags":["Numbers"],"description":"Crea un nuevo slot de número de WhatsApp en la cuenta y lo devuelve. Es el paso uno de dos: el slot empieza `disconnected` y no puede enviar hasta que pair_number vincule una cuenta real de WhatsApp. Cuántos pueden existir a la vez es lo que vende el plan. Gratis.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"description":"Una etiqueta que eliges tú, p. ej. “Soporte” — solo tú la ves."},"historySync":{"description":"Trae el historial de chats existente del teléfono al vincular. Apagado por defecto: copia conversaciones que de otro modo nunca veríamos.","type":"boolean"},"age":{"description":"Hace cuánto que esta línea de WhatsApp está en uso real — no podemos verlo, así que preguntamos. Define cuántas conversaciones NUEVAS puede iniciar el número por día: `new` (creada hace pocos días) empieza en 5/día y va subiendo, `established` (en uso hace más de un mes) arranca en el techo de 20/día. Por defecto es `new`, la respuesta segura. Exagerar aquí solo te cuesta tu propio número.","type":"string","enum":["new","weeks","established"]}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/numbers/check/{phone}":{"get":{"operationId":"check_number_get","summary":"Comprueba si un teléfono está en WhatsApp","tags":["Numbers"],"description":"Le pregunta a WhatsApp si un teléfono tiene cuenta y, teniéndola, trae su foto de perfil. Los celulares brasileños se comprueban con y sin el noveno dígito, y la respuesta informa la forma que realmente coincidió. Si no se puede contactar a WhatsApp, la respuesta asume que sí y marca `verified: false` — una caída no debe bloquear envíos a personas reales. Gratis.","security":[{"bearerAuth":[]}],"parameters":[{"name":"phone","in":"path","required":true,"description":"El teléfono a consultar, p. ej. +5511988887777.","schema":{"type":"string","minLength":6,"description":"El teléfono a consultar, p. ej. +5511988887777."}},{"name":"from","in":"query","required":false,"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","schema":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1}}],"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/numbers/{id}":{"get":{"operationId":"number_status_get","summary":"Mira el estado de un número","tags":["Numbers"],"description":"El estado actual de un número: `connected` (listo para enviar), `pairing` (esperando el escaneo), `disconnected` (se cerró la sesión — vincúlalo de nuevo) o `banned` (WhatsApp lo restringió; espera a que pase la ventana de 24h antes de reconectar). Gratis.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Id del número, viene de list_numbers.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id del número, viene de list_numbers."}}],"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}},"patch":{"operationId":"update_number_patch","summary":"Cambia los ajustes de un número","tags":["Numbers"],"description":"Renombra un número, corrige la antigüedad que declaró su dueño o cambia cómo envía. Las dos políticas de envío son la parte interesante:\n\n`pacing` es la cola. Mientras está activada — lo predeterminado — un mensaje a alguien a quien este número nunca le escribió se acepta y se retiene, y sale 15–25 minutos después, de a uno. Es lo más eficaz para mantener vivo un número no oficial, así que desactívalo solo si vas a espaciar los envíos por tu cuenta.\n\n`humanize` es cómo se ve un envío: en línea, lee lo que te escribieron, escribe durante lo que dure la respuesta. Cuesta unos segundos por llamada y compra un número que no se lee como un script.\n\nTodo es opcional y se combina con lo que ya está. Gratis.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Id del número, viene de list_numbers.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id del número, viene de list_numbers."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"La etiqueta que elegiste.","type":"string","minLength":1,"maxLength":80},"age":{"description":"Corrige hace cuánto que esta línea de WhatsApp está en uso real — define cuántas conversaciones NUEVAS puede iniciar el número por día. Exagerar aquí solo te cuesta tu propio número.","type":"string","enum":["new","weeks","established"]},"pacing":{"description":"Cómo espacia este número las conversaciones que INICIA. Responder a quien escribió primero nunca se retiene.","type":"object","properties":{"enabled":{"description":"Pone los mensajes en frío en cola en vez de enviarlas de inmediato. Activado por defecto. Desactivarlo hace que todo envío sea inmediato — y el ritmo pasa a ser tu problema, que es algo serio de asumir: las ráfagas de mensajes en frío son lo que hace que WhatsApp banee números.","type":"boolean"},"minMinutes":{"description":"Intervalo más corto entre dos conversaciones que este número inicia, en minutos. Por defecto 15.","type":"integer","minimum":1,"maximum":720},"maxMinutes":{"description":"Intervalo más largo, en minutos. Por defecto 25. El intervalo real se sortea entre los dos, porque una cola que suelta un mensaje exactamente cada veinte minutos es un metrónomo, y un metrónomo es una firma.","type":"integer","minimum":1,"maximum":720}},"additionalProperties":false},"humanize":{"description":"Qué partes del comportamiento humano hace este número en cada envío. Cada una es un patrón que WhatsApp puede leer: una sesión que nunca está en línea, nunca lee nada y contesta un mensaje largo al instante se está declarando software.","type":"object","properties":{"enabled":{"description":"Si este número envía como una persona. Activado por defecto.","type":"boolean"},"typing":{"description":"Muestra “escribiendo…” durante lo que tardaría en escribirse el mensaje, antes de enviarlo.","type":"boolean"},"presence":{"description":"Se conecta antes de enviar y se desconecta después.","type":"boolean"},"markRead":{"description":"Pone los ticks azules en lo último que escribió el destinatario antes de contestarle. Desactívalo si no quieres enviar acuses de lectura en tus chats.","type":"boolean"}},"additionalProperties":false}},"additionalProperties":false}}}},"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}},"delete":{"operationId":"delete_number_delete","summary":"Elimina un número","tags":["Numbers"],"description":"Saca un número de la cuenta para siempre, cerrando su sesión de WhatsApp en el camino. Esto libera un slot del límite del plan. Los mensajes ya enviados quedan igual, y el consumo que costaron sigue en la factura. Gratis, y sin vuelta atrás.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Id del número a eliminar, viene de list_numbers.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id del número a eliminar, viene de list_numbers."}}],"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/numbers/{id}/pair":{"post":{"operationId":"pair_number_post","summary":"Vincula un número con WhatsApp","tags":["Numbers"],"description":"Inicia (o retoma) la vinculación de una cuenta de WhatsApp a uno de tus números. `qr` devuelve un código QR para escanear desde WhatsApp → Dispositivos vinculados; `code` devuelve un código de vinculación de 8 caracteres para escribir ahí en su lugar, y necesita el teléfono que se está vinculando. Un QR vive ~90 segundos y uno nuevo solo se puede generar cada pocos minutos, así que llama de nuevo para consultar: `state: \"pairing\"` sin código todavía significa simplemente que se está generando. Gratis.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Id del número a vincular, viene de list_numbers.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id del número a vincular, viene de list_numbers."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"description":"`qr` (por defecto) o `code` para el código de vinculación que se escribe.","type":"string","enum":["qr","code"]},"phone":{"description":"Obligatorio con `mode: \"code\"` — el teléfono que se está vinculando.","type":"string","minLength":8}},"additionalProperties":false}}}},"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/numbers/{id}/unpair":{"post":{"operationId":"unpair_number_post","summary":"Desconecta un número de WhatsApp","tags":["Numbers"],"description":"Cierra la sesión de la cuenta de WhatsApp vinculada a este número, conservando el número en sí. Úsalo para vincular otro teléfono: un número conectado no puede volver a vincularse hasta que se desconecte. Gratis.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Id del número, viene de list_numbers.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id del número, viene de list_numbers."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/chats/{phone}/typing":{"post":{"operationId":"typing_post","summary":"Muestra el indicador de escritura","tags":["Messages"],"description":"Muestra (o limpia) el indicador de “escribiendo…” en un chat. Gratis — no es un mensaje. WhatsApp lo limpia solo tras unos segundos, así que vuelve a llamar durante una respuesta larga.","security":[{"bearerAuth":[]}],"parameters":[{"name":"phone","in":"path","required":true,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us).","schema":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"state":{"description":"`composing` muestra el indicador, `paused` lo limpia. Por defecto: composing.","type":"string","enum":["composing","paused"]},"recording":{"description":"Muestra “grabando audio…” en lugar de “escribiendo…”.","type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/chats/{phone}/read":{"post":{"operationId":"mark_read_post","summary":"Marca los mensajes como leídos","tags":["Messages"],"description":"Pone los ticks azules en los mensajes que tu número recibió en un chat. Gratis. Los ids de los mensajes vienen del webhook entrante.","security":[{"bearerAuth":[]}],"parameters":[{"name":"phone","in":"path","required":true,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us).","schema":{"type":"string","minLength":1,"description":"Destinatario — un teléfono en formato internacional (+5511988887777) o una dirección de WhatsApp (5511988887777@s.whatsapp.net, un contacto @lid o un grupo @g.us)."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"from":{"description":"Cuál de tus números de WhatsApp hace el envío — su id o su teléfono. Opcional: con un solo número conectado lo resolvemos por ti.","type":"string","minLength":1},"messageIds":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1},"description":"Ids de los mensajes recibidos para confirmar."}},"required":["messageIds"],"additionalProperties":false}}}},"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/webhooks":{"get":{"operationId":"get_webhook_get","summary":"Mira tu endpoint de webhook","tags":["Webhooks"],"description":"El endpoint al que los eventos llegan por POST, a cuáles está suscrito y si sigue sano. `null` cuando no hay ninguno registrado. `disabledReason: \"failures\"` significa que dejamos de entregar tras una racha larga de intentos muertos — arregla el endpoint y guárdalo de nuevo (o envía una prueba) para volver a encenderlo. Gratis.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}},"put":{"operationId":"set_webhook_put","summary":"Registra tu endpoint de webhook","tags":["Webhooks"],"description":"Apunta whatsapi.sh a una URL https y empieza a recibir eventos ahí. La respuesta trae el secreto de firma UNA VEZ, en el primer registro: guárdalo — cada entrega va firmada con él en el header `x-whatsapi-signature` (`t=<unix>,v1=<hmac-sha256 of \"<t>.<body>\">`), y esa firma es como sabes que la solicitud es realmente nuestra. Llamar de nuevo actualiza la URL o la suscripción y conserva el mismo secreto salvo que pidas rotarlo. Guardar también vuelve a habilitar un endpoint que hubiéramos deshabilitado. Gratis.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"A dónde van los eventos por POST, p. ej. https://api.yourapp.com/webhooks/whatsapp."},"events":{"minItems":1,"type":"array","items":{"type":"string","enum":["message.created","message.sent","message.failed","message.status","number.connected","number.disconnected","number.logged_out"]},"description":"Qué eventos recibir: message.created, message.sent, message.failed, message.status, number.connected, number.disconnected, number.logged_out. Por defecto, todos."},"rotateSecret":{"description":"Genera un nuevo secreto de firma y lo devuelve — el anterior deja de funcionar.","type":"boolean"}},"required":["url"],"additionalProperties":false}}}},"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}},"post":{"operationId":"set_webhook_post","summary":"Registra tu endpoint de webhook","tags":["Webhooks"],"description":"Apunta whatsapi.sh a una URL https y empieza a recibir eventos ahí. La respuesta trae el secreto de firma UNA VEZ, en el primer registro: guárdalo — cada entrega va firmada con él en el header `x-whatsapi-signature` (`t=<unix>,v1=<hmac-sha256 of \"<t>.<body>\">`), y esa firma es como sabes que la solicitud es realmente nuestra. Llamar de nuevo actualiza la URL o la suscripción y conserva el mismo secreto salvo que pidas rotarlo. Guardar también vuelve a habilitar un endpoint que hubiéramos deshabilitado. Gratis.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"A dónde van los eventos por POST, p. ej. https://api.yourapp.com/webhooks/whatsapp."},"events":{"minItems":1,"type":"array","items":{"type":"string","enum":["message.created","message.sent","message.failed","message.status","number.connected","number.disconnected","number.logged_out"]},"description":"Qué eventos recibir: message.created, message.sent, message.failed, message.status, number.connected, number.disconnected, number.logged_out. Por defecto, todos."},"rotateSecret":{"description":"Genera un nuevo secreto de firma y lo devuelve — el anterior deja de funcionar.","type":"boolean"}},"required":["url"],"additionalProperties":false}}}},"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}},"delete":{"operationId":"delete_webhook_delete","summary":"Quita tu endpoint de webhook","tags":["Webhooks"],"description":"Deja de enviar eventos y olvida el endpoint, su secreto y su historial de entregas. Registrar de nuevo genera un secreto nuevo. Gratis.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/webhooks/test":{"post":{"operationId":"test_webhook_post","summary":"Envía un evento de prueba a tu endpoint","tags":["Webhooks"],"description":"Envía un evento `ping` a tu endpoint registrado ahora mismo y espera la respuesta, para que compruebes la URL y tu verificación de firma sin esperar un mensaje real. El resultado es el desenlace real: `ok: false` trae el status HTTP que respondió tu servidor, o por qué no pudimos alcanzarlo. Un ping exitoso también revive un endpoint que hubiéramos deshabilitado automáticamente. Gratis.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/webhooks/deliveries":{"get":{"operationId":"list_webhook_deliveries_get","summary":"Entregas de webhook recientes","tags":["Webhooks"],"description":"Las últimas entregas que intentamos, más nuevas primero — qué enviamos, qué respondió tu endpoint y cuántos intentos tomó. Es la respuesta a “el evento nunca llegó”. Las entregas se guardan una semana. Gratis.","security":[{"bearerAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"Cuántos devolver, más nuevos primero. Hasta 50 (el valor por defecto).","schema":{"description":"Cuántos devolver, más nuevos primero. Hasta 50 (el valor por defecto).","type":"integer","minimum":1,"maximum":50}}],"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/queue":{"get":{"operationId":"list_queued_messages_get","summary":"Mira qué está esperando para salir","tags":["Queue"],"description":"Cada mensaje que está ocupando un lugar en la cola de envío de un número, del más próximo al más lejano. Un mensaje llega aquí cuando abre una conversación que el número nunca tuvo. Se acepta y se cobra al instante, y después sale con 15–25 minutos de diferencia, para que una serie de mensajes en frío no parezca un envío masivo. `scheduledAt` dice cuándo sale cada uno, y `message.sent` llega al webhook de la cuenta cuando eso pasa. Gratis.","security":[{"bearerAuth":[]}],"parameters":[{"name":"numberId","in":"query","required":false,"description":"Solo la cola de este número. Omítelo para ver todos los números de la cuenta.","schema":{"description":"Solo la cola de este número. Omítelo para ver todos los números de la cuenta.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},{"name":"limit","in":"query","required":false,"description":"Cuántos devolver, primero los más próximos. Por defecto 200.","schema":{"description":"Cuántos devolver, primero los más próximos. Por defecto 200.","type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]},"example":{"data":[{"id":"9c1f0b7e-8b1a-4a2e-9b0a-1f2e3d4c5b6a","numberId":"a1b2c3d4-0000-0000-0000-000000000000","to":"+5511988887777","kind":"text","preview":"Hi Marina — following up on the quote…","scheduledAt":"2026-08-24T19:18:00.000Z","queuedAt":"2026-08-24T19:00:00.000Z"}]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/queue/{id}":{"delete":{"operationId":"cancel_queued_message_delete","summary":"Cancela un mensaje que todavía no salió","tags":["Queue"],"description":"Saca de la cola un mensaje que sigue esperando. La cuota del mensaje y el límite de conversaciones nuevas del día vuelven los dos, así que es como si nunca se hubiera pedido. Un mensaje que ya salió no se puede cancelar — WhatsApp lo tiene — y la respuesta lo dice en vez de fingir. El id es el mismo que devolvió la llamada de envío. Gratis.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Id del mensaje en cola — lo que devolvió la llamada de envío.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id del mensaje en cola — lo que devolvió la llamada de envío."}}],"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}},"/usage":{"get":{"operationId":"get_usage_get","summary":"Consulta cuota y uso","tags":["Usage"],"description":"Mensajes enviados este mes contra la cuota del plan, hoy contra el límite diario de uso justo, el saldo de crédito restante y el desglose por clave. Léelo antes de un envío en lote. Gratis.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"La solicitud fue exitosa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"VALIDATION_ERROR — los errores, nunca los valores."},"401":{"description":"UNAUTHORIZED / INVALID_TOKEN — la clave falta o está mal."},"429":{"description":"RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED — el Retry-After dice cuándo se libera."}}}}},"x-mcp-tools":[{"name":"send_message","title":"Send a WhatsApp message","description":"Send a text message to a phone number over WhatsApp. Counts as one message against the account's monthly quota.\n\n**A message to someone this number has never written to is not sent immediately.** It is accepted, charged and QUEUED: the answer carries `status: \"queued\"` and `scheduledAt`. It leaves 15–25 minutes later, one at a time. That is deliberate. A burst of first approaches is the fastest way to get a WhatsApp number banned, and spacing them out is what separates a number that lasts from one that does not. `message.sent` fires on the account's webhook when it goes out, carrying WhatsApp's id. Use `skipQueue: true` for a message the recipient is waiting for right now, or turn the queue off for the number with update_number.\n\nReplies to people who wrote first are never queued and never spend budget. A first approach also spends one of the number's NEW CONVERSATIONS for the day — the anti-ban ceiling reported as `newConversationsRemaining`. A `sent` answer carries WhatsApp's message id; delivery and read receipts arrive later on the account's webhook, not here.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Recipient — a phone number in international format (+5511988887777) or a WhatsApp address (5511988887777@s.whatsapp.net, an @lid contact, or a @g.us group)."},"from":{"description":"Which of your WhatsApp numbers sends this — its id or its phone number. Optional: with a single connected number it is resolved for you.","type":"string","minLength":1},"text":{"type":"string","minLength":1,"maxLength":4096,"description":"The message body."},"linkPreview":{"description":"Render a preview card for the first link in the text. Default: on.","type":"boolean"},"skipQueue":{"description":"Send immediately instead of taking a slot in this number's queue. Only for a message the recipient is expecting right now — a reply, a code, a confirmation of something they just did. Outreach sent this way is exactly what gets WhatsApp numbers banned, which is why the queue exists. Default: off.","type":"boolean"},"humanize":{"description":"Whether to send like a person: come online, open what they last wrote, show “typing…” for as long as the message would take to type. On by default, which adds a few seconds to the call — `false` sends it bare and instantly. Configure the parts per number with update_number.","type":"boolean"}},"required":["to","text"],"additionalProperties":false}},{"name":"send_media","title":"Send a photo, video, voice note or file","description":"Send media over WhatsApp from a public URL or base64 data. `type` picks the bubble the recipient sees: `image` and `video` support a caption, `document` is a file attachment named by `fileName`, and `audio` becomes a voice note when the file is ogg/opus — any other audio format is sent as a playable attachment instead, because WhatsApp will not render a voice note that is not opus. Files are capped at 16MB, and `url` beats `base64` for anything sizeable. Counts as one message.\n\nLike every send: media to someone this number has never written to is QUEUED rather than sent, and comes back with `status: \"queued\"` and `scheduledAt`. The file is fetched when the slot comes up, so a `url` has to still be reachable then.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Recipient — a phone number in international format (+5511988887777) or a WhatsApp address (5511988887777@s.whatsapp.net, an @lid contact, or a @g.us group)."},"from":{"description":"Which of your WhatsApp numbers sends this — its id or its phone number. Optional: with a single connected number it is resolved for you.","type":"string","minLength":1},"type":{"type":"string","enum":["image","video","audio","document"],"description":"Which kind of media bubble to send."},"url":{"description":"Public https URL of the file — the better option. We stream the download and forward the bytes, so the size of the file never rides in your request.","type":"string","format":"uri"},"base64":{"description":"The file as base64, bare or as a `data:<mime>;base64,…` URI. Base64 inflates a request by a third, so prefer `url` for anything but small files.","type":"string","minLength":1},"mimeType":{"description":"Overrides the detected content type, e.g. `image/webp`.","type":"string","minLength":3},"caption":{"description":"Text shown under the media. Images and videos only — WhatsApp has no caption field for documents or audio.","type":"string","maxLength":1024},"fileName":{"description":"Documents: the filename WhatsApp shows on the bubble.","type":"string","minLength":1,"maxLength":255},"seconds":{"description":"Voice notes: duration, so the bubble shows the right length.","type":"integer","exclusiveMinimum":0,"maximum":600},"skipQueue":{"description":"Send immediately instead of taking a slot in this number's queue. Only for a message the recipient is expecting right now — a reply, a code, a confirmation of something they just did. Outreach sent this way is exactly what gets WhatsApp numbers banned, which is why the queue exists. Default: off.","type":"boolean"},"humanize":{"description":"Whether to send like a person: come online, open what they last wrote, show “typing…” for as long as the message would take to type. On by default, which adds a few seconds to the call — `false` sends it bare and instantly. Configure the parts per number with update_number.","type":"boolean"}},"required":["to","type"],"additionalProperties":false}},{"name":"send_batch","title":"Send one text to many recipients","description":"Send the same text to up to 25 recipients in one call — one message, and one message's worth of quota, per recipient. The answer reports each recipient separately: a batch where some land and some do not is the normal case, not an error. If something systemic stops the run (the number drops its session, the quota or rate limit refuses) the remaining recipients come back as `skipped` instead of being attempted. Send them again once that is cleared.\n\n**While the number paces its sends, which is the default, a batch does not send: it queues.** Every recipient comes back as `queued` with its own `scheduledAt`. The messages leave one at a time: minutes apart for anyone this number has never written to, seconds apart for people it already talks to. Twenty-five messages arriving in one second is precisely the pattern WhatsApp restricts numbers for, so the endpoint no longer does that. Watch `message.sent` on the account's webhook for the ids, `GET /v1/queue` for what is still waiting, and turn pacing off for the number if you truly want them at once. Paid plans only.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"to":{"minItems":1,"maxItems":25,"type":"array","items":{"type":"string","minLength":1,"description":"Recipient — a phone number in international format (+5511988887777) or a WhatsApp address (5511988887777@s.whatsapp.net, an @lid contact, or a @g.us group)."},"description":"Recipients, up to 25. Each gets its own message; no duplicates."},"from":{"description":"Which of your WhatsApp numbers sends this — its id or its phone number. Optional: with a single connected number it is resolved for you.","type":"string","minLength":1},"text":{"type":"string","minLength":1,"maxLength":4096,"description":"The message body — the same for everyone."},"skipQueue":{"description":"Send immediately instead of taking a slot in this number's queue. Only for a message the recipient is expecting right now — a reply, a code, a confirmation of something they just did. Outreach sent this way is exactly what gets WhatsApp numbers banned, which is why the queue exists. Default: off.","type":"boolean"},"humanize":{"description":"Whether to send like a person: come online, open what they last wrote, show “typing…” for as long as the message would take to type. On by default, which adds a few seconds to the call — `false` sends it bare and instantly. Configure the parts per number with update_number.","type":"boolean"}},"required":["to","text"],"additionalProperties":false}},{"name":"send_location","title":"Send a location pin","description":"Send a map pin over WhatsApp. Counts as one message, and — like every send — is queued rather than sent when it opens a conversation this number has never had.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Recipient — a phone number in international format (+5511988887777) or a WhatsApp address (5511988887777@s.whatsapp.net, an @lid contact, or a @g.us group)."},"from":{"description":"Which of your WhatsApp numbers sends this — its id or its phone number. Optional: with a single connected number it is resolved for you.","type":"string","minLength":1},"latitude":{"type":"number","minimum":-90,"maximum":90},"longitude":{"type":"number","minimum":-180,"maximum":180},"name":{"description":"Label shown on the pin.","type":"string","maxLength":200},"skipQueue":{"description":"Send immediately instead of taking a slot in this number's queue. Only for a message the recipient is expecting right now — a reply, a code, a confirmation of something they just did. Outreach sent this way is exactly what gets WhatsApp numbers banned, which is why the queue exists. Default: off.","type":"boolean"},"humanize":{"description":"Whether to send like a person: come online, open what they last wrote, show “typing…” for as long as the message would take to type. On by default, which adds a few seconds to the call — `false` sends it bare and instantly. Configure the parts per number with update_number.","type":"boolean"}},"required":["to","latitude","longitude"],"additionalProperties":false}},{"name":"send_contact","title":"Send a contact card","description":"Send a contact card (vCard) over WhatsApp, so the recipient can save the number with one tap. Counts as one message, and — like every send — is queued rather than sent when it opens a conversation this number has never had.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Recipient — a phone number in international format (+5511988887777) or a WhatsApp address (5511988887777@s.whatsapp.net, an @lid contact, or a @g.us group)."},"from":{"description":"Which of your WhatsApp numbers sends this — its id or its phone number. Optional: with a single connected number it is resolved for you.","type":"string","minLength":1},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Display name on the card."},"vcard":{"type":"string","minLength":1,"description":"The vCard payload, e.g. `BEGIN:VCARD\\nVERSION:3.0\\n…\\nEND:VCARD`."},"skipQueue":{"description":"Send immediately instead of taking a slot in this number's queue. Only for a message the recipient is expecting right now — a reply, a code, a confirmation of something they just did. Outreach sent this way is exactly what gets WhatsApp numbers banned, which is why the queue exists. Default: off.","type":"boolean"},"humanize":{"description":"Whether to send like a person: come online, open what they last wrote, show “typing…” for as long as the message would take to type. On by default, which adds a few seconds to the call — `false` sends it bare and instantly. Configure the parts per number with update_number.","type":"boolean"}},"required":["to","name","vcard"],"additionalProperties":false}},{"name":"send_otp","title":"Send a verification code","description":"Generate a 6-digit code, send it over WhatsApp and return it. Returning the code is deliberate: verify it yourself, or hand it back to check_otp and keep nothing. Codes expire in 5 minutes by default and one number can only be sent a new code once a minute. Counts as one message.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Recipient — a phone number in international format (+5511988887777) or a WhatsApp address (5511988887777@s.whatsapp.net, an @lid contact, or a @g.us group)."},"from":{"description":"Which of your WhatsApp numbers sends this — its id or its phone number. Optional: with a single connected number it is resolved for you.","type":"string","minLength":1},"template":{"description":"Message body. Must contain `{code}`; `{minutes}` expands to the expiry. Default: “{code} is your verification code. It expires in {minutes} minutes.”","type":"string","minLength":1,"maxLength":1000},"expiresInSeconds":{"description":"How long the code stays valid. Default 300 (5 minutes).","type":"integer","minimum":60,"maximum":3600}},"required":["to"],"additionalProperties":false}},{"name":"check_otp","title":"Check a verification code","description":"Verify a code sent with send_otp and consume it, so it can never be used twice. A wrong or expired code answers 400 OTP_INVALID — deliberately an error, not a `verified: false`, so a missing check at the caller can never read as success. Five wrong guesses kill the code. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Recipient — a phone number in international format (+5511988887777) or a WhatsApp address (5511988887777@s.whatsapp.net, an @lid contact, or a @g.us group)."},"code":{"type":"string","minLength":4,"maxLength":10,"description":"The code the user typed."}},"required":["to","code"],"additionalProperties":false}},{"name":"list_numbers","title":"List your WhatsApp numbers","description":"Every WhatsApp number on the account with its connection status. Start here when you do not know which number to send from, or to check whether one needs re-pairing. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false}},{"name":"create_number","title":"Add a WhatsApp number","description":"Create a new WhatsApp number slot on the account and return it. This is step one of two: the slot starts `disconnected` and cannot send until pair_number links a real WhatsApp account to it. How many can exist at once is what the plan sells. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"description":"A label you choose, e.g. “Support” — only you ever see it."},"historySync":{"description":"Pull the phone's existing chat history on pairing. Off by default: it copies conversations we otherwise never see.","type":"boolean"},"age":{"description":"How long this WhatsApp line has been in real use — we cannot see it, so we ask. It sets how many NEW conversations the number may start per day: `new` (registered days ago) starts at 5/day and climbs, `established` (in use over a month) starts at the 20/day ceiling. Defaults to `new`, the safe answer. Overstating it only costs you the number.","type":"string","enum":["new","weeks","established"]}},"required":["name"],"additionalProperties":false}},{"name":"check_number","title":"Check whether a phone is on WhatsApp","description":"Ask WhatsApp whether a phone number has an account, and get its profile picture when it does. Brazilian mobiles are checked with and without the extra 9 and the answer reports the form that actually matched. If WhatsApp cannot be reached the answer assumes yes and sets `verified: false` — an outage must not block sends to real people. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"phone":{"type":"string","minLength":6,"description":"The phone number to look up, e.g. +5511988887777."},"from":{"description":"Which of your WhatsApp numbers sends this — its id or its phone number. Optional: with a single connected number it is resolved for you.","type":"string","minLength":1}},"required":["phone"],"additionalProperties":false}},{"name":"number_status","title":"Get one number's status","description":"One number's current state: `connected` (ready to send), `pairing` (waiting for a scan), `disconnected` (session dropped — pair it again) or `banned` (WhatsApp restricted it; wait out the 24h window before reconnecting). Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"numberId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id of the number, from list_numbers."}},"required":["numberId"],"additionalProperties":false}},{"name":"update_number","title":"Change a number's settings","description":"Rename a number, correct the age its owner declared, or change how it sends. The two sending policies are the interesting part:\n\n`pacing` is the queue. While it is on — the default — a message to someone this number has never written to is accepted and held, then sent 15–25 minutes later, one at a time. It is the single most effective thing keeping an unofficial number alive, so turn it off only if you are pacing the sends yourself.\n\n`humanize` is what one send looks like: online, reads what they wrote, types for as long as the reply is long. It costs a few seconds per call and buys a number that does not read as a script.\n\nEverything is optional and merges into what is already there. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"numberId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id of the number, from list_numbers."},"name":{"description":"The label you chose.","type":"string","minLength":1,"maxLength":80},"age":{"description":"Correct how long this WhatsApp line has been in real use — it sets how many NEW conversations the number may start per day. Overstating it only costs you the number.","type":"string","enum":["new","weeks","established"]},"pacing":{"description":"How this number spaces the conversations it STARTS. Replies to people who wrote first are never held.","type":"object","properties":{"enabled":{"description":"Queue first approaches instead of sending them straight away. On by default. Turning it off makes every send immediate — and makes the pacing your problem, which is a real thing to take on: bursts of first approaches are what WhatsApp bans numbers for.","type":"boolean"},"minMinutes":{"description":"Shortest gap between two conversations this number starts, in minutes. Default 15.","type":"integer","minimum":1,"maximum":720},"maxMinutes":{"description":"Longest gap, in minutes. Default 25. The real gap is picked at random between the two, because a queue that fires exactly every twenty minutes is a metronome, and a metronome is a signature.","type":"integer","minimum":1,"maximum":720}},"additionalProperties":false},"humanize":{"description":"Which parts of the human act this number performs on every send. Each one is a pattern WhatsApp can read: a session that is never online, never reads and answers a long message instantly is describing itself as software.","type":"object","properties":{"enabled":{"description":"Send like a person at all. On by default.","type":"boolean"},"typing":{"description":"Show “typing…” for as long as the message would take to type, before sending it.","type":"boolean"},"presence":{"description":"Come online before sending, go offline after.","type":"boolean"},"markRead":{"description":"Put blue ticks on what the recipient last wrote before answering it. Turn this off if read receipts on your own chats are not wanted.","type":"boolean"}},"additionalProperties":false}},"required":["numberId"],"additionalProperties":false}},{"name":"pair_number","title":"Pair a number with WhatsApp","description":"Start (or resume) linking a WhatsApp account to one of your numbers. `qr` returns a QR code to scan from WhatsApp → Linked devices; `code` returns an 8-character linking code to type there instead, and needs the phone number being linked. A QR lives ~90 seconds and a new one can only be minted every few minutes, so call again to poll: `state: \"pairing\"` with no code yet simply means it is still being minted. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"numberId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id of the number to pair, from list_numbers."},"mode":{"description":"`qr` (default) or `code` for the type-in linking code.","type":"string","enum":["qr","code"]},"phone":{"description":"Required with `mode: \"code\"` — the phone number being linked.","type":"string","minLength":8}},"required":["numberId"],"additionalProperties":false}},{"name":"typing","title":"Show the typing indicator","description":"Show (or clear) the “typing…” indicator in a chat. Free — it is not a message. WhatsApp clears it on its own after a few seconds, so call it again for a long reply.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Recipient — a phone number in international format (+5511988887777) or a WhatsApp address (5511988887777@s.whatsapp.net, an @lid contact, or a @g.us group)."},"from":{"description":"Which of your WhatsApp numbers sends this — its id or its phone number. Optional: with a single connected number it is resolved for you.","type":"string","minLength":1},"state":{"description":"`composing` shows the indicator, `paused` clears it. Default: composing.","type":"string","enum":["composing","paused"]},"recording":{"description":"Show “recording audio…” instead of “typing…”.","type":"boolean"}},"required":["to"],"additionalProperties":false}},{"name":"mark_read","title":"Mark messages as read","description":"Put blue ticks on messages your number received in a chat. Free. Message ids come from the inbound webhook.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"to":{"type":"string","minLength":1,"description":"Recipient — a phone number in international format (+5511988887777) or a WhatsApp address (5511988887777@s.whatsapp.net, an @lid contact, or a @g.us group)."},"from":{"description":"Which of your WhatsApp numbers sends this — its id or its phone number. Optional: with a single connected number it is resolved for you.","type":"string","minLength":1},"messageIds":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1},"description":"Ids of the received messages to acknowledge."}},"required":["to","messageIds"],"additionalProperties":false}},{"name":"get_webhook","title":"Get your webhook endpoint","description":"The endpoint events are being POSTed to, which ones it is subscribed to, and whether it is still healthy. `null` when none is registered. `disabledReason: \"failures\"` means we stopped delivering after a long run of dead attempts — fix the endpoint and set it again (or fire a test) to switch it back on. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false}},{"name":"set_webhook","title":"Register your webhook endpoint","description":"Point whatsapi.sh at an https URL and start receiving events there. The answer carries the signing secret ONCE, on first registration: keep it — every delivery is signed with it in the `x-whatsapi-signature` header (`t=<unix>,v1=<hmac-sha256 of \"<t>.<body>\">`), and that signature is how you know a request is really from us. Calling this again updates the URL or the subscription and keeps the same secret unless you ask to rotate it. Saving also re-enables an endpoint we had disabled. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","format":"uri","description":"Where to POST events, e.g. https://api.yourapp.com/webhooks/whatsapp."},"events":{"minItems":1,"type":"array","items":{"type":"string","enum":["message.created","message.sent","message.failed","message.status","number.connected","number.disconnected","number.logged_out"]},"description":"Which events to receive: message.created, message.sent, message.failed, message.status, number.connected, number.disconnected, number.logged_out. Defaults to all of them."},"rotateSecret":{"description":"Mint a new signing secret and return it — the old one stops working.","type":"boolean"}},"required":["url"],"additionalProperties":false}},{"name":"test_webhook","title":"Fire a test event at your endpoint","description":"Send a `ping` event to your registered endpoint right now and wait for the answer, so you can verify the URL and your signature check without waiting for a real message. The result is the actual outcome: `ok: false` carries the HTTP status your server answered, or why we could not reach it. A successful ping also revives an endpoint we had auto-disabled. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false}},{"name":"list_queued_messages","title":"See what is waiting to be sent","description":"Every message currently holding a slot in a number's send queue, soonest first. Messages land here when they open a conversation the number has never had. They are accepted and charged straight away, then released 15–25 minutes apart, so a run of first approaches does not read as a broadcast. `scheduledAt` says when each one leaves, and `message.sent` fires on the account's webhook when it does. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"numberId":{"description":"Only this number's queue. Omit for every number on the account.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"limit":{"description":"How many to return, soonest first. Default 200.","type":"integer","minimum":1,"maximum":200}},"additionalProperties":false}},{"name":"cancel_queued_message","title":"Cancel a message that hasn't been sent yet","description":"Take a waiting message back out of the queue. Its message quota and the day's new-conversation budget are both handed back, so it is as if it had never been asked for. A message that has already left cannot be cancelled — WhatsApp has it — and the answer says so rather than pretending. The id is the one the send call returned. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Id of the queued message — what the send call answered with."}},"required":["id"],"additionalProperties":false}},{"name":"get_usage","title":"Check quota and usage","description":"Messages sent this month against the plan quota, today against the daily fair-use cap, the leftover credit balance, and the per-key breakdown. Read this before a bulk send. Free.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false}}]}