Para desenvolvedores

Integre em cinco chamadas

API HTTP com JSON, no mesmo contrato da uazapi. Base URL https://api.pontzap.com.br (somente HTTPS). Todos os exemplos têm versão em cURL, JavaScript e Python — a linguagem que você escolher vale para a página inteira.

Início rápido

  1. Crie uma instância

    Use o admintoken da sua conta. A resposta traz o token da instância: guarde-o, ele autoriza todas as outras chamadas desse número.

    curl -X POST 'https://api.pontzap.com.br/instance/create' \
      -H 'admintoken: SEU_ADMINTOKEN' \
      -H 'Content-Type: application/json' \
      -d '{
      "name": "loja-centro",
      "systemName": "meu-erp"
    }'
    Ver resposta
    200 OK
    {
      "response": "Instance created successfully",
      "name": "loja-centro",
      "token": "7f3c2a1e-…-token-da-instancia",
      "instance": {
        "id": "r395f3fbc2622e8",
        "status": "disconnected",
        "…": "…"
      }
    }
  2. Gere o QR code

    Sem corpo, devolve o QR code em instance.qrcode (data URL PNG, pronto para <img src>). Com {"phone": "55…"}, devolve um código de pareamento em instance.paircode.

    curl -X POST 'https://api.pontzap.com.br/instance/connect' \
      -H 'token: SEU_TOKEN_DA_INSTANCIA' \
      -H 'Content-Type: application/json' \
      -d '{}'
    Ver resposta
    200 OK
    {
      "response": "Connecting",
      "loggedIn": false,
      "instance": {
        "status": "connecting",
        "qrcode": "data:image/png;base64,iVBORw0KGgo…",
        "…": "…"
      }
    }
  3. Espere o celular confirmar

    O QR code é renovado a cada poucos segundos. Consulte o estado a cada 2–3 s, mostre o qrcode mais recente e pare quando status.loggedIn for true (chega também o webhook connection).

    curl -X GET 'https://api.pontzap.com.br/instance/status' \
      -H 'token: SEU_TOKEN_DA_INSTANCIA'
    Ver resposta
    200 OK
    {
      "instance": {
        "status": "connected",
        "…": "…"
      },
      "status": {
        "connected": true,
        "loggedIn": true,
        "jid": {
          "user": "5592988887777",
          "agent": 0,
          "device": 12,
          "server": "s.whatsapp.net"
        }
      }
    }

    Exemplo completo de espera pelo QR code:

    // Mostra o QR code e espera o celular confirmar.
    const headers = { token: 'SEU_TOKEN_DA_INSTANCIA' };
    const img = document.querySelector('#qr');
    
    async function aguardarConexao() {
      for (;;) {
        const r = await fetch('https://api.pontzap.com.br/instance/status', { headers });
        const { instance, status } = await r.json();
        if (status.loggedIn) return instance;           // conectado
        if (instance.qrcode) img.src = instance.qrcode; // QR mais recente (data URL PNG)
        await new Promise((ok) => setTimeout(ok, 3000));
      }
    }
  4. Envie uma mensagem

    number com DDI e DDD. Para não perder envio se o número cair, use "async": true (veja Fila assíncrona).

    curl -X POST 'https://api.pontzap.com.br/send/text' \
      -H 'token: SEU_TOKEN_DA_INSTANCIA' \
      -H 'Content-Type: application/json' \
      -d '{
      "number": "5592999999999",
      "text": "Olá! Seu pedido #4821 saiu para entrega."
    }'
    Ver resposta
    200 OK
    {
      "id": "5592988887777:3EB0D8448DC0B9FE593A2C",
      "messageid": "3EB0D8448DC0B9FE593A2C",
      "status": "Sent",
      "…": "…",
      "response": {
        "status": "success",
        "message": "Message sent successfully"
      }
    }
  5. Receba as respostas

    Informe a URL pública do seu sistema e os eventos. A partir daí cada mensagem recebida chega lá como POST JSON.

    curl -X POST 'https://api.pontzap.com.br/webhook' \
      -H 'token: SEU_TOKEN_DA_INSTANCIA' \
      -H 'Content-Type: application/json' \
      -d '{
      "url": "https://meusistema.com.br/webhooks/pontzap",
      "events": [
        "messages",
        "messages_update",
        "connection"
      ],
      "excludeMessages": [
        "wasSentByApi"
      ]
    }'
    Ver resposta
    200 OK
    [
      {
        "id": "wh_db2aff2c34",
        "enabled": true,
        "url": "https://meusistema.com.br/webhooks/pontzap",
        "events": [
          "messages",
          "messages_update",
          "connection"
        ],
        "excludeMessages": [
          "wasSentByApi"
        ],
        "…": "…"
      }
    ]

Autenticação

Toda chamada leva um header com a chave certa. Não há login nem sessão.

HeaderO que éServe para
admintokenChave da sua conta, entregue pela PontZap.Criar e listar instâncias, campos administrativos, trocar token, webhook global.
tokenChave de uma instância (um número), devolvida por /instance/create.Todo o resto: conectar, enviar, buscar, webhooks, grupos.
nenhum—GET /status, /health, /health/ready.
  • Chave ausente ou errada responde 401 com {"code":401,"message":"Invalid token.","data":{}}.
  • Guarde as chaves no servidor (variável de ambiente, cofre). Nunca no front-end, no app ou no repositório.
  • Vazou um token? Troque com POST /instance/token/rotate — o antigo para de valer na hora.

Respostas e erros

Respostas são JSON. Erros vêm como {"error": "mensagem"} (exceto o 401, acima) — use o status HTTP para decidir e a mensagem para registrar.

StatusQuando
200Deu certo. O corpo é JSON.
400Pedido inválido (campo faltando, número sem WhatsApp) ou WhatsApp instance is not connected.
401Token ou admintoken ausente ou errado. Formato próprio: {"code":401,"message":"Invalid token.","data":{}}.
403Grupos/comunidades: o número não tem permissão (não é admin).
404Instância, webhook global ou grupo não encontrado. Em algumas rotas de grupo, também sessão desconectada ou JID inválido.
408/group/list: o WhatsApp não respondeu a tempo.
409/instance/token/rotate sem confirm_impacts: true.
413Corpo ou arquivo grande demais (File too large); descrição de grupo acima de 2048 caracteres.
415/group/updateImage: imagem que não dá para decodificar.
500Erro interno, ou No session em rotas que precisam da instância conectada (privacidade, agenda, bloqueios).
501Rota do contrato uazapi que a PontZap ainda não implementou: {"error":"… (previsto para a Onda N).","code":"not_implemented"}.
400 · instância desconectada
{
  "error": "WhatsApp instance is not connected"
}
501 · rota ainda não implementada
{
  "error": "Endpoint ainda não disponível na PontZap (previsto para a Onda 3).",
  "code": "not_implemented"
}

Fila assíncrona

Acrescente "async": true a qualquer /send/*. A API responde na hora com status: "Queued" e o messageid definitivo; o envio sai pela fila, que fica gravada no banco.

curl -X POST 'https://api.pontzap.com.br/send/text' \
  -H 'token: SEU_TOKEN_DA_INSTANCIA' \
  -H 'Content-Type: application/json' \
  -d '{
  "number": "5592999999999",
  "text": "Seu pedido #4821 saiu para entrega.",
  "async": true,
  "track_source": "erp",
  "track_id": "pedido-4821"
}'
Ver resposta
200 OK
{
  "id": "3EB0D8448DC0B9FE593A2C",
  "messageid": "3EB0D8448DC0B9FE593A2C",
  "chatid": "5592999999999@s.whatsapp.net",
  "fromMe": true,
  "isGroup": false,
  "messageType": "Conversation",
  "source": "api",
  "messageTimestamp": 1791933175740,
  "status": "Queued",
  "text": "Seu pedido #4821 saiu para entrega.",
  "wasSentByApi": true,
  "sendFunction": "/send/text",
  "sendPayload": {
    "number": "5592999999999",
    "text": "Seu pedido #4821 saiu para entrega."
  },
  "track_source": "erp",
  "track_id": "pedido-4821",
  "response": {
    "status": "success",
    "message": "Message queued"
  }
}
  • Ordem garantida: um trabalhador por instância, na ordem de chegada.
  • Número desconectado: a fila espera reconectar, sem gastar tentativas (queue.status: "waiting_connection").
  • Erro do WhatsApp: 3 tentativas (de novo após 5 s e 30 s). Se não der, a mensagem fica Failed e chega o webhook messages com status: "Failed" e error.
  • Reinício do servidor: a fila continua de onde parou.
  • Ritmo: /instance/updateDelaySettings define um intervalo aleatório entre envios; delay (“digitando…”) roda na fila, sem prender a requisição.
  • Acompanhar e cancelar: GET /message/async e DELETE /message/async (as canceladas ficam Canceled).
  • Rastrear: mande track_source/track_id e encontre a mensagem com /message/find.

Eventos de webhook

A PontZap faz POST com JSON na URL configurada em /webhook (ou no webhook global, que recebe de todas as instâncias).

Eventos

EventoQuando chega
connectionO número conectou, desconectou ou caiu. Traz instance.status e, ao desconectar, o motivo.
messagesMensagem nova, recebida ou enviada (inclusive pela API, com wasSentByApi: true). Traz message e chat. Envio da fila que falhou chega aqui com status: "Failed".
messages_updateMudança de estado: entregue, lida, tocada, apagada (Deleted).
historyHistórico que o celular manda ao conectar, em lotes de chats, messages e status.
chatsConversa criada ou alterada (arquivada, fixada, silenciada, lida).
groupsMudanças em grupos avisadas pelo WhatsApp (o evento original vai em event).
newsletter_messagesPosts de canais. Só chega se for assinado pelo nome.
status_postsStatus (stories) dos contatos. Só chega se for assinado pelo nome.

Formato

Todo evento traz o envelope EventType, owner (número da instância), token, BaseUrl e instanceName, mais os campos do evento na raiz.

messages · mensagem recebida
{
  "EventType": "messages",
  "owner": "5592988887777",
  "token": "7f3c2a1e-…-token-da-instancia",
  "BaseUrl": "https://api.pontzap.com.br",
  "instanceName": "loja-centro",
  "message": {
    "id": "5592988887777:3EB0C1A2B3C4D5E6F7",
    "messageid": "3EB0C1A2B3C4D5E6F7",
    "chatid": "5592999999999@s.whatsapp.net",
    "fromMe": false,
    "isGroup": false,
    "messageType": "Conversation",
    "source": "",
    "messageTimestamp": 1791933175740,
    "status": "",
    "text": "Pode entregar depois das 14h?",
    "wasSentByApi": false,
    "sendFunction": "",
    "track_source": "",
    "track_id": "",
    "content": "Pode entregar depois das 14h?",
    "sender": "5592999999999@s.whatsapp.net",
    "senderName": "Maria",
    "owner": "5592988887777"
  },
  "chat": {
    "wa_chatid": "5592999999999@s.whatsapp.net",
    "wa_name": "Maria",
    "wa_unreadCount": 1,
    "…": "…"
  },
  "chatSource": "updated"
}
messages_update · lida
{
  "EventType": "messages_update",
  "owner": "5592988887777",
  "token": "7f3c2a1e-…-token-da-instancia",
  "BaseUrl": "https://api.pontzap.com.br",
  "instanceName": "loja-centro",
  "type": "ReadReceipt",
  "state": "Read",
  "event": {
    "Chat": "5592999999999@s.whatsapp.net",
    "chatid": "5592999999999@s.whatsapp.net",
    "Sender": "5592999999999@s.whatsapp.net",
    "MessageIDs": [
      "3EB0D8448DC0B9FE593A2C"
    ],
    "Timestamp": 1791933240,
    "Type": "Read",
    "IsFromMe": false,
    "IsGroup": false
  }
}
connection · desconectou
{
  "EventType": "connection",
  "owner": "5592988887777",
  "token": "7f3c2a1e-…-token-da-instancia",
  "BaseUrl": "https://api.pontzap.com.br",
  "instanceName": "loja-centro",
  "event_id": "b8f0…",
  "type": "Disconnected",
  "instance": {
    "name": "loja-centro",
    "status": "disconnected",
    "lastDisconnectReason": "disconnected by request",
    "lastDisconnect": "2026-10-13 23:13:01.585Z"
  }
}

Atenção: o envelope inclui o token da instância. Use HTTPS na sua URL e não grave o corpo bruto em logs abertos.

Headers da entrega

  • Content-Type: application/json e User-Agent: PontZap-Webhook/1.0.
  • X-PontZap-Delivery: id da entrega, igual em todas as tentativas. Use para não processar o mesmo evento duas vezes.

Novas tentativas

Sucesso é qualquer resposta 2xx em até 15 segundos. Erro de rede, timeout, 408, 429 e 5xx são tentados de novo — até 6 tentativas no total, com esperas de:

  1. 1ªna hora
  2. 2ª+5 s
  3. 3ª+30 s
  4. 4ª+2 min
  5. 5ª+10 min
  6. 6ª+30 min
  • Outros 4xx (400, 401, 404…) falham na primeira tentativa: corrija o seu endpoint.
  • O que falhar de vez aparece em GET /webhook/errors (últimas 20, com o payload).
  • A fila de entregas fica no banco: um reinício do servidor não perde eventos. Entregas concluídas são apagadas após 24 h; falhas, após 7 dias.
  • A URL precisa ser pública. localhost e IPs de rede interna são recusados — para testar na sua máquina, use um túnel (ngrok, Cloudflare Tunnel).

Receptor mínimo

// Node 18+, sem dependências.
import { createServer } from 'node:http';

const vistos = new Set(); // troque por banco/Redis em produção

createServer(async (req, res) => {
  if (req.method !== 'POST' || req.url !== '/webhooks/pontzap') return res.writeHead(404).end();
  let corpo = '';
  for await (const parte of req) corpo += parte;

  const entrega = req.headers['x-pontzap-delivery']; // igual em todas as tentativas
  res.writeHead(200).end();                         // responda rápido (até 15 s)
  if (vistos.has(entrega)) return;                  // já processado
  vistos.add(entrega);

  const evento = JSON.parse(corpo);
  if (evento.EventType === 'messages' && !evento.message.fromMe) {
    console.log(evento.message.chatid, evento.message.text);
  }
}).listen(3000);

Compatibilidade com a uazapi

Mesmos caminhos, mesmos headers (token, admintoken), mesmos corpos e eventos de webhook. Para migrar uma integração que já funciona:

  1. Troque a base URL para https://api.pontzap.com.br.
  2. Crie as instâncias na PontZap e troque os tokens no seu sistema (eles são diferentes dos da uazapi).
  3. Escaneie o QR code de cada número de novo.
  4. Configure os webhooks (mesmo formato) e confira os primeiros eventos.

Confira se as rotas que você usa estão na referência. As que ainda não chegaram respondem 501 com a onda prevista — veja a lista em Em breve.

Diferenças conhecidas

  • linkPreview*, readchat, readmessages e placeholders como {{name}} são aceitos e ignorados.
  • replyid cita a mensagem só pelo id (sem copiar o conteúdo original).
  • ptt exige o arquivo já em OGG/Opus; /message/download não converte para MP3 (generate_mp3) nem transcreve (transcribe).
  • /message/status responde com os recibos gravados no banco (source: "memory"); não consulta o celular. Vantagem: a uazapi guarda só 24 h em memória.
  • POST /instance/updateInstanceName com nome vazio responde 400.
  • A instância sai com current_presence (schema) e currentPresence (exemplos da documentação deles).
  • message.id sai como owner:messageid, como nos exemplos de webhook da uazapi.
  • /chat/delete com clearChatWhatsApp devolve o erro em errors (limpar sem apagar ainda não é suportado).
  • /chat/totalcount: label_counts e attendant_counts saem vazios (etiquetas e CRM chegam nas próximas ondas).
  • /contact/add e /contact/remove respondem 501 por enquanto.
  • Grupos: sessão desconectada, JID inválido e timeout respondem 404 em rotas cujo contrato não prevê 400/500; /group/list com timeout → 408.
  • Grupos: image_url/picture_* não são preenchidos e participant_profiles sai vazio.
  • Webhook groups: o evento do WhatsApp vai em event (sem amostra real da uazapi para comparar).
  • Vantagem: os webhooks fazem novas tentativas automáticas; a uazapi documenta que não repete entregas que falharam.