Como funciona
Um webhook é um endereço do seu sistema, o endpoint, que a Gaio chama quando algo acontece na conta: chega uma mensagem, um contato ganha uma etiqueta, uma automação termina. A Gaio envia um POST com o evento em JSON, assinado com um segredo que só a Gaio e o seu sistema conhecem. Assim o seu sistema reage na hora, sem consultar a API de tempos em tempos.
O evento traz o recurso como a API o responde: um message.received traz a mensagem como GET /v1/messages/{messageId}. Para agir, como responder à mensagem, use a API.
Os webhooks fazem parte da API: só funcionam numa conta com a API liberada (veja Como obter acesso). Se a API for desligada para a conta, a Gaio para de enviar.
Cadastrar um endpoint
Cada conta tem até 10 endpoints, contando os desativados: exclua um para cadastrar outro. Cada endpoint recebe só os tipos de evento escolhidos para ele, e um evento vai para todos os endpoints que recebem o tipo dele.
No console
No console da Gaio, vá em Configurações › Desenvolvedores › Webhooks e clique em Adicionar endpoint. Como as chaves, os webhooks só aparecem para o dono da conta, entrando com o próprio login: um endpoint continua recebendo os dados da conta depois que a sessão termina.
Preencha:
- URL: para onde a Gaio envia os eventos (veja A URL).
- Descrição: opcional, quem recebe os eventos, como "Integração com o CRM".
- Eventos: os tipos de evento que o endpoint recebe.
Depois de criar, o console mostra o segredo de assinatura uma única vez. Guarde-o no servidor que recebe os eventos, como a chave da API: é com ele que você confere a assinatura. Se perdê-lo, gere outro.
Pela API
Com uma chave que tenha o escopo webhooks:write:
curl https://api.gaio.social/v1/webhooks \
-H "Authorization: Bearer $GAIO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-d '{
"url": "https://seu-servidor.com/webhooks/gaio",
"description": "Integração com o CRM",
"event_types": ["message.received", "contact.tagged"]
}'
Os campos: url, até 2048 caracteres; description, opcional, até 200 caracteres (em branco, fica sem descrição); event_types, de 1 a 50 tipos, guardados sem repetição e em ordem alfabética.
A resposta, 201, é o endpoint com o secret, o segredo de assinatura:
{
"id": "…",
"url": "https://seu-servidor.com/webhooks/gaio",
"description": "Integração com o CRM",
"event_types": ["contact.tagged", "message.received"],
"status": "enabled",
"disabled_reason": null,
"disabled_at": null,
"failing_since": null,
"previous_secret_expires_at": null,
"created_at": "…",
"updated_at": "…",
"secret": "whsec_…"
}
Só esta resposta e a da troca do segredo trazem o secret: guarde-o na hora. Repetir o cadastro com a mesma chave de idempotência devolve o mesmo endpoint, com o segredo atual dele.
As rotas dos webhooks:
| Rota | O que faz | Escopo |
|---|---|---|
GET /v1/webhooks | Lista os endpoints, do mais novo para o mais antigo, numa página só. | webhooks:read |
POST /v1/webhooks | Cadastra um endpoint. | webhooks:write |
GET /v1/webhooks/{webhookId} | Lê um endpoint. | webhooks:read |
PATCH /v1/webhooks/{webhookId} | Muda url, description, event_types (a lista inteira) ou status (enabled ou disabled). | webhooks:write |
DELETE /v1/webhooks/{webhookId} | Exclui um endpoint. | webhooks:write |
POST /v1/webhooks/{webhookId}/rotate-secret | Troca o segredo. | webhooks:write |
POST /v1/webhooks/{webhookId}/test | Envia um evento de teste. | webhooks:write |
GET /v1/webhooks/{webhookId}/deliveries | Lista as entregas. | webhooks:read |
POST /v1/webhooks/{webhookId}/deliveries/{deliveryId}/retry | Reenvia uma entrega. | webhooks:write |
Nos limites de uso, o teste e o reenvio contam como envio, as leituras como leitura e o resto como escrita.
Para assinar um tipo de evento, a chave também precisa do escopo que lê os dados dele pela API, porque um webhook nunca entrega ao seu sistema o que a própria chave não poderia ler. Sem ele, a resposta é 403, com insufficient_scope e o escopo que falta em context.required_scope.
| Eventos | Escopo de leitura |
|---|---|
message.*, comment.received e live_comment.received | conversations:read |
contact.* e connection.* | contacts:read |
workflow_run.ended e link.clicked | workflows:read |
Desativar um endpoint (status: disabled) ou excluí-lo desiste do que ele ainda tinha para receber. Ativado de novo, ele recebe os eventos que acontecerem a partir daí. O disabled_reason diz por que o endpoint está desativado: requested, quando alguém o desativou (no console ou com PATCH e {"status": "disabled"}), ou failing, quando a Gaio o desativou por falhas.
Cadastrar, alterar, excluir um endpoint e trocar o segredo dele entra no log de auditoria da conta, com a chave como autora quando a mudança vem pela API: cadastrar um endpoint é a instrução da conta para enviar os dados dela àquele endereço.
A URL
A Gaio só envia para uma URL:
https, sem porta (a porta é sempre a 443);- sem usuário e senha;
- num domínio que aponta para um endereço público, e não para uma rede privada, local ou reservada.
A URL é conferida no cadastro e de novo a cada envio. Uma URL recusada recebe 422, com invalid_webhook_url e o motivo em context.reason:
reason | O que significa |
|---|---|
invalid | Não é uma URL. |
https_required | Não é https. |
credentials | Tem usuário ou senha. |
port | Tem uma porta diferente da 443. |
unreachable | O domínio não aponta para um endereço público. |
O que chega
Cada evento chega num POST para a URL do endpoint, com o corpo em JSON:
{
"id": "…",
"type": "message.received",
"api_version": "v1",
"created_at": "…",
"data": {
"id": "…",
"conversation_id": "…",
"channel": { "type": "instagram", "connection_id": "…" },
"type": "message",
"direction": "inbound",
"sender": { "type": "contact", "user_id": null, "name": null },
"text": "Oi! Vocês entregam em Curitiba?",
"attachments": [],
"template": null,
"reply_to_message_id": null,
"button": null,
"post": null,
"status": "received",
"is_unsupported": false,
"is_removed": false,
"is_edited": false,
"reactions": [],
"read_at": null,
"created_at": "…"
}
}
| Campo | O que é |
|---|---|
id | O id do evento. É o mesmo em todas as tentativas e nos reenvios: é por ele que você descarta os repetidos. |
type | O tipo do evento. |
api_version | A versão da API em que o data está escrito: v1. |
created_at | Quando aconteceu. |
data | O recurso, como a API o responde. |
E com estes cabeçalhos:
| Cabeçalho | O que é |
|---|---|
Content-Type | application/json. |
User-Agent | Gaio-Webhooks/1.0. |
Gaio-Signature | A assinatura do corpo. |
Gaio-Event-Id | O id do evento. |
Gaio-Event-Type | O type do evento. |
Gaio-Delivery-Attempt | O número da tentativa: 1 na primeira, 2 na segunda, e assim por diante. |
Conferir a assinatura
Qualquer um pode chamar a URL do seu endpoint. Confira a assinatura de cada entrega antes de confiar no conteúdo: ela prova que foi a Gaio que enviou aquele corpo, com o segredo do endpoint, há pouco tempo.
O cabeçalho Gaio-Signature tem o formato t=1760020200,v1=…:
té o momento da tentativa, em segundos desde 1970 (Unix).v1é o HMAC-SHA256, em hexadecimal, de<t>.<corpo>: o valor det, um ponto e o corpo exatamente como chegou. A chave do HMAC é o segredo inteiro, com owhsec_.- Durante uma troca de segredo, vêm dois
v1, um com cada segredo.
Para conferir:
- Leia o corpo cru, os bytes que chegaram, antes de interpretar o JSON. Um JSON interpretado e escrito de novo muda espaços, ordem e acentos, e a assinatura não bate.
- Calcule o HMAC-SHA256 de
<t>.<corpo>com o segredo. - Compare o resultado com cada
v1, em tempo constante, e aceite se um deles bater. - Recuse se
testiver a mais de 5 minutos do relógio do seu servidor: assim, quem capturou uma entrega não consegue reenviá-la depois. Mantenha o relógio sincronizado.
Cada tentativa é assinada de novo, com o seu próprio t: uma nova tentativa da mesma entrega traz outro Gaio-Signature e o mesmo id.
Responda com um erro, como 400, a uma entrega que não passa. Se o problema for do seu lado, como um segredo desatualizado, a Gaio tenta de novo, e nada se perde enquanto você corrige.
Os exemplos abaixo usam só a biblioteca padrão para conferir a assinatura e leem o segredo da variável de ambiente GAIO_WEBHOOK_SECRET. enqueue é onde o seu sistema guarda o evento para processar depois, como uma fila ou uma tabela (veja Responder e processar).
Node.js
Com Express, que entrega o corpo cru com express.raw:
import { createHmac, timingSafeEqual } from 'node:crypto';
import express from 'express';
const TOLERANCE_SECONDS = 5 * 60;
export function verifyGaioSignature(rawBody, header, secret) {
const fields = (header ?? '').split(',').map((field) => field.trim().split('='));
const timestamp = Number(fields.find(([key]) => key === 't')?.[1]);
if (!Number.isInteger(timestamp)) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > TOLERANCE_SECONDS) return false;
const expected = createHmac('sha256', secret).update(`${timestamp}.`).update(rawBody).digest();
return fields.some(
([key, value]) =>
key === 'v1' &&
/^[0-9a-f]{64}$/.test(value ?? '') &&
timingSafeEqual(expected, Buffer.from(value, 'hex'))
);
}
const app = express();
app.post('/webhooks/gaio', express.raw({ type: 'application/json' }), (req, res) => {
const header = req.get('Gaio-Signature');
const secret = process.env.GAIO_WEBHOOK_SECRET;
if (!Buffer.isBuffer(req.body) || !verifyGaioSignature(req.body, header, secret)) {
return res.sendStatus(400);
}
enqueue(JSON.parse(req.body.toString('utf8')));
res.sendStatus(200);
});
app.listen(3000);
Python
Com Flask, que entrega o corpo cru em request.get_data():
import hashlib
import hmac
import json
import os
import time
from flask import Flask, request
TOLERANCE_SECONDS = 5 * 60
def verify_gaio_signature(raw_body: bytes, header: str, secret: str) -> bool:
fields = [field.strip().partition("=") for field in header.split(",")]
timestamp = next((value for key, _, value in fields if key == "t"), "")
signatures = [value for key, _, value in fields if key == "v1"]
if not (timestamp.isascii() and timestamp.isdigit()):
return False
if abs(int(time.time()) - int(timestamp)) > TOLERANCE_SECONDS:
return False
signed = timestamp.encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(expected.encode(), value.encode()) for value in signatures)
app = Flask(__name__)
@app.post("/webhooks/gaio")
def gaio_webhook():
raw_body = request.get_data()
header = request.headers.get("Gaio-Signature", "")
if not verify_gaio_signature(raw_body, header, os.environ["GAIO_WEBHOOK_SECRET"]):
return "", 400
enqueue(json.loads(raw_body))
return "", 200
Em outro framework, o que muda é como ler o corpo cru: desligue o leitor de JSON na rota do webhook, ou leia o corpo antes dele.
Testar um endpoint
No console, clique em Enviar evento de teste no endpoint. Pela API, use POST /v1/webhooks/{webhookId}/test. A Gaio envia na hora um evento do tipo webhook.test, assinado como qualquer outro, com data igual a {"webhook_id": "…"}, e responde como o endpoint o recebeu:
{
"delivered": true,
"response_status": 200,
"error": null,
"duration_ms": 182
}
delivered diz se o endpoint respondeu 2xx, response_status é o status que ele respondeu, quando respondeu, e duration_ms é quanto o envio levou, em milissegundos. Quando o envio falha, delivered é false e error traz um dos motivos de last_error; quando dá certo, error vem null. O teste vai para o endpoint mesmo desativado, não entra no registro de entregas e não é tentado de novo. Responda 2xx a ele como a qualquer evento.
Responder e processar
Responda rápido
Responda 2xx em até 10 segundos e processe depois: guarde o evento, responda e faça o trabalho em seguida. Uma chamada ao seu CRM ou a um modelo de IA antes de responder pode passar dos 10 segundos.
Qualquer outro resultado é uma tentativa que falhou: um status fora de 2xx, inclusive um redirecionamento (a Gaio não segue redirecionamentos), uma resposta depois dos 10 segundos ou uma conexão que não se completa. O corpo da sua resposta é ignorado.
A Gaio faz até 5 entregas ao mesmo tempo para cada endpoint. Quando há mais eventos do que isso, eles esperam e saem aos poucos, até cerca de 100 por minuto.
Descarte os repetidos
A entrega é pelo menos uma vez: o mesmo evento pode chegar mais de uma vez, por exemplo quando a sua resposta se perde no caminho. Descarte os repetidos pelo id, que é o mesmo em todas as tentativas e nos reenvios. Guarde os id processados por pelo menos 7 dias, o tempo em que uma entrega ainda pode ser reenviada.
Não conte com a ordem
Os eventos não chegam necessariamente na ordem em que aconteceram: uma nova tentativa pode chegar depois de um evento mais novo, e um message.updated pode chegar antes do message.received da mesma mensagem. Quando a ordem importa, compare o created_at ou leia o recurso de novo pela API, que responde o estado atual.
Novas tentativas
Uma entrega que falhou é tentada de novo, até 10 tentativas, ao longo de uns 2 dias:
| Tentativa | Quando |
|---|---|
| 1ª | Logo depois do evento. |
| 2ª | 1 minuto depois da 1ª. |
| 3ª | 5 minutos depois da 2ª. |
| 4ª | 30 minutos depois da 3ª. |
| 5ª | 2 horas depois da 4ª. |
| 6ª | 5 horas depois da 5ª. |
| 7ª a 10ª | 10 horas depois da anterior. |
Depois da 10ª, a entrega fica com falha (failed), e você ainda pode reenviá-la.
Registro de entregas
Cada evento enviado a um endpoint é uma entrega, guardada por 7 dias. No console, abra Entregas no endpoint. Pela API, use GET /v1/webhooks/{webhookId}/deliveries, da mais recente para a mais antiga, com o filtro status (pending, succeeded ou failed):
{
"id": "…",
"webhook_id": "…",
"event": { "id": "…", "type": "message.received", "created_at": "…" },
"status": "failed",
"attempts": 10,
"next_attempt_at": null,
"last_attempt_at": "…",
"last_response_status": 500,
"last_error": "http_status",
"delivered_at": null,
"created_at": "…"
}
O status é pending (esperando uma tentativa, ou numa agora), succeeded (o endpoint respondeu 2xx) ou failed (a Gaio desistiu). last_response_status é o status que o endpoint respondeu na última tentativa, quando respondeu, e last_error diz por que ela falhou:
last_error | O que significa |
|---|---|
http_status | O endpoint respondeu um status fora de 2xx, inclusive um redirecionamento. |
timeout | O endpoint não respondeu em 10 segundos. |
dns_failed | O domínio não foi encontrado. |
connection_failed | Não foi possível conectar ao servidor. |
tls_failed | O certificado HTTPS do servidor foi recusado. |
unsafe_target | O domínio passou a apontar para uma rede para a qual a Gaio não envia. |
endpoint_disabled | O endpoint foi desativado ou excluído antes do envio. |
account_disabled | A conta estava suspensa ou sem a API. |
Reenviar uma entrega
No console, clique em Reenviar na entrega. Pela API, use POST /v1/webhooks/{webhookId}/deliveries/{deliveryId}/retry, que responde 202 com a entrega de volta na fila. Dá para reenviar uma entrega que falhou ou uma que deu certo e o seu sistema precisa de novo; uma entrega pendente é respondida como está.
O reenvio manda o mesmo evento, com o mesmo id. Se ele falhar, as novas tentativas continuam de onde a entrega parou, então uma entrega que já usou as 10 tentativas falha de novo sem outras. O endpoint precisa estar ativo (409, com webhook_disabled), e depois de 7 dias a entrega não existe mais (404, com not_found).
Endpoint desativado por falhas
Se todas as tentativas para um endpoint falham por 3 dias, a Gaio desativa o endpoint (status: disabled, com disabled_reason: failing), desiste das entregas pendentes dele e avisa o dono da conta por e-mail. Enquanto as tentativas falham, failing_since diz desde quando, e uma entrega que dá certo zera a contagem.
Para voltar a receber, corrija o servidor, envie um evento de teste e ative o endpoint: no console, em Ativar, ou com PATCH /v1/webhooks/{webhookId} e {"status": "enabled"}. Ele recebe os eventos que acontecerem a partir daí; o que ficou para trás você reenvia pelo registro, dentro dos 7 dias.
Conta suspensa
Enquanto a conta está suspensa, ou sem a API, a Gaio não registra nem entrega eventos: o que acontece nesse período não gera evento, nem depois que a conta volta. As entregas que estavam pendentes, inclusive as da própria suspensão (como o connection.disconnected de cada conexão que ela desliga), ficam no registro de entregas com falha (account_disabled) e não são tentadas de novo sozinhas. Quando a conta volta, você pode reenviá-las dentro dos 7 dias.
Trocar o segredo
No console, clique em Gerar novo segredo no endpoint. Pela API, use POST /v1/webhooks/{webhookId}/rotate-secret, que responde o endpoint com o novo secret. Como no cadastro, o segredo só aparece nessa resposta.
O segredo anterior continua assinando por 24 horas, até o previous_secret_expires_at do endpoint: nesse tempo, cada entrega traz dois v1, um com cada segredo. Para trocar sem recusar nenhuma entrega:
- Gere o novo segredo.
- Passe o seu servidor para o novo segredo nas 24 horas seguintes. Os exemplos acima aceitam a entrega quando qualquer
v1bate, então funcionam antes e depois dessa troca.
Gerar outro segredo dentro dessas 24 horas descarta na hora o mais antigo dos dois.
Se o segredo vazou, gere outro e passe o seu servidor para ele logo em seguida: a partir daí, o seu servidor recusa uma assinatura feita com o segredo vazado, mesmo que a Gaio ainda assine com ele até o fim das 24 horas.
Eventos
O type diz o que aconteceu, e o data traz o recurso como a API o responde naquele momento. A lista de tipos e os campos de data crescem sem mudar de versão, como o resto da /v1 (veja Versionamento): ignore os campos que você não conhece.
Mensagens e comentários
O data é a mensagem como GET /v1/messages/{messageId} a responde naquele momento, com a conversa em conversation_id. Pedem o escopo conversations:read.
| Tipo | Quando é enviado |
|---|---|
message.received | O contato mandou uma mensagem direta, respondeu ou mencionou um story, ou tocou num botão ou num quebra-gelo (o título dele vem em text), no Instagram ou no WhatsApp. |
comment.received | O contato comentou num post ou num reel. |
live_comment.received | O contato comentou numa live. |
message.sent | A conta enviou uma mensagem: alguém da equipe pela Gaio, uma automação, uma integração pela API (a sua também) ou alguém no app do Instagram ou do WhatsApp. Inclui as respostas da conta a comentários, públicas (data.type igual a comment_reply) ou no direct (comment_dm_reply). |
message.updated | O remetente editou a mensagem, ou alguém reagiu a ela ou tirou a reação. Só no Instagram. |
message.deleted | O remetente apagou a mensagem no Instagram, ou a conta excluiu o comentário pela Gaio. |
message.failed | O WhatsApp aceitou um envio e depois avisou que ele não foi entregue. |
Respostas e menções em story não têm um tipo próprio: chegam como message.received, com data.type igual a story_message (uma resposta a um story) ou story_mention (uma menção num story).
- O histórico não é enviado: as mensagens que a Gaio guarda ao reconectar uma conta, ou que importa do app WhatsApp Business, não geram evento. Os comentários que a Gaio encontra sozinha, quando o Instagram não os entregou, geram.
- Uma mensagem apagada antes de o evento dela ser entregue chega apagada: as tentativas que ainda faltam trazem a mensagem como a API a responde agora (
is_removed: true,text: null). A exceção é omessage.updated, de uma edição ou de uma reação: ele mantém a mensagem como estava naquela mudança. - Reconheça os seus próprios envios pelo
idque o envio respondeu, e não pelosender.type: quando o canal informa a mensagem antes de a Gaio registrar quem a enviou, omessage.sentvem comunattributed, e a mensagem lida depois mostra o remetente. - A mídia de uma mensagem pode responder
404por alguns segundos depois que o evento chega, enquanto a Gaio a guarda: tente de novo. - Um contato bloqueado das automações continua gerando
message.received, mas um envio pela API para ele é recusado, comcontact_blocked.
Contatos
O data traz o contato como GET /v1/contacts/{contactId} o responde depois da mudança. Pedem o escopo contacts:read.
| Tipo | Quando é enviado | data |
|---|---|---|
contact.created | Um contato foi criado, de qualquer jeito: alguém novo escreveu ou comentou, a conta escreveu para um número novo, uma conversa veio do histórico do canal, uma importação ou POST /v1/contacts. | O contato, com as etiquetas e os valores com que foi criado. |
contact.tagged | Etiquetas foram postas num contato que não as tinha, por qualquer caminho: o console, uma automação, uma ação em massa, uma importação ou uma chave. | { "contact": …, "tags": […] }, com as etiquetas postas. |
contact.untagged | Etiquetas foram tiradas de um contato que as tinha. Tirar uma etiqueta tira de todos os contatos da mesma pessoa, e cada um que a tinha gera um evento. | { "contact": …, "tags": […] }, com as etiquetas tiradas. |
contact.custom_fields_updated | Valores de campos personalizados foram preenchidos ou limpos, por qualquer caminho, inclusive uma pergunta de automação que guarda um e-mail ou um telefone. | { "contact": …, "custom_fields": […] }, uma entrada por campo que mudou, com field_id, name, type e value (null quando o valor foi limpo). |
- Um contato criado com etiquetas ou valores gera um único
contact.created, que já os traz, e não tambémcontact.taggedoucontact.custom_fields_updated. - Gravar o que o contato já tem não gera evento. Um contato que volta depois de excluído mantém o
ide não chega como criado. - Excluir ou renomear uma etiqueta ou um campo personalizado não gera evento nos contatos que o tinham: se você guarda uma cópia, leia esses contatos de novo.
- Uma ação em massa ou uma importação gera um evento por contato que mudou. Esses eventos saem a cerca de 100 por minuto por endpoint: uma ação em 5.000 contatos chega ao endpoint ao longo de uns 50 minutos.
Automações
Pedem o escopo workflows:read.
| Tipo | Quando é enviado | data |
|---|---|---|
workflow_run.ended | Uma execução terminou: chegou ao fim (completed), parou (stopped), por exemplo numa proteção da Gaio como a que impede laços, foi barrada (blocked), quando o Instagram recusa as respostas privadas no post, ou falhou e nada vai tentá-la de novo. Um envio adiado só termina a execução quando a Gaio desiste dele. | A execução como GET /v1/workflows/{workflowId}/runs/{runId} a responde, com os passos. |
link.clicked | Um contato abriu um link que uma automação enviou, a cada vez que abre. | workflow_id, run_id, contact_id, node_id, button_id, url e clicked_at. |
- Uma execução esperando o contato (
waiting) não terminou. Uma execuçãostoppedque o contato retoma depois, respondendo, termina de novo e gera outro evento. - O
datadoworkflow_run.endedtraz os passos dos trechos mais recentes da execução, comsteps_truncatedquando os mais antigos ficaram de fora. Numa execução iniciada por um comentário, a resposta pública ao comentário pode ser registrada depois do fim: lida depois, a execução a mostra entre os passos, com o mesmostatus. - No
link.clicked,urlé para onde o contato foi levado,node_idé o bloco que enviou o link,button_idé oiddo botão no grafo (mantenha os ids ao reescrever um bloco) erun_idé a execução que enviou o link ao contato por último. Cada um vemnullquando a Gaio não sabe, econtact_idvemnullpara um contato que a conta não tem mais. - Toda abertura de um link conta, inclusive recarregar a página, como nas métricas de clique da automação. Um botão que passa a conversa para o WhatsApp não gera evento.
- O clique chega aos webhooks por melhor esforço: numa falha rara, um clique pode ficar sem evento. Um link aberto mais de um ano depois do último envio mostra a página de link expirado e não gera evento.
Conexões
O data é a conexão como GET /v1/connections a lista depois da mudança, e o created_at do evento é quando ela mudou. Pedem o escopo contacts:read.
| Tipo | Quando é enviado |
|---|---|
connection.connected | Uma conexão passou a conectada: no console, num login, quando a configuração de um número de WhatsApp termina ou quando uma fatura paga traz de volta o que uma suspensão desligou. |
connection.disconnected | Uma conexão passou a desconectada: no console, porque o canal retirou o acesso, por uma suspensão, pela Gaio ou porque a conta não foi usada nos dias depois de conectar. |
O disconnect_reason da conexão diz por que ela está desconectada:
disconnect_reason | O que significa |
|---|---|
authorization_lost | O canal retirou o acesso da Gaio: o app foi removido, uma permissão foi revogada ou a senha mudou. Conectar de novo resolve. |
disconnected_by_user | Alguém da conta desconectou. |
account_suspended | A conta foi suspensa, ou a Gaio desligou a conexão. |
activation_expired | A conta não foi usada nos dias depois de conectar. |
setup_incomplete | Um número de WhatsApp com a configuração em andamento, com falha ou esperando o PIN da confirmação em duas etapas do número. |
Ele vem null numa conexão conectada, e também numa conexão desconectada antes de a Gaio passar a registrar o motivo. Outros valores podem aparecer.
- Um evento sai a cada vez que o status de uma conexão muda de verdade. Uma mudança que encontra a conexão já naquele status não gera evento, então a mesma mudança feita duas vezes, ao mesmo tempo ou numa nova tentativa, é um evento só.
- Um número de WhatsApp configurado de novo enquanto conectado gera
connection.disconnected, comsetup_incomplete, até a configuração terminar: nesse meio-tempo ele não envia mensagens. - Remover uma conexão no console a desconecta antes, e isso gera evento; a remoção em si não gera, nem a troca de um número por outro.
- Os
connection.disconnectedde uma suspensão não são entregues (veja Conta suspensa).
Erros
Os erros das rotas de webhooks seguem o formato de todas as rotas (veja Erros), com estes códigos próprios:
| Código | Status | Quando acontece | Campos extras em context |
|---|---|---|---|
invalid_webhook_url | 422 | A URL não é uma para a qual a Gaio envia (veja A URL). | reason |
webhook_limit_reached | 409 | A conta já tem o máximo de endpoints. | limit |
webhook_disabled | 409 | Uma entrega reenviada para um endpoint desativado. Ative o endpoint antes. | — |
api_disabled | 403 | A API foi desligada para a conta. | — |
service_unavailable | 503 | Os webhooks não podem ser assinados agora. Tente de novo. | — |