Saltar al contenido
WbizTool

API v2 (beta)

API v2

La API v2 es la nueva API REST de WbizTool: una clave por app con solo el acceso que necesita, URLs orientadas a recursos, códigos de estado HTTP reales, webhooks JSON firmados y reintentos seguros. Esta página explica lo que tienen en común todas las solicitudes. Las páginas de recursos describen cada endpoint.

URL base#

https://wbiztool.com/api/v2

Las rutas no llevan barra final (/api/v2/messages), aunque también se acepta. Una dirección que no existe responde 404 con el código not_found.

Ejemplo rápido#

Comprueba tu clave con GET /me. Funciona con cualquier permiso y no envía nada:

curl https://wbiztool.com/api/v2/me \
  -H "Authorization: Bearer $WBIZTOOL_API_KEY"
{
  "workspace": { "id": 5012, "name": "Acme Bakery" },
  "key": {
    "id": 7,
    "name": "Order system",
    "mode": "live",
    "scopes": ["messages:send", "messages:read"],
    "limited_to_numbers": false,
    "number_ids": [],
    "expires_at": null
  },
  "plan": { "name": "Starter" },
  "limits": {
    "requests_per_minute": 60,
    "requests_per_day": 10000,
    "messages_per_number_per_day": 1000,
    "messages_remaining": null
  }
}

messages_remaining es null cuando el plan envía mensajes ilimitados. plan es null cuando el espacio de trabajo no tiene un plan activo.

Claves#

Envía tu clave en la cabecera Authorization en cada solicitud:

Authorization: Bearer wbz_live_…

No hay client_id: la clave indica su espacio de trabajo. Una clave v2 empieza por wbz_live_ o wbz_test_, seguida de 38 letras y dígitos. Las claves v1 no funcionan en v2 (reciben v1_key_on_v2), y las claves v2 no funcionan en v1.

Crear una clave#

Los Propietarios crean claves en Configuración → Claves API, en la tarjeta Claves API v2: haz clic en Crear clave v2 y elige:

  • Nombre: la app que la usará, hasta 50 caracteres.
  • Tipo de clave: Real envía mensajes de verdad; Prueba lo comprueba todo y no envía nada (consulta Modo de prueba).
  • Qué puede hacer: los permisos, o Todo.
  • Números que puede usar: todos los números, incluidos los que se añadan después, o solo algunos (consulta Claves limitadas a algunos números).
  • Caduca: nunca, o en 30 días, 90 días o 1 año.

La clave se muestra una sola vez. Cópiala enseguida y guárdala en tu servidor, por ejemplo en una variable de entorno. WbizTool solo guarda un hash de la clave y sus cuatro últimos caracteres, así que una clave perdida no se puede volver a mostrar: revócala y crea otra.

Un espacio de trabajo puede tener hasta 10 claves v2 sin revocar. La tarjeta muestra de cada clave sus cuatro últimos caracteres, sus permisos, sus números, su caducidad, y cuándo y desde qué dirección IP se usó por última vez. Revocar detiene una clave al instante y, con ella, los endpoints de webhook que creó. Una clave que pasó su fecha de caducidad recibe api_key_expired.

Permisos#

Cada clave solo puede hacer lo que permiten sus permisos. Una solicitud fuera de ellos responde 403 con missing_scope, y required_scope en el error indica cuál necesita.

PermisoQué permite
messages:sendEnviar mensajes y lotes, y cancelar mensajes en espera: POST /messages, POST /messages/batch, DELETE /messages/{id}
messages:readLeer mensajes y su estado (GET /messages, GET /messages/{id}), y gestionar endpoints de webhook
remindersCrear, listar, cambiar, pausar y eliminar recordatorios
numbersListar números, ver su conexión y conectar números de WhatsApp
mediaSubir, listar y eliminar archivos del Directorio de medios
contactsVerificaciones de números (los endpoints de contactos llegarán más adelante)

GET /me no necesita ningún permiso en particular. Enviar a un contacto guardado con to.contact_id solo necesita messages:send.

Claves limitadas a algunos números#

Una clave limitada a algunos números solo funciona con ellos:

  • Solo envía desde esos números, y solo lista, lee y cancela sus mensajes. Otro número en from recibe number_not_allowed.
  • GET /numbers lista solo esos números. Puede volver a conectarlos, pero no puede añadir un número nuevo.
  • Solo ve y cambia los recordatorios que se envían desde esos números, y un recordatorio nuevo debe indicar uno de ellos como from.
  • No puede gestionar endpoints de webhook en absoluto, porque reciben los eventos de todo el espacio de trabajo.

GET /me muestra limited_to_numbers y los number_ids que la clave puede usar.

Solicitudes#

  • JSON de entrada y de salida. Envía los cuerpos como Content-Type: application/json. La única excepción es subir un archivo, que es multipart/form-data. Todas las respuestas son JSON, también los errores.
  • Los nombres de los campos están en snake_case, y los campos desconocidos se rechazan: un error de escritura como txt responde validation_failed en lugar de ignorarse.
  • Los números de teléfono van en formato internacional con + (E.164), por ejemplo +919876543210. Se permiten espacios y guiones; un número sin + se rechaza con invalid_phone.
  • Las horas van en ISO 8601 con desfase horario, por ejemplo 2026-10-20T09:30:00+05:30 o 2026-10-20T04:00:00Z. Una hora sin desfase se rechaza con invalid_datetime. Las horas de las respuestas están en UTC y terminan en Z, excepto el next_run_at de un recordatorio, que está en la zona horaria del recordatorio.
  • Los IDs son números enteros, como el id de un número (from cuando envías) o el id de un mensaje.
  • Los códigos de estado significan lo que dicen: 200 correcto, 201 creado, 202 aceptado (en cola), 400 la solicitud es incorrecta, 401 la clave es incorrecta, 402 no quedan mensajes, 403 no permitido, 404 no encontrado, 409 conflicto, 413 demasiado grande, 415 tipo de contenido incorrecto, 422 un valor no se puede usar, 429 demasiadas solicitudes, 5xx problema de nuestro lado.

Las respuestas llevan Cache-Control: no-store.

Errores#

Todos los errores tienen el mismo formato:

{
  "error": {
    "code": "invalid_phone",
    "message": "This is not a valid phone number. Write it in international format with +, e.g. +919876543210.",
    "field": "to.phone",
    "docs": "https://wbiztool.com/docs/api/v2/errors/#invalid_phone",
    "request_id": "req_4f1c2a9b8e7d6c5b4a392817"
  }
}
  • code es estable: usa este campo para decidir qué hacer en tu código. message es un texto en inglés para el desarrollador y puede cambiar.
  • field indica el campo al que se refiere el error (to.phone, to[3].phone, schedule.cron), o es null.
  • docs enlaza al código en la página de errores.
  • request_id es igual que la cabecera X-Request-Id. Indícalo si te pones en contacto con nosotros.
  • Algunos errores añaden detalles, por ejemplo status en not_cancellable o used y limit en storage_full. Cuando una solicitud tiene varios problemas, errors lista los demás, cada uno con field y message.

Todos los códigos, su estado HTTP y qué hacer están en la página de errores.

Idempotency-Key#

Un error de red o un tiempo de espera agotado te deja sin saber si una solicitud funcionó. Envía una cabecera Idempotency-Key con un POST que crea algo y podrás volver a enviarlo sin riesgo:

curl -X POST https://wbiztool.com/api/v2/messages \
  -H "Authorization: Bearer $WBIZTOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042-ready" \
  -d '{"to": {"phone": "+919876543210"}, "text": "Hi Asha, your order #1042 is ready."}'
  • El valor lo eliges tú: cualquier cadena única de 1 a 255 caracteres imprimibles, como un UUID o tu propio ID de pedido. Crea uno nuevo para cada solicitud nueva y reutilízalo solo para reintentar la misma solicitud.
  • Un reintento recibe la primera respuesta con la cabecera Idempotent-Replayed: true. Nada se hace dos veces: ni un segundo mensaje, ni un segundo recordatorio, archivo o endpoint.
  • Los valores duran 24 horas desde la primera solicitud y pertenecen a tu clave API: otra clave API del espacio de trabajo puede usar el mismo valor sin conflicto.
  • Mismo valor, otra solicitud: una solicitud con un valor ya usado para otro método, otra ruta u otro cuerpo responde 422 idempotency_key_reused.
  • Aún en curso: mientras la primera solicitud con ese valor todavía se está procesando, un reintento responde 409 idempotency_in_progress con Retry-After: 1. Espera un momento y vuelve a enviarla.
  • Los errores no se guardan. Si la primera solicitud respondió con un error, no se guarda nada: corrige la solicitud y vuelve a enviarla con el mismo valor.
  • Los secretos no se repiten. Una repetición de POST /webhooks omite el secret del endpoint, que solo se muestra en la primera respuesta.

Funciona en POST /messages, POST /messages/batch, POST /numbers, POST /reminders, POST /media, POST /number-checks y POST /webhooks, y en todos es opcional. Las demás solicitudes ignoran la cabecera. Un valor vacío o demasiado largo responde 400 invalid_idempotency_key.

Límites de solicitudes#

Cada clave puede hacer 60 solicitudes por minuto y 10.000 solicitudes al día. Son ventanas fijas: la ventana de un minuto empieza al inicio de cada minuto del reloj, y la de un día a las 00:00 UTC. Por encima de un límite, la solicitud responde 429 rate_limited.

Las respuestas a solicitudes con una clave válida llevan estas cabeceras:

CabeceraSignificado
RateLimit-LimitSolicitudes permitidas en la ventana actual (60, o 10000 una vez alcanzado el límite diario)
RateLimit-RemainingSolicitudes que quedan en esa ventana
RateLimit-ResetSegundos hasta que esa ventana vuelva a empezar
RateLimit-PolicyAmbos límites: 60;w=60, 10000;w=86400
Retry-AfterEn un 429: segundos que debes esperar antes de volver a intentarlo

Aparte de estos:

  • Claves incorrectas: más de 30 solicitudes por minuto con una clave incorrecta desde una misma dirección IP responden 429 too_many_auth_failures con Retry-After: 60.
  • Mensajes por número: un número envía como máximo 1.000 mensajes en 24 horas (uso justo). Un envío por encima de ese límite responde 429 number_daily_cap_reached.

Paginación#

Las listas que pueden crecer (GET /messages, /reminders, /media, /number-checks y los /results de una verificación) llegan en páginas, primero lo más reciente:

{
  "data": [ … ],
  "next_cursor": "eyJpZCI6OTgxNzI2MywidCI6IjIwMjYtMTAtMThUMTA6MTU6MDIuMzExWiIsImYiOiI0NGIxMzZmYTM1NWIifQ"
}
  • limit fija el tamaño de la página, de 1 a 100 (25 por defecto).
  • Para obtener la página siguiente, envía la misma solicitud con cursor igual al next_cursor de la última página. next_cursor es null en la última página.
  • Mantén iguales los demás filtros: un cursor usado con otros filtros responde 400 invalid_cursor. Vuelve a empezar sin cursor.
  • Trata el cursor como una cadena opaca. Los elementos que se añaden mientras recorres las páginas no las desplazan.
curl "https://wbiztool.com/api/v2/messages?limit=100&cursor=eyJpZCI6OTgxNzI2MywidCI6..." \
  -H "Authorization: Bearer $WBIZTOOL_API_KEY"

GET /numbers y GET /webhooks devuelven todo en una sola lista data.

IDs de solicitud#

Todas las respuestas, también los errores, llevan una cabecera X-Request-Id como req_4f1c2a9b8e7d6c5b4a392817. Los errores llevan el mismo valor como request_id. Regístralo en tus logs e indícalo si te pones en contacto con nosotros: nos permite encontrar la solicitud en nuestros registros, que guardan el ID de tu clave, pero nunca la clave.

Si envías tu propio X-Request-Id (hasta 100 caracteres imprimibles), vuelve como X-Client-Request-Id, para que puedas relacionar las respuestas con tus propios logs.

Modo de prueba#

Una clave de prueba (wbz_test_…) te permite crear y probar una integración sin enviar nada. Créala en Configuración → Claves API con Tipo de clave: Prueba. GET /me muestra "mode": "test".

Lo que una clave de prueba hace igual que una clave real:

  • POST /messages y POST /messages/batch comprueban todo lo que comprueba un envío real: el destinatario, el texto, los medios, send_at y expires_at, el número que envía, y los permisos y números de la clave. Los mismos fallos reciben los mismos errores.
  • Los permisos, los números, los límites de solicitudes y Idempotency-Key funcionan igual.
  • Cada lectura del resto del espacio de trabajo (GET /me, /numbers, /reminders, /media, /number-checks, /webhooks) muestra sus datos reales, solo en lectura.

Lo que cambia:

  • No se envía nada. Un envío de prueba se guarda como mensaje de prueba y no usa los mensajes del plan ni los 1.000 diarios del número, así que nunca recibe no_credits ni number_daily_cap_reached.
  • Su estado avanza solo: queued, luego sent 5 segundos después y delivered 15 segundos después de crearse (contados desde send_at si está programado). Un mensaje cuyo expires_at llega antes de que se envíe pasa a expired. Los mensajes de prueba nunca pasan a read ni a failed.
  • Una clave de prueba solo ve sus propios mensajes de prueba. GET /messages y GET /messages/{id} los muestran y nada más, y DELETE /messages/{id} cancela uno mientras está queued o scheduled. Las claves reales nunca ven los mensajes de prueba. El filtro status de GET /messages no se admite con claves de prueba y responde test_mode_unsupported; number, since y until funcionan.
  • Las demás escrituras se rechazan con 403 test_mode_unsupported: conectar un número; crear, cambiar o eliminar recordatorios; subir o eliminar medios; iniciar una verificación de números; y añadir, quitar, rotar o probar endpoints de webhook.
  • No se envían eventos de webhook para los mensajes de prueba.
  • Los mensajes de prueba se guardan durante 30 días.

Recursos#

  • Mensajes: envía ahora o más tarde, envía a hasta 1.000 destinatarios, lee el historial, cancela.
  • Números: lista tus números de WhatsApp y cuentas de Telegram, y conecta un número de WhatsApp con código QR o código de vinculación.
  • Recordatorios: mensajes que se repiten según una programación cron.
  • Medios: sube los archivos una vez y envíalos por media_id.
  • Verificaciones de números: averigua qué números tienen WhatsApp.
  • Webhooks: eventos firmados cuando un mensaje se envía, falla, se cancela o caduca.
  • Errores: todos los códigos de error.
  • Pasar de v1 a v2: cada endpoint y campo de v1, y su equivalente en v2.