Changelog
Histórico de mudanças relevantes na API REST e nos webhooks do wpp-gateway.
A API segue versionamento de URL: /v1/*. Breaking changes promovem para /v2/*. Adições não-breaking continuam em /v1/*.
Unreleased
Mudanças no main desde a v0.1.0. O que está no ar agora: GET /versao.
Adicionado
buttonsno envio de template (POST /v1/messages,type: "template"). Parâmetros dos botões que levam valor no envio:url(sufixo da URL dinâmica e código do botão de OTP),copy_code(código do cupom) equick_reply(payload). Ver Mensagens → template.GET /versao— o commit que a instância serve ({ servico, commit, subidoEm }), sem autenticação. Serve para conferir versão contra versão antes de publicar um produto que depende de capacidade nova do gateway;/healthsó diz que o processo está de pé.POST /v1/phones/:id/logout— reinicia o pareamento. Descarta a credencial e, para Baileys, reabre a sessão pedindo QR no mesmo comando: mesmo id, mesmo webhook. É a saída para o canal preso com credencial obsoleta (403,411,440e o loop de503preservam a identidade, e nenhum QR é emitido), que antes só se resolvia apagando o phone. Não é preciso encadearreconnect: ologoutjá reabre, e umreconnectconcorrente se junta ao reset.GET /v1/phones/:id/qrcoderesponde425 PAIRING_IN_PROGRESScom o phone emCONNECTING(socket subindo com credencial). Antes, nessa janela, o endpoint servia um QR já consumido — e o celular recusava com "verifique sua conexão". Trate como "aguarde". Passando de ~1 minuto, ofereça ao usuário oPOST /:id/logout— nunca automático: uma reconexão legítima também passa disso, e ologoutdesvincula o aparelho.channel:disconnectedcomreason: "PAIRING_RESET"quando o pareamento é reiniciado pelo/logout. Antes o reset saía comoMANUAL_DELETE, o mesmo motivo doDELETEdo phone, e as duas coisas não se distinguiam nem na auditoria nem para quem mostra o motivo na tela.
Mudado
- Canal pareado reconectando aparece como
CONNECTING, não maisPENDING_QR. O gateway decidia "tem credencial?" porcreds.registered, que no Baileys 7 só viratrueno pareamento por código — todo reconnect de canal pareado por QR era reportado como "aguardando QR". Quem alerta emPENDING_QRdeixa de ver falso positivo a cada reconexão. message:statusfailedagora identifica o erro.errors[0].codepassa a trazer o código da Meta quando existe (132018,131047,131026, …) em vez de sempre0, eerrors[0].titlepassa a trazer o código semântico do gateway (META_BAD_REQUEST,INVALID_PTT_FORMAT, …) em vez da string fixa'SEND_FAILED'. Quem detectava falha portitle === 'SEND_FAILED'precisa usarstatus === 'failed', e quem for rotear deve usarcode:titlecontinua sendo o título textual da Meta quando a falha chega pelo webhook de status dela. Formato do array inalterado.- Recusa determinística da Meta não é mais retentada. Erros de envio com
metaError.codeem{132000, 132005, 132012, 132015, 132016, 132018, 131047, 131026}falham na 1ª tentativa.132001ficou de fora de propósito: é a janela em que a Meta ainda não propagou a aprovação, e nela o retry resolve. Antes queimavam 5 tentativas (~2 min) para chegar ao mesmoFAILED— que agora chega em segundos. Para esses erros ofailedReason(GET /v1/messages/:id) passa a vir prefixado com o código do gateway ([META_BAD_REQUEST] …), como já acontecia com os demais erros terminais. Demais 400 da Meta (ex.: mídia ainda em processamento) seguem retentáveis. POST /v1/templatescom nome que já falhou volta a funcionar. Template recusado na hora pela Meta deixa a linha local emREJECTEDsemmetaTemplateId; reenviar com o mesmo(phoneId, name, language)agora reaproveita essa linha (mesmoid) em vez de responder 409ALREADY_EXISTS. Template que existe na Meta — ou submissão do mesmo nome ainda em voo — continua dando 409.PATCH /v1/templates/:idem templateREJECTEDsemmetaTemplateIdre-submete em vez de responder 409TEMPLATE_NOT_SUBMITTED("aguarde aprovação inicial") para um template que a Meta já tinha recusado. Linha emDRAFT(submissão em voo) continua respondendo 409, emessageSendTtlSecondsna re-submissão passa a ser recusado com 400 em vez de ignorado em silêncio.messageIdé o único identificador de mensagem exposto. O ID nativo do canal (wamid da Meta /key.iddo Baileys) nunca é mais retornado. Operações de mensagem agora usam omessageIddo gateway no path (POST /v1/messages/{messageId}/react,DELETE /v1/messages/{messageId}, etc.) — antes usavam o ID nativo.quotedMessageIdpassa a ser omessageIddo gateway da mensagem citada.- Webhooks canônicos entre canais.
message:receivedemessage:statusagora têm o mesmo shape em Baileys / WABA / Instagram. Removidos os camposwhatsappMessageId,fromMe,mediaUrl/mediaStorageKeyno topo,trackederaw. Mídia inbound vem emmedia.downloadUrl. Status demessage:statusagora é lowercase:sent | delivered | read | failed | deleted. GET /v1/messages/{id}não retorna maiswhatsappMessageId.- Mídia:
metaMediaIdrenomeado paramediaHandle(body dePOST /v1/messagese resposta dePOST /v1/media/upload). - Instagram: usa o envelope canônico. Identidade vem como
from.canonicalKey = "igsid:<id>",type: "igsid". Read receipts chegam comomessage:statuscomstatus: "read"+watermark(semmessageId, pois IG é por watermark).
Corrigido
- Botão de cupom (
copy_code) passa a enviar{ type: "coupon_code" }. Antes ia comotext: a Meta aprovava o template e recusava o envio com132018. - Botão
quick_replypassa a enviar{ type: "payload" }— mesmo defeito, mesmo sintoma. template:statuspassa a ser emitido. Estava na referência de webhooks, no openapi e no SDK desde a v0.1.0, mas nada o publicava: aprovação e rejeição só se descobriam relistando os templates.- Webhook: a última retentativa (12h) passa a acontecer. A entrega fazia 8 tentativas e
só usava 7 das 8 esperas da tabela, então a DLQ chegava em ~8h40 em vez de ~20h40. Agora
são 9 tentativas (
X-WppGateway-Attemptvai até 9), e onextRetryAtda entrega mostra a espera que de fato será aplicada (mostrava a seguinte). - Docs — validação da assinatura. O header é
sha256=<hex>; o exemplo em Node comparava hex puro, e otimingSafeEquallançava erro por tamanho diferente. Quem copiou o exemplo não estava validando nada. POST /v1/phonesdeixa de mandarDeprecationna criação de Baileys. O header é para o WABA por credenciais (legado; o sucessor é o Embedded Signup). Em Baileys, que só nasce por essa rota, era um aviso de descontinuação falso.- Docs — referência conferida contra o código. As cópias do repo e do site divergiam em 12
páginas; agora são iguais, e um teste no CI as mantém assim. No caminho foram corrigidos a
tabela e a contagem de retry dos webhooks, o formato da resposta de
identities/resolve(pné JID), o pairing code (8 caracteres, não dígitos),credentials.verifyToken(opcional), a resposta derequest-name-change({ id }) e os motivos dosmb-sync. A página Grupos, que só existia no site, passa a existir também no repo.
v0.1.0 — 2026-05-21
Release inicial pública do wpp-gateway.
Adicionado
- Canais: Baileys (QR + pairing code), WABA (manual + Embedded Signup), Instagram (OAuth).
- Messages: 15 tipos de conteúdo (text, image, video, audio, document, sticker, location, contact, button, list, template, cta_url, location_request, address_message, flow).
- Operações de mensagem: react, edit, delete, read, typing.
- Media: download via signed URL, upload Meta, resumable upload, TTL 7d com ACK pattern.
- Webhooks: registro por phone, HMAC-SHA256, 8 retries com backoff exponencial, DLQ, replay manual.
- Events SSE:
/v1/events/streampara dashboards real-time. - WABA Templates: CRUD + Meta sync, suporte a carousel, OTP, flow buttons, limited time offer.
- WABA Calls: signaling SDP (make/accept/reject/hangup), recording upload.
- WABA Groups: CRUD, participants, admins, join requests, send messages.
- WABA Flows: CRUD + Meta sync, encryption RSA-OAEP para data_exchange, executions tracking.
- Lookup: verificação de números (Baileys only).
- Billing: invoices, Asaas (Pix/boleto/cartão), dunning automatizado.
- Admin observability: audit logs, error events.
- SDK React: Provider, hooks (
usePhones,useChannelStatus,useQrCode), componentes (BaileysQRConnect,BaileysPairingConnect,WabaEmbeddedSignup,WabaManualConnect,ConnectChannelWizard).
Conhecidos
- Scopes granulares de API key ainda não implementados (campo
scopesaceito mas não validado). - Webhook
template:statusecall:*payloads ainda em estabilização — schemas podem evoluir.