API v2 (beta)
Pasar de v1 a v2
La API v2 hace lo mismo que la v1, con claves más seguras, un solo estilo de URL, códigos de estado HTTP reales y webhooks JSON firmados. Esta página relaciona cada endpoint y campo de la v1 con su equivalente en la v2. Lo que es nuevo en la propia v2 está en el resumen.
Lo que cambia en todas partes#
- Claves: el
client_id+api_keyde la v1 se convierten en una sola cabecera,Authorization: Bearer wbz_live_…. Crea una clave v2 en Configuración → Claves API, en la tarjeta Claves API v2, solo con los permisos que necesite la app. Las claves v1 no funcionan en la v2, y las claves v2 no funcionan en la v1. - Solo JSON: sin campos de formulario. Los archivos se suben a
POST /media(multipart) y se envían conmedia_id. - Métodos y URL:
GETpara leer,POSTpara crear,PATCHpara cambiar,DELETEpara cancelar o eliminar, bajohttps://wbiztool.com/api/v2. - Códigos de estado HTTP en lugar de
"status": 0o1(consulta Respuestas y errores). - Números de teléfono en formato internacional con
+, sincountry_code. - Horas en ISO 8601 con desfase, en lugar de campos separados de fecha, hora y zona horaria.
- Webhooks: son endpoints que añades una sola vez para el espacio de trabajo, con eventos JSON firmados, en lugar de una URL
webhooken cada mensaje.
Endpoint por endpoint#
Las rutas de v2 son relativas a https://wbiztool.com/api/v2.
Enviar, campo por campo#
| v1 | v2 | Notas |
|---|---|---|
client_id + api_key (cuerpo o Authorization: Bearer) | Authorization: Bearer wbz_live_… | Una clave v2 nueva; sin client_id |
phone + country_code | to.phone | Un número en E.164 con +: "9876543210" con "country_code": "91" pasa a ser {"phone": "+919876543210"}. No se añade ningún código de país por ti |
phone con un @username (Telegram) | to.username | @name, t.me/name o tg: y un id, desde una cuenta de Telegram |
group_name | to.group | El nombre exacto del grupo |
| (no existe en v1) | to.contact_id | Un contacto guardado |
whatsapp_client | from | El id del número; opcional cuando hay un solo número conectado, como antes |
msg (o message) | text | Hasta 3.000 caracteres, como antes |
msg_type 0, 1, 2 | ninguno | Un mensaje con media es un mensaje multimedia; media.type (image o file) cuando el enlace no lo indica |
img_url / file_url | media.url | Un enlace público http(s) |
file (subido en la misma solicitud) | media.media_id | Súbelo primero y luego envía el id tantas veces como quieras |
file_name | media.file_name | Para archivos |
date + time + timezone de schedule_msg/ | send_at | Una sola hora con su desfase: 24/12/2026, 10:30 y Asia/Kolkata pasan a ser "2026-12-24T10:30:00+05:30" |
expire_after_seconds | expires_at | Una hora, no una duración: ahora más los segundos, con su desfase |
webhook | endpoints de webhook | Se añaden una sola vez con POST /webhooks, para todos los mensajes del espacio de trabajo |
msg_id en la respuesta | id | Se devuelve el propio Message, con su status |
El mismo envío en v1 y en v2:
# v1
curl -X POST https://wbiztool.com/api/v1/send_msg/ \
-H "Authorization: Bearer YOUR_V1_KEY" \
-H "Content-Type: application/json" \
-d '{"whatsapp_client": 101, "msg_type": 1, "country_code": "91", "phone": "9876543210",
"img_url": "https://example.com/offers/diwali-sale.jpg", "msg": "Our Diwali sale starts today",
"expire_after_seconds": 3600}'
# v2
curl -X POST https://wbiztool.com/api/v2/messages \
-H "Authorization: Bearer $WBIZTOOL_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: diwali-9876543210" \
-d '{"from": 101, "to": {"phone": "+919876543210"},
"media": {"url": "https://example.com/offers/diwali-sale.jpg"}, "text": "Our Diwali sale starts today",
"expires_at": "2026-10-20T11:30:00+05:30"}'
Algunos comportamientos de la v1 desaparecen a propósito:
- No se adivinan códigos de país. La v1 ponía
country_codedelante de un número (con91, siempre delante de uno de 10 dígitos). La v2 toma el número tal como está escrito, con+. - No se descarga el archivo al llamar. La v1 descargaba
img_urlyfile_urldurante la solicitud. La v2 comprueba que el enlace es público y descarga el archivo cuando se envía el mensaje; un enlace incorrecto aparece como un mensajefailed. - No se guarda una fila fallida al rechazar. Cuando la v1 rechaza un envío por superar los 1.000 diarios, igualmente guarda el mensaje como fallido. La v2 no guarda nada cuando rechaza un envío.
- Los campos desconocidos se rechazan en lugar de ignorarse.
Estados#
Los números y palabras de la v1 se convierten en un solo campo status:
| v1 | status de v2 |
|---|---|
0 Created / Pending | queued, o scheduled mientras su send_at está por llegar |
1 Sent | sent, delivered o read (los tics de WhatsApp) |
2 Failed | failed |
3 Cancelled | cancelled |
4 Expired | expired |
Los eventos de webhook son message.sent, message.failed, message.cancelled y message.expired, que corresponden a SENT, FAILED, CANCELLED y EXPIRED de la v1.
Respuestas y errores#
La v1 responde a muchos errores con HTTP 200 o 400 y "status": 0, y a los éxitos con "status": 1. La v2 usa el código de estado HTTP:
- Éxito es
200,201(creado) o202(en cola), y el cuerpo es el propio objeto, como el Message. No hay envoltoriostatus: 1. - Errores son
4xxo5xxcon una sola forma:{"error": {"code", "message", "field", "docs", "request_id"}}. Decide segúncode, no según elmessageen inglés. Todos los códigos están en la página de errores.
# v1
result = response.json()
if result.get("status") == 1:
msg_id = result["msg_id"]
# v2
if response.status_code == 202:
message_id = response.json()["id"]
else:
error = response.json()["error"]
if error["code"] == "number_daily_cap_reached":
... # try again tomorrow
Algunos mensajes de la v1 y sus códigos en la v2:
| Mensaje de v1 | Código de v2 |
|---|---|
Auth Error: invalid api key | 401 invalid_api_key, api_key_revoked o api_key_expired |
Invalid phone number / Invalid Contact Number "…" | 422 invalid_phone |
Msg cant be null | 400 text_required |
Message length is too long | 400 text_too_long |
Invalid whatsapp client | 422 number_not_found |
Invalid whatsapp client id. | 422 from_required |
Not enough credits | 402 no_credits |
Daily limit reached: … | 429 number_daily_cap_reached |
Demo Account can not access apis | 403 demo_account |
Account Disabled | 403 account_disabled |
Invalid JSON format: … | 400 invalid_json |
Webhooks#
En la v1, cada mensaje puede llevar una URL webhook que recibe un msg_id=…&status=SENT codificado como formulario. En la v2:
- Añade un endpoint una sola vez con
POST /webhooksy guarda susecret. - Recibe eventos JSON de todos los mensajes del espacio de trabajo, con el Message completo en
data.message. Relaciónalos con tus registros pordata.message.id. - Comprueba la firma: el mismo esquema que en la v1 (
WbizTool-Timestamp,WbizTool-Signature: v1=…), pero con el secreto propio del endpoint en lugar del del espacio de trabajo. - Descarta los repetidos con
WbizTool-Event-Id. Los reintentos continúan durante unas 23 horas, no 4 intentos en pocos minutos como en la v1, y las respuestas4xxtambién se reintentan.
Los eventos de conexión de la v1 (webhook_url en whatsapp/connect/) aún no tienen evento en la v2: consulta GET /numbers/{id} en su lugar.
Lista de comprobación#
Crea una clave v2 por app
En Configuración → Claves API, solo con los permisos que necesite. Pruébala primero con una clave de prueba, que lo comprueba todo y no envía nada.
Cambia las solicitudes
URL y métodos nuevos, cuerpos JSON,
to/from/text/media, números de teléfono con+, horas con desfase. Añade unaIdempotency-Keya cada envío.Cambia el manejo de errores
Comprueba el estado HTTP y luego
error.code. Gestiona429esperando lo que indiqueRetry-After.Pasa los webhooks
Añade un endpoint, comprueba las firmas con su secreto y descarta los repetidos por id de evento.
Cambia y revoca
Cuando la app funcione con la v2, elimina su clave v1 en Configuración → Claves API.
