Webhooks
Configure URLs externas que recebem POST em tempo real quando eventos importantes acontecem no seu servidor: raid detectado, ticket aberto, membro entrou, etc. Tudo assinado com HMAC SHA-256.
Quickstart em 3 passos
- 1Configure o webhook no painel: entre em
/dashboard/[seu_servidor]/webhooks, clique "Adicionar webhook", cole a URL do seu receiver, escolha os eventos. - 2Anote o secret que aparece (1 vez só — guarde como variável de ambiente no seu receiver).
- 3Implemente validação HMAC + processamento. Exemplos abaixo. Clica em "Testar" no painel pra confirmar que tá tudo conectado.
Headers da requisição
| Header | Descrição |
|---|---|
| Content-Type | Sempre application/json |
| User-Agent | GBBOT-Webhooks/1.0 |
| X-GBBOT-Signature | sha256=<hex> — HMAC SHA-256 da body |
| X-GBBOT-Event | Nome do evento (ex: raid.detected) |
| X-GBBOT-Delivery-Id | UUID único — use pra idempotência |
Validar a assinatura
SEMPRE valide a assinatura antes de processar — sem isso qualquer um pode fingir ser o GBBOT e mandar payload falso pro seu endpoint.
Node.js (Express)
// Express + raw body
const express = require('express');
const crypto = require('crypto');
const app = express();
// IMPORTANTE: precisa do body BRUTO (string) antes de parsear
app.post('/webhooks/gbbot',
express.raw({ type: 'application/json' }),
(req, res) => {
const SECRET = process.env.GBBOT_WEBHOOK_SECRET;
const rawBody = req.body.toString('utf8');
// Calcula HMAC esperado
const expected = crypto
.createHmac('sha256', SECRET)
.update(rawBody)
.digest('hex');
// Pega o que o GBBOT enviou
const received = (req.headers['x-gbbot-signature'] || '')
.replace('sha256=', '');
// timingSafeEqual evita timing attack
const ok = received.length === expected.length &&
crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(received)
);
if (!ok) return res.status(401).end();
// Parse + processa
const payload = JSON.parse(rawBody);
console.log('Evento:', payload.event, payload.data);
res.status(200).end();
}
);
app.listen(3000);Python (Flask)
# Flask
from flask import Flask, request, abort
import hmac, hashlib, os
app = Flask(__name__)
SECRET = os.environ['GBBOT_WEBHOOK_SECRET'].encode()
@app.post('/webhooks/gbbot')
def gbbot_webhook():
body = request.get_data() # bytes brutos, ANTES de parsear
expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
received = request.headers.get(
'X-GBBOT-Signature', ''
).replace('sha256=', '')
if not hmac.compare_digest(expected, received):
abort(401)
payload = request.get_json()
print(f"Evento: {payload['event']}", payload['data'])
return '', 200Referência de eventos (9)
Cada evento tem `event`, `guildId`, `deliveryId`, `timestamp` e um `data` específico.
raid.detected
Anti-raid expulsou ou baniu uma conta suspeita.
{
"event": "raid.detected",
"guildId": "1234567890",
"deliveryId": "abc-uuid",
"timestamp": "2026-05-20T15:30:00.000Z",
"data": {
"action": "ban", // 'ban' ou 'kick'
"reason": "conta_nova",
"accountAgeDays": 2,
"minAccountAgeDays": 7,
"member": {
"id": "999999",
"tag": "spammer#0000",
"username": "spammer"
}
}
}punishment.applied
AutoMod aplicou punição (ban/kick/mute/timeout). Não inclui "warn" (que só apaga msg).
{
"event": "punishment.applied",
"guildId": "1234567890",
"deliveryId": "abc-uuid",
"timestamp": "2026-05-20T15:30:00.000Z",
"data": {
"action": "timeout", // timeout/mute/kick/ban
"reason": "AutoMod: Flood/Spam detectado",
"durationMins": 10, // null se kick/ban
"member": {
"id": "999999",
"tag": "user#0000",
"username": "user"
}
}
}ticket.created
Cliente abriu ticket de suporte com guildId associado.
{
"event": "ticket.created",
"guildId": "1234567890",
"deliveryId": "abc-uuid",
"timestamp": "2026-05-20T15:30:00.000Z",
"data": {
"ticketId": "65f8a1b2c3d4e5f6a7b8c9d0",
"subject": "Dúvida sobre boas-vindas",
"category": "support",
"userId": "123456789",
"userTag": "fernando#1234"
}
}ticket.closed
Admin fechou um ticket (transição open→closed).
{
"event": "ticket.closed",
"guildId": "1234567890",
"deliveryId": "abc-uuid",
"timestamp": "2026-05-20T15:30:00.000Z",
"data": {
"ticketId": "65f8...",
"subject": "...",
"category": "support",
"userId": "123456789",
"closedBy": "987654321",
"closedReason": "Resolvido."
}
}member.joined
Novo membro entrou (e PERMANECEU — se anti-raid expulsou, dispara raid.detected em vez disso).
{
"event": "member.joined",
"guildId": "1234567890",
"deliveryId": "abc-uuid",
"timestamp": "2026-05-20T15:30:00.000Z",
"data": {
"member": {
"id": "555",
"tag": "novo#0001",
"username": "novo",
"avatar": "https://cdn.discordapp.com/avatars/...",
"accountAgeDays": 120
},
"memberCount": 1234
}
}member.left
Membro saiu (kick ou voluntária — não distinguimos no webhook).
{
"event": "member.left",
"guildId": "1234567890",
"deliveryId": "abc-uuid",
"timestamp": "2026-05-20T15:30:00.000Z",
"data": {
"member": { "id": "555", "tag": "saiu#0001", "username": "saiu" },
"memberCount": 1233
}
}license.expiring
Plano vence em 7, 1 ou 0 dias. Disparado pelo sweeper diário na janela 09:00–10:00.
{
"event": "license.expiring",
"guildId": "1234567890",
"deliveryId": "abc-uuid",
"timestamp": "2026-05-20T09:30:00.000Z",
"data": {
"guildName": "Meu Servidor",
"expiresAt": "2026-05-27T23:59:59.000Z",
"daysUntilExpiration": 7,
"phase": "D-7", // D-7, D-1 ou D-0
"plan": "elite"
}
}license.expired
Plano expirou ontem (D+1). Dispara 1 vez.
{
"event": "license.expired",
"guildId": "1234567890",
"deliveryId": "abc-uuid",
"timestamp": "2026-05-20T09:30:00.000Z",
"data": {
"guildName": "Meu Servidor",
"expiresAt": "2026-05-19T23:59:59.000Z",
"daysUntilExpiration": -1,
"phase": "D+1",
"plan": "elite"
}
}test
Disparado pelo botão "Testar" no painel. Pra validação inicial do receiver.
{
"event": "test",
"guildId": "1234567890",
"deliveryId": "abc-uuid",
"timestamp": "2026-05-20T15:30:00.000Z",
"data": {
"triggeredBy": "admin-discord-id",
"message": "Webhook de teste do GBBOT. Se você está vendo isso, o receiver tá funcionando."
}
}Boas práticas
- Idempotência via
X-GBBOT-Delivery-IdEm caso de retry (timeout/5xx), o GBBOT reenvia o MESMO deliveryId. Guarde em cache/Redis e dropa duplicado. - Responda rápido (< 5 segundos)Timeout do dispatcher é 5s. Processamento longo? Responda 200 OK e processa async (fila/worker).
- Retries automáticosGBBOT retenta 1× em caso de timeout ou 5xx, com 2 segundos de backoff. Após 10 falhas seguidas, o webhook é AUTO-DESATIVADO — você reativa no painel.
- Não confie em ordem de eventosWebhooks podem chegar fora de ordem em raros casos (rede). Use timestamps no payload pra ordenar se precisar.
- Rotacione o secret periodicamentePainel tem botão "Rotacionar secret" — gera novo, invalida o antigo. Atualize seu receiver no momento da rotação.
Receitas comuns
📢 Postar no Slack
Recebe o webhook do GBBOT, valida HMAC, formata como Slack message e POST no Slack incoming webhook. ~30 linhas em Node.
🔌 Integrar via Zapier / n8n
Use o "Webhook by Zapier" como trigger. NÃO permite validar HMAC nativamente — recomenda-se autenticar com URL secreta (`?token=`) como camada adicional.
📊 Salvar em BigQuery / Postgres
Recebe + valida + INSERT INTO eventos. Útil pra analytics agregada por guilda ao longo do tempo.
🚨 Alertar no Telegram
Filtra só raid.detected e license.expiring, manda mensagem na sua DM do Telegram via Bot API.
Tem alguma dúvida ou achou um bug nessa doc?
Nos fala no Discord ou abre um ticket.