Chamar a API
Com o access token de uma instalação, o app chama https://api.botzetta.com/plataforma/v1 em nome
daquela empresa. A empresa vem do token: não há cabeçalho para escolher outra.
GET https://api.botzetta.com/plataforma/v1/usuarios
Authorization: Bearer <access token da empresa>
Accept: application/json
{ "dados": [{ "id": 7, "nome": "Ana Souza", "perfil": "admin" }] }
Cada endpoint, com parâmetros, respostas e o escopo que exige, está na
Referência da API. Neste guia, cada endpoint citado leva o operationId da
especificação OpenAPI entre parênteses, com link para a página dele, por exemplo GET /usuarios
(getUsuarios).
Convenções
| Item | Como é |
|---|---|
| Formato | JSON (upload de mídia em multipart/form-data) |
| Nomes | Recursos no plural, em português (/pessoas, /oportunidades); campos em camelCase |
| Datas | ISO 8601, em UTC |
| Paginação | ?pagina=1&porPagina=50 (máximo 200) → { dados, pagina, porPagina, total } |
| Histórico de mensagens | Por cursor: ?limite= e ?cursor= → { dados, proximoCursor }. Repasse o proximoCursor como veio; null é o fim |
| Rastreio | Mande X-Request-Id e ele volta na resposta. Inclua-o quando falar com o suporte |
| Compatibilidade | Mudanças na v1 são aditivas: ignore campos que não conhece |
Erros
Todo erro tem o mesmo formato:
{ "error": "ERR_ESCOPO_INSUFICIENTE", "message": "texto em português" }
Trate pelo código (error), não pela mensagem. Os mais comuns:
| Código | HTTP | O que fazer |
|---|---|---|
ERR_TOKEN_INVALIDO | 401 | Renove o token e repita uma vez. Se voltar, a instalação acabou |
ERR_APP_NAO_HABILITADO | 403 | A empresa desinstalou o app, o plano não permite mais ou o app foi suspenso. Pare de chamar para essa empresa |
ERR_ESCOPO_INSUFICIENTE | 403 | O escopo não está entre os efetivos. Peça na próxima versão; a empresa precisa consentir |
ERR_EMPRESA_DIVERGENTE | 400 | Você mandou X-Botzetta-Empresa diferente da empresa do token. Não mande o cabeçalho |
ERR_ASSINATURA_VENCIDA / ERR_ASSINATURA_BLOQUEADA | 403 | A assinatura da empresa está pendente: escritas são recusadas, leituras continuam |
ERR_NAO_ENCONTRADO | 404 | Não existe ou é de outra empresa (a API não distingue) |
ERR_VALIDACAO | 400 | Corpo ou consulta inválidos; message diz o campo |
ERR_CONFLITO | 409 | Conflito de estado. Pode tentar de novo depois |
ERR_OPERACAO_RECUSADA | 403 | Uma regra do Botzetta recusou a operação. Não é token nem escopo: não renove |
ERR_LIMITE_REQUISICOES | 429 | Espere o Retry-After (segundos) |
ERR_INTERNO | 500 | Tente de novo com espera crescente |
A lista completa fica em Erros, na Referência da API.
Limites
Detalhes e cabeçalhos em Limites e idempotência.
- 600 requisições por minuto por app e por empresa. Acima disso,
429 ERR_LIMITE_REQUISICOEScomRetry-After. - Envio de mensagem para número avulso (fora do cadastro de pessoas): até 200 por dia por app e
empresa (
429 ERR_LIMITE_ENVIO_AVULSO). Envios porpessoaIdnão contam: prefira-os. - Prefira webhooks a consultar a API em intervalos. Para sincronizar, use os filtros
atualizadoDesdedas listagens de tickets e oportunidades.
Idempotência
Todo POST que cria um registro ou envia uma mensagem exige o cabeçalho Idempotency-Key
(até 7 dias, por empresa e por rota):
POST https://api.botzetta.com/plataforma/v1/mensagens
Authorization: Bearer <access token da empresa>
Content-Type: application/json
Idempotency-Key: pedido-9812-confirmacao
{ "whatsappId": 12, "destino": { "pessoaId": 345 }, "conteudo": { "texto": "Seu pedido 9812 foi confirmado." } }
- Mesma chave e mesmo corpo → a mesma resposta, sem repetir o efeito, com
Idempotency-Replay: true. - Mesma chave com corpo diferente →
422 ERR_IDEMPOTENCY_KEY_REUTILIZADA. - Chave ainda em processamento →
409 ERR_REQUISICAO_EM_ANDAMENTO. - Respostas 5xx, 409 e 429 não são gravadas: a mesma chave serve para tentar de novo.
Derive a chave do seu evento de negócio (ex.: pedido-9812-confirmacao), nunca do número da
tentativa: se a primeira tentativa der timeout depois que a mensagem saiu, a segunda recebe o replay em
vez de enviar de novo. PATCH, PUT e ações de estado (mover, ganhar, perder, reabrir, opt-out) não
exigem a chave, porque repetir dá o mesmo resultado.
Enviar mensagens
POST /mensagens (postMensagens, escopo mensagens:enviar) envia pelo WhatsApp da empresa:
destino:{ "pessoaId": 345 }(recomendado),{ "contatoId": 77 }(contato do atendimento, inclusive grupo) ou{ "numero": "5511999990000" };- pessoa ou número com opt-out →
422 ERR_DESTINO_OPT_OUT. É resultado final (LGPD): não tente de novo e mostre ao cliente quem ficou de fora; 409 ERR_WHATSAPP_DESCONECTADOpode ser tentado de novo mais tarde;422 ERR_DESTINO_SEM_WHATSAPPnão.
As conexões de WhatsApp da empresa estão em GET /whatsapps (getWhatsapps).
Exemplo: ler pessoas
async function listarPessoas(accessToken, pagina = 1) {
const resp = await fetch(`https://api.botzetta.com/plataforma/v1/pessoas?pagina=${pagina}&porPagina=100`, {
headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/json" }
});
const corpo = await resp.json();
if (!resp.ok) throw new Error(`${corpo.error}: ${corpo.message}`);
return corpo; // { dados: [{ id, nome, … }], pagina, porPagina, total }
}
GET /pessoas (getPessoas) exige pessoas:ler; o detalhe com os contatos de WhatsApp é
GET /pessoas/{id} (getPessoasId).
Tudo o que o app lê é dado pessoal de clientes da empresa (LGPD). Guarde só o necessário, não ponha em logs e apague quando a empresa desinstalar o app. Veja Obrigações.