Visão geral
Convenções da API REST do PEPINO ZAP — base URLs e modos teste/produção, autenticação por API key, formato do campo to (E.164/JID), envelope de erro, envio assíncrono e índice de endpoints.
Esta é a referência da API REST do PEPINO ZAP. Com ela o seu sistema conecta números de WhatsApp (instâncias) e envia mensagens de forma programática. Cada endpoint da referência traz exemplos de requisição em várias linguagens e um playground "Try it" para testar a chamada direto na página.
Esta página de visão geral reúne as convenções comuns a toda a API — as regras que valem para qualquer endpoint: como autenticar, em que ambiente você está (teste ou produção), como escrever o destinatário, como o sucesso e o erro são representados e por que o envio é assíncrono. Leia-a uma vez antes de mergulhar nos endpoints; ela evita a maioria dos tropeços de integração.
Se você ainda não gerou uma API key nem conectou um número, comece pelos guias passo a passo: Autenticação (criar a conta e gerar a key) e Sua primeira instância (conectar um número via QR Code). Esta página é a referência das convenções; aqueles guias são o caminho guiado.
Antes de começar: os três pré-requisitos
Para uma chamada de uso real (criar instância ou enviar mensagem) ser aceita, três coisas precisam estar certas ao mesmo tempo:
- API key válida no header
Authorization(veja Autenticação). Sem ela:401. - Host correto para o modo da key — o host
sandbox.*é Teste e exigepzk_test_; o hostapi.*é Produção e exigepzk_live_. Key e host têm que casar. Se não casarem:401. - Plano ativo no Projeto (só em Produção) — criar instâncias e enviar mensagens exigem assinatura ativa. Sem ela:
402. No modo Teste é grátis (com teto diário).
Leitura (listar/detalhar), pareamento por QR Code e "marcar como lida" só exigem os pré-requisitos 1 e 2 — não dependem de plano.
Base URL e o prefixo /v1
Todas as rotas vivem sob o prefixo /v1. A URL completa de um endpoint é sempre <BASE_URL>/v1/<caminho>. A base URL define em qual modo (Teste ou Produção) você está operando:
| Modo | Base URL | Prefixo da key | Cobrança |
|---|---|---|---|
| Teste (sandbox) | https://sandbox.pepinozap.com.br | pzk_test_ | grátis, com teto de 100 mensagens/dia por Projeto |
| Produção (live) | https://api.pepinozap.com.br | pzk_live_ | exige plano ativo |
É o host que decide o modo: qualquer host que comece com sandbox é Teste; os demais (api.*) são Produção. A API key não decide o modo — ela apenas precisa ser coerente com o host. Por isso uma pzk_test_ chamando api.* (ou uma pzk_live_ chamando sandbox.*) é rejeitada com 401, mesmo sendo uma key real e válida.
# Teste (sandbox) — key pzk_test_
curl https://sandbox.pepinozap.com.br/v1/instances \
-H "Authorization: Bearer pzk_test_xxxxxxxxxxxxxxxx"
# Produção (live) — key pzk_live_
curl https://api.pepinozap.com.br/v1/instances \
-H "Authorization: Bearer pzk_live_xxxxxxxxxxxxxxxx"Os dois modos são 100% isolados: instâncias, conversas e webhooks criados
em Teste nunca aparecem em Produção e vice-versa. Inclusive um id de
instância de um modo, usado no outro modo, retorna 404 (não vaza entre
ambientes). Valide a integração inteira em sandbox.* sem custo e, quando
estiver pronto, troque a base URL para api.* e a key para pzk_live_ — o
resto do código permanece igual.
Autenticação
Toda chamada envia a API key no header Authorization com o esquema Bearer:
Authorization: Bearer <API_KEY>A key é escopada a um Projeto: todas as instâncias e mensagens criadas ou listadas com ela pertencem a esse Projeto. A geração é self-service no painel (Integrações → API Keys), e a key crua aparece uma única vez na criação. Detalhes de cadastro, do toggle Teste/Produção e de revogação estão em Autenticação.
Exceção — SSE de QR Code. O único endpoint que não usa o header é o stream de QR Code via Server-Sent Events (GET /v1/instances/{id}/qr), porque o EventSource do navegador não envia headers customizados. Nele a key vai pela query string, ?token=<API_KEY>. Recomenda-se emitir um token efêmero (POST /v1/instances/{id}/qr-token, válido ~2 min) para não expor a key crua na URL. Todos os demais endpoints — inclusive o polling de QR (/qr/poll) — usam o header normalmente. Veja Autenticação → Fallback ?token= e Sua primeira instância → Parear via QR Code.
Formato do campo to
Todos os endpoints de mensagem identificam o destinatário pelo campo to. Ele aceita três formatos, todos equivalentes — use o que for mais conveniente:
| Formato | Exemplo | Observação |
|---|---|---|
Apenas dígitos (E.164 sem +) | 5511999999999 | Forma mais simples; recomendada. |
E.164 com + | +5511999999999 | O + é aceito. |
| JID completo | 5511999999999@s.whatsapp.net | Forma "nativa" do WhatsApp. |
O número deve incluir código do país e DDD, sem espaços, traços ou parênteses. Para o Brasil, o código do país é 55.
A API não verifica, no momento do envio, se o número existe no WhatsApp —
a validação só confirma que o campo to não está vazio. Um to com número
inexistente é aceito e enfileirado (resposta 202), mas a mensagem
simplesmente não é entregue, e não há evento de falha por número inválido.
Garanta que o to está correto antes de enviar. Para envio a grupos, use
o JID de grupo recebido nos webhooks; este guia trata de conversas individuais
(1:1).
Convenções de requisição e resposta
- Método e corpo. Endpoints de escrita usam
POST/PATCH/DELETE; os de leitura usamGET. Requisições com corpo JSON exigem o headerContent-Type: application/json(a exceção é o upload de mídia por arquivo, que émultipart/form-data). - Formato. Pedidos e respostas são JSON (
UTF-8), salvo o SSE de QR (text/event-stream) e o upload de mídia (multipart). - Códigos de sucesso por tipo de operação:
| Operação | Sucesso | Corpo da resposta |
|---|---|---|
Criar instância (POST /v1/instances) | 201 Created | A instância criada (objeto completo). |
| Detalhar / listar / atualizar settings | 200 OK | A instância (ou a lista). |
Remover instância (DELETE) | 204 No Content | Sem corpo. |
| Enviar qualquer tipo de mensagem | 202 Accepted | Confirmação de enfileiramento (veja abaixo). |
- Confirmação de envio. Os endpoints de envio respondem com um objeto curto que confirma o enfileiramento, não a entrega:
{
"queued": true,
"to": "5511999999999"
}Endpoints de mídia e sticker incluem também o campo type (ex.: "type": "image"). O to ecoado é exatamente o que você enviou.
Envio assíncrono (202) e idempotência
O envio de mensagens é assíncrono. A resposta 202 Accepted confirma apenas que a mensagem foi enfileirada — não que foi entregue nem lida. O processamento real (envio ao WhatsApp) ocorre logo em seguida, em segundo plano. A confirmação de entrega e leitura chega depois pelo evento message.status no webhook.
A instância precisa estar CONNECTED para a mensagem sair. Enviar para
uma instância que não está conectada (por exemplo, DISCONNECTED ou aguardando
QR) também retorna 202 — mas a mensagem fica em espera e não é
entregue, sem gerar evento de falha. Confirme status == CONNECTED (via
GET /v1/instances/{id} ou GET /v1/instances/{id}/qr/poll com
connected == true) antes de enviar. Instâncias em estado terminal
(BANNED/ERROR) são bloqueadas já no envio, com 409 (veja a tabela de
erros).
Idempotência. Os endpoints de envio não são idempotentes: cada chamada enfileira um novo envio. Se a sua requisição falhar com erro de rede ou timeout depois de a API ter aceitado o job, repeti-la pode resultar em mensagem duplicada. Para reenviar com segurança, controle a deduplicação no seu lado (por exemplo, só repita quando tiver certeza de que a chamada anterior não retornou 202). Já os erros 402 (plano/créditos), 401, 403, 404, 409 e 429 são respostas de negócio/validação — não os repita em loop: corrija a causa (assinar, recarregar, conectar a instância, corrigir a key) e tente de novo.
Envelope de erro
Toda resposta de erro segue um envelope único e estável:
{
"error": {
"code": "BAD_REQUEST",
"message": "campos to e body são obrigatórios"
}
}O campo code identifica a categoria de forma estável — programe o seu tratamento de erros em cima dele. O campo message é apenas legível por humanos (em português) e pode mudar; não faça parsing nem switch sobre a message, com uma exceção prática anotada na tabela (o 402 de plano vs. créditos).
| Status | code | Significado |
|---|---|---|
400 | BAD_REQUEST | Corpo JSON inválido ou campo obrigatório faltando/ inválido (ex.: to vazio, type de mídia inválido). |
401 | UNAUTHORIZED | API key ausente, inválida, ou do modo errado para o host (pzk_live_ no sandbox, ou vice-versa). |
402 | PAYMENT_REQUIRED | Sem plano ativo ou sem créditos da API (ver abaixo). Distinga os dois casos pela message. Não repita a chamada — exige ação humana (assinar/recarregar). |
403 | FORBIDDEN | Projeto inativo ou sem permissão. |
404 | NOT_FOUND | Recurso não encontrado no Projeto da key — inclui o caso de a instância existir, mas pertencer a outro modo (teste/produção) ou a outro Projeto. |
409 | CONFLICT | Ao criar instância: cota de números do plano atingida. Ao enviar: instância não-operacional (BANNED ou ERROR). |
429 | RATE_LIMITED | Apenas no sandbox: teto diário do Projeto atingido (100 mensagens/dia por Projeto, enviadas + recebidas; o contador zera à meia-noite no horário de São Paulo). Em produção não ocorre. |
5xx | INTERNAL_ERROR | Falha inesperada do servidor. Em geral é transitória — pode ser repetida com recuo (backoff). |
O 404 é deliberadamente "cego" ao motivo: tentar acessar uma instância de
outro modo (ex.: um id de produção usando a base URL de sandbox) ou de outro
Projeto retorna 404, e não um 403. Isso evita vazar a existência de
recursos entre Projetos e entre ambientes. Se um id que você "tem certeza"
que existe dá 404, confira primeiro se a base URL e o prefixo da key batem
com o modo em que a instância foi criada.
Pré-requisito: plano ativo (402)
Além da API key, o Projeto precisa de uma assinatura ativa para criar instâncias e enviar mensagens em produção. Sem plano ativo, esses endpoints retornam 402 (PAYMENT_REQUIRED):
{
"error": {
"code": "PAYMENT_REQUIRED",
"message": "É necessário um plano ativo para usar este recurso. Assine em \"Meu plano\"."
}
}Endpoints que exigem plano ativo (em produção):
POST /v1/instances— criar instânciaPOST /v1/instances/{id}/messagese todos os demais envios (/media,/media-upload,/location,/contact,/sticker,/reaction,/poll)
Endpoints de leitura (listar/detalhar instâncias), o pareamento por QR Code (SSE, polling e emissão de token) e o marcar como lida (/messages/read) continuam liberados mesmo sem plano.
Modo Teste é grátis. Em sandbox.* a assinatura não é exigida — você só esbarra no teto diário (429). Use o sandbox para validar a integração inteira sem custo.
Créditos da API (também 402). Projetos no modo API consomem créditos pré-pagos a cada mensagem enviada pela API key (envios pelo painel/inbox não consomem). Quando o saldo zera, os endpoints de envio retornam o mesmo 402 PAYMENT_REQUIRED, mas com a message indicando créditos insuficientes (ex.: "créditos da API insuficientes — recarregue em Meu plano"). É por isso que, para o 402, a message é o único sinal que distingue os dois casos: falta de plano versus falta de créditos.
A resolução de ambos (assinar ou recarregar) é em Meu plano, no painel — é um gate de negócio que exige ação humana, não repita a chamada. Projetos de cortesia (isentos, marcados pelo time PEPINO) nunca recebem este erro. Detalhes em Autenticação → Plano ativo.
Como navegar a referência
Os endpoints abaixo agrupam-se em quatro áreas. Cada link leva à página do endpoint, com todos os campos, exemplos por linguagem e o playground "Try it".
Instâncias
Conectar e gerenciar os números de WhatsApp do Projeto.
| Operação | Método e caminho | Referência |
|---|---|---|
| Listar instâncias | GET /v1/instances | listInstances |
| Criar instância | POST /v1/instances | createInstance |
| Detalhar instância | GET /v1/instances/{id} | getInstance |
| Atualizar configurações | PATCH /v1/instances/{id}/settings | updateInstanceSettings |
| Remover instância | DELETE /v1/instances/{id} | deleteInstance |
| Parear via QR Code (SSE) | GET /v1/instances/{id}/qr | streamInstanceQR |
| Parear via QR Code (polling) | GET /v1/instances/{id}/qr/poll | pollInstanceQR |
| Token efêmero para o SSE de QR | POST /v1/instances/{id}/qr-token | createQrToken |
O ciclo de vida da instância e o pareamento estão detalhados em Sua primeira instância.
Mensagens
Todos os envios são por POST /v1/instances/{id}/messages/..., respondem 202 e são assíncronos.
| Tipo | Caminho | Referência |
|---|---|---|
| Texto | POST .../messages | sendTextMessage |
| Mídia (por URL) | POST .../messages/media | sendMediaMessage |
| Mídia (upload de arquivo) | POST .../messages/media-upload | sendMediaUpload |
| Localização | POST .../messages/location | sendLocationMessage |
| Contato | POST .../messages/contact | sendContactMessage |
| Sticker | POST .../messages/sticker | sendStickerMessage |
| Reação | POST .../messages/reaction | sendReactionMessage |
| Enquete | POST .../messages/poll | sendPollMessage |
| Marcar como lida | POST .../messages/read | markMessagesRead |
Notas úteis: em mídia, o campo type aceita image, video, audio ou document, e um áudio com ptt: true vira nota de voz; em reação, emoji vazio remove a reação; em enquete, options deve ter de 2 a 12 alternativas. Os campos exatos de cada tipo estão na página da referência correspondente.
Webhooks
O integrador recebe eventos (mensagem recebida, status de envio e status da instância) em uma URL própria, cadastrada via API (self-service):
| Operação | Método e caminho | Referência |
|---|---|---|
| Cadastrar webhook | POST /v1/webhooks | createWebhook |
| Listar webhooks | GET /v1/webhooks | listWebhooks |
| Remover webhook | DELETE /v1/webhooks/{id} | deleteWebhook |
O formato do payload, a assinatura HMAC e a lista de eventos estão em Webhooks. Para receber eventos em tempo real por conexão persistente em vez de HTTP, veja também o WebSocket.
Inbox (CRM)
Consultar as conversas e mensagens armazenadas pela plataforma e gerir o atendimento (status, atribuição):
| Operação | Método e caminho | Referência |
|---|---|---|
| Listar conversas | GET /v1/inbox/conversations | listInboxConversations |
| Detalhar conversa | GET /v1/inbox/conversations/{id} | getInboxConversation |
| Listar mensagens da conversa | GET /v1/inbox/conversations/{id}/messages | listInboxMessages |
| Mudar status (encerrar/reabrir) | POST /v1/inbox/conversations/{id}/status | setConversationStatus |
| Atribuir a um atendente | POST /v1/inbox/conversations/{id}/assign | assignConversation |
Gestão (CRM)
As ferramentas de gestão do atendimento. O modelo de dados e o passo a passo de cada uma estão no guia CRM e gestão:
| Área | O que faz | Referência |
|---|---|---|
| Relatórios | Métricas agregadas do atendimento | getReportsSummary · guia |
| Etiquetas | Classificar e filtrar conversas | listLabels · guia |
| Justificativas | Motivos de encerramento | listCloseReasons |
| Fluxos | Construtor de chatbot por número | listFlows · guia |
| Funil | Funil cujas etapas são regras sobre a conversa | listFunnels · guia |
Próximos passos
- Autenticação — gerar a API key e entender os modos teste/produção.
- Sua primeira instância — conectar um número via QR Code.
- Enviar uma mensagem de texto — o primeiro envio após o pareamento.
- Webhooks — receber eventos de mensagens e de status da instância.
- CRM e gestão — relatórios, etiquetas, encerramento, equipe e o construtor de chatbot.