API v2 (beta)
API v2
A API v2 é a nova API REST do WbizTool: uma chave por app, só com o acesso de que ele precisa, URLs no estilo de recursos, códigos de status HTTP reais, webhooks JSON assinados e novas tentativas seguras. Esta página cobre o que toda requisição tem em comum. As páginas de recursos cobrem cada endpoint.
URL base#
https://wbiztool.com/api/v2
Os caminhos não têm barra no final (/api/v2/messages), mas uma barra final também é aceita. Um endereço que não existe responde 404 com o código not_found.
Exemplo rápido#
Verifique sua chave com GET /me. Funciona com qualquer permissão e não envia nada:
curl https://wbiztool.com/api/v2/me \
-H "Authorization: Bearer $WBIZTOOL_API_KEY"import os
import requests
response = requests.get(
"https://wbiztool.com/api/v2/me",
headers={"Authorization": f"Bearer {os.environ['WBIZTOOL_API_KEY']}"},
timeout=30,
)
print(response.status_code, response.json())// Node.js 18+ (built-in fetch). Save as .mjs to use top-level await.
const response = await fetch("https://wbiztool.com/api/v2/me", {
headers: { Authorization: `Bearer ${process.env.WBIZTOOL_API_KEY}` },
});
console.log(response.status, await response.json());<?php
$ch = curl_init('https://wbiztool.com/api/v2/me');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('WBIZTOOL_API_KEY')],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = json_decode(curl_exec($ch), true);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n";
print_r($body);
curl_close($ch);{
"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 é null quando o plano envia mensagens ilimitadas. plan é null quando o espaço de trabalho não tem um plano ativo.
Chaves#
Envie sua chave no cabeçalho Authorization em cada requisição:
Authorization: Bearer wbz_live_…
Não existe client_id: a chave identifica o espaço de trabalho dela. Uma chave v2 começa com wbz_live_ ou wbz_test_, seguida de 38 letras e dígitos. Chaves v1 não funcionam na v2 (recebem v1_key_on_v2), e chaves v2 não funcionam na v1.
Criar uma chave#
Os Proprietários criam chaves em Configurações → Chaves de API, no cartão Chaves de API v2: clique em Criar chave v2 e escolha:
- Nome: o app que vai usá-la, até 50 caracteres.
- Tipo de chave: Real envia mensagens de verdade; Teste verifica tudo e não envia nada (veja Modo de teste).
- O que ela pode fazer: as permissões, ou Tudo.
- Números que ela pode usar: todos os números, incluindo os adicionados depois, ou só alguns (veja Chaves limitadas a alguns números).
- Expira: nunca, ou em 30 dias, 90 dias ou 1 ano.
A chave é exibida uma única vez. Copie-a na hora e guarde-a no seu servidor, por exemplo em uma variável de ambiente. O WbizTool guarda apenas um hash dela e os quatro últimos caracteres, então uma chave perdida não pode ser exibida de novo: revogue-a e crie uma nova.
Um espaço de trabalho pode ter até 10 chaves v2 não revogadas. O cartão mostra, para cada chave, os quatro últimos caracteres, as permissões, os números, a expiração e quando e de qual endereço IP ela foi usada pela última vez. Revogar desativa uma chave na hora, e com ela os endpoints de webhook que ela criou. Uma chave com a data de expiração vencida recebe api_key_expired.
Permissões#
Cada chave só pode fazer o que as permissões dela permitem. Uma requisição fora delas responde 403 com missing_scope, e required_scope no erro diz qual permissão é necessária.
| Permissão | O que ela permite |
|---|---|
messages:send | Enviar mensagens e lotes, e cancelar mensagens em espera: POST /messages, POST /messages/batch, DELETE /messages/{id} |
messages:read | Ler mensagens e o status delas (GET /messages, GET /messages/{id}) e gerenciar endpoints de webhook |
reminders | Criar, listar, alterar, pausar e excluir lembretes |
numbers | Listar números, ver a conexão deles e conectar números de WhatsApp |
media | Enviar, listar e excluir arquivos no Diretório de mídia |
contacts | Verificação de números (os endpoints de contatos virão depois) |
GET /me não precisa de nenhuma permissão específica. Enviar para um contato salvo com to.contact_id precisa só de messages:send.
Chaves limitadas a alguns números#
Uma chave limitada a alguns números só funciona com eles:
- Ela envia só desses números e lista, lê e cancela só as mensagens deles. Outro número em
fromrecebenumber_not_allowed. GET /numberslista só esses números. Ela pode reconectá-los, mas não pode adicionar um número novo.- Ela vê e altera só os lembretes que enviam desses números, e um lembrete novo precisa indicar um deles como
from. - Ela não pode gerenciar endpoints de webhook de forma alguma, porque eles recebem os eventos do espaço de trabalho inteiro.
GET /me mostra limited_to_numbers e os number_ids que a chave pode usar.
Requisições#
- JSON na entrada e na saída. Envie os corpos como
Content-Type: application/json. A única exceção é o envio de um arquivo, que émultipart/form-data. Toda resposta é JSON, inclusive os erros. - Os nomes dos campos são
snake_case, e campos desconhecidos são recusados: um erro de digitação comotxtrespondevalidation_failedem vez de ser ignorado. - Números de telefone vão no formato internacional com
+(E.164), por exemplo+919876543210. Espaços e hifens são permitidos; um número sem+é recusado cominvalid_phone. - Horários vão em ISO 8601 com o deslocamento, por exemplo
2026-10-20T09:30:00+05:30ou2026-10-20T04:00:00Z. Um horário sem deslocamento é recusado cominvalid_datetime. Os horários nas respostas estão em UTC e terminam emZ, exceto onext_run_atde um lembrete, que fica no fuso horário do lembrete. - IDs são inteiros, como o
idde um número (fromquando você envia) ou oidde uma mensagem. - Os códigos de status significam o que dizem:
200OK,201criado,202aceito (na fila),400a requisição está errada,401a chave está errada,402não há mensagens restantes,403não permitido,404não encontrado,409conflito,413grande demais,415tipo de conteúdo errado,422um valor não pode ser usado,429requisições demais,5xxproblema do nosso lado.
As respostas trazem Cache-Control: no-store.
Erros#
Todo erro tem o mesmo 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é estável: tome decisões com base nele no seu código.messageé um texto em inglês para o desenvolvedor e pode mudar.fieldindica o campo a que o erro se refere (to.phone,to[3].phone,schedule.cron), ou énull.docsleva ao código na página de erros.request_idé igual ao cabeçalhoX-Request-Id. Informe-o se entrar em contato com a gente.- Alguns erros trazem detalhes, por exemplo
statusemnot_cancellableouusedelimitemstorage_full. Quando uma requisição tem vários problemas,errorslista os outros, cada um comfieldemessage.
Todos os códigos, o status HTTP de cada um e o que fazer estão na página de erros.
Idempotency-Key#
Um erro de rede ou um timeout deixa você sem saber se uma requisição funcionou. Envie um cabeçalho Idempotency-Key com um POST que cria algo, e você pode enviá-lo de novo com segurança:
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."}'
- O valor é seu: qualquer string única de 1 a 255 caracteres imprimíveis, como um UUID ou o ID do seu próprio pedido. Crie um novo para cada requisição nova e reutilize-o só para uma nova tentativa da mesma requisição.
- Uma nova tentativa recebe de volta a primeira resposta, com o cabeçalho
Idempotent-Replayed: true. Nada é feito duas vezes: nenhuma segunda mensagem, lembrete, arquivo ou endpoint. - As chaves duram 24 horas a partir da primeira requisição e pertencem à sua chave de API: outra chave de API do espaço de trabalho pode usar o mesmo valor sem conflito.
- Mesma chave, requisição diferente: uma requisição com uma chave já usada para outro método, caminho ou corpo responde
422idempotency_key_reused. - Ainda em andamento: enquanto a primeira requisição com a chave ainda está sendo processada, uma nova tentativa responde
409idempotency_in_progresscomRetry-After: 1. Espere um instante e envie de novo. - Erros não são guardados. Se a primeira requisição respondeu com um erro, nada fica guardado: corrija a requisição e envie de novo com a mesma chave.
- Segredos não são repetidos. Uma repetição de
POST /webhooksnão inclui osecretdo endpoint, que só aparece na primeira resposta.
Funciona em POST /messages, POST /messages/batch, POST /numbers, POST /reminders, POST /media, POST /number-checks e POST /webhooks, e é opcional em todos. As outras requisições ignoram o cabeçalho. Um valor vazio ou longo demais responde 400 invalid_idempotency_key.
Limites de requisições#
Cada chave pode fazer 60 requisições por minuto e 10.000 requisições por dia. São janelas fixas: a janela do minuto começa no minuto do relógio, e a do dia às 00:00 UTC. Acima de um limite, a requisição responde 429 rate_limited.
As respostas a requisições com uma chave válida trazem estes cabeçalhos:
| Cabeçalho | Significado |
|---|---|
RateLimit-Limit | Requisições permitidas na janela atual (60, ou 10000 depois que o limite diário é atingido) |
RateLimit-Remaining | Requisições restantes nessa janela |
RateLimit-Reset | Segundos até essa janela recomeçar |
RateLimit-Policy | Os dois limites: 60;w=60, 10000;w=86400 |
Retry-After | Em um 429: segundos para esperar antes de tentar de novo |
Além desses:
- Chaves erradas: mais de 30 requisições por minuto com uma chave errada a partir de um endereço IP respondem
429too_many_auth_failurescomRetry-After: 60. - Mensagens por número: um número envia no máximo 1.000 mensagens em 24 horas (uso justo). Um envio acima disso responde
429number_daily_cap_reached.
Paginação#
Listas que podem crescer (GET /messages, /reminders, /media, /number-checks e os /results de uma verificação) vêm em páginas, das mais recentes para as mais antigas:
{
"data": [ … ],
"next_cursor": "eyJpZCI6OTgxNzI2MywidCI6IjIwMjYtMTAtMThUMTA6MTU6MDIuMzExWiIsImYiOiI0NGIxMzZmYTM1NWIifQ"
}
limitdefine o tamanho da página, de 1 a 100 (padrão 25).- Para obter a próxima página, envie a mesma requisição com
cursorigual aonext_cursorda última página.next_cursorénullna última página. - Mantenha os outros filtros iguais: um cursor usado com filtros diferentes responde
400invalid_cursor. Recomece sem cursor. - Trate o cursor como uma string opaca. Itens adicionados enquanto você percorre as páginas não deslocam as páginas.
curl "https://wbiztool.com/api/v2/messages?limit=100&cursor=eyJpZCI6OTgxNzI2MywidCI6..." \
-H "Authorization: Bearer $WBIZTOOL_API_KEY"
GET /numbers e GET /webhooks retornam tudo em uma única lista data.
IDs de requisição#
Toda resposta, inclusive os erros, tem um cabeçalho X-Request-Id como req_4f1c2a9b8e7d6c5b4a392817. Os erros trazem o mesmo valor em request_id. Registre-o e informe-o se entrar em contato com a gente: ele nos permite encontrar a requisição nos nossos logs, que registram o ID da sua chave, mas nunca a chave.
Se você enviar o seu próprio X-Request-Id (até 100 caracteres imprimíveis), ele volta como X-Client-Request-Id, para você relacionar as respostas com os seus próprios logs.
Modo de teste#
Uma chave de teste (wbz_test_…) permite criar e testar uma integração sem enviar nada. Crie uma em Configurações → Chaves de API com Tipo de chave: Teste. GET /me mostra "mode": "test".
O que uma chave de teste faz igual a uma chave real:
POST /messagesePOST /messages/batchverificam tudo o que um envio real verifica: o destinatário, o texto, a mídia,send_ateexpires_at, o número que envia e as permissões e os números da chave. Os mesmos erros recebem as mesmas respostas de erro.- Permissões, números, limites de requisições e
Idempotency-Keyfuncionam do mesmo jeito. - Toda leitura do resto do espaço de trabalho (
GET /me,/numbers,/reminders,/media,/number-checks,/webhooks) mostra os dados reais, somente para leitura.
O que muda:
- Nada é enviado. Um envio de teste fica guardado como mensagem de teste e não usa as mensagens do plano nem as 1.000 diárias do número, então nunca recebe
no_creditsnemnumber_daily_cap_reached. - O status avança sozinho:
queued, depoissent5 segundos mais tarde edelivered15 segundos depois de criada (contados a partir desend_atem uma mensagem agendada). Uma mensagem cujoexpires_atchega antes de ela ser enviada passa aexpired. Mensagens de teste nunca ficamreadnemfailed. - Uma chave de teste vê só as próprias mensagens de teste.
GET /messageseGET /messages/{id}mostram essas mensagens e nada mais, eDELETE /messages/{id}cancela uma enquanto ela estáqueuedouscheduled. Chaves reais nunca veem mensagens de teste. O filtrostatusdeGET /messagesnão é suportado em chaves de teste e respondetest_mode_unsupported;number,sinceeuntilfuncionam. - As outras gravações são recusadas com
403test_mode_unsupported: conectar um número, criar, alterar ou excluir lembretes, enviar ou excluir mídia, iniciar uma verificação de números e adicionar, remover, trocar o segredo ou testar endpoints de webhook. - Nenhum evento de webhook é enviado para mensagens de teste.
- As mensagens de teste ficam guardadas por 30 dias.
Recursos#
- Mensagens: envie agora ou depois, envie para até 1.000 destinatários, leia o histórico, cancele.
- Números: liste seus números de WhatsApp e contas do Telegram e conecte um número de WhatsApp por código QR ou código de login.
- Lembretes: mensagens repetidas em um agendamento cron.
- Mídia: envie os arquivos uma vez e mande-os por
media_id. - Verificação de números: descubra quais números estão no WhatsApp.
- Webhooks: eventos assinados quando uma mensagem é enviada, falha, é cancelada ou expira.
- Erros: todos os códigos de erro.
- Migrar da v1 para a v2: cada endpoint e campo da v1 e o equivalente na v2.
