Webhook
O canal de webhook genérico é o fallback. Quando você precisa empurrar alertas para uma superfície de chat, sistema de tickets, ou relay interno que o Ongrid não fala nativamente (Microsoft Teams, Mattermost, Rocket Chat, OpsGenie, PagerDuty, seu próprio router), aponte um webhook genérico para lá e o pipeline de alerta posta o JSON canônico de mensagem Ongrid.
Use o canal dedicado quando houver um
Para Slack / Feishu / DingTalk / WeCom / Telegram, os canais dedicados renderizam payloads mais ricos (attachments Slack, assinatura). Recorra ao webhook genérico só quando não há um canal dedicado.
Payload
O body é o formato canônico notify.Message, JSON-encoded verbatim:
{
"severity": "critical",
"subject": "node-01 swap_high",
"body": "swap_in_pages > 1000 for 5m",
"source": "alert",
"labels": {
"rule": "swap_high",
"incident_id": "1234",
"device_id": "7",
"host": "node-01"
},
"dedupe_key": "alert:swap_high:device=7",
"occurred_at": "2026-05-30T14:02:35Z"
}Sem envelope, sem msgtype, sem achatamento. O receiver recebe exatamente os campos que o pipeline de alerta produziu. Montado por NewGenericWebhookSender.
Assinatura HMAC opcional
Quando você preenche o campo Secret, cada requisição carrega um header X-Ongrid-Signature:
X-Ongrid-Signature: sha256=<hex>Onde <hex> é hex(HMAC-SHA256(secret, body)) sobre os bytes exatos postados. O signer é signGenericWebhook.
Verificando no lado receiver
# Python receiver
import hmac, hashlib
def verify(secret, body_bytes, header):
expected = "sha256=" + hmac.new(
secret.encode(), body_bytes, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, header)// Go receiver
func verify(secret string, body, header []byte) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
expected := []byte("sha256=" + hex.EncodeToString(mac.Sum(nil)))
return hmac.Equal(expected, header)
}Sempre verifique a assinatura quando o endpoint é público
Uma URL de webhook genérico sem assinatura é aberta a qualquer um que descobre a URL. O header de assinatura é a única coisa que autentica "isto é do Ongrid".
Formato da requisição
| Propriedade | Valor |
|---|---|
| Método | POST |
Content-Type | application/json |
User-Agent | ongrid-notify/1.0 |
| Body | JSON de notify.Message |
| Header de sig | X-Ongrid-Signature: sha256=… (só quando há secret) |
| Timeout | Timeout por requisição do http.Client (padrão 15s) |
Critério de sucesso
O Sender trata HTTP 2xx (200–299) como sucesso. Qualquer outra coisa — incluindo redirects 3xx, 4xx, 5xx — é unexpected status: … e conta como entrega falha para o pipeline de dampening. O body da resposta não é parseado; o Ongrid não se importa com qual JSON seu relay devolve.
Retry e dampening
A semântica de retry pertence ao pipeline de alerta, não ao Sender. O Sender posta exatamente uma vez por chamada de Send(ctx, msg). O pipeline decide:
- Janela de dampening. Por rule, padrão 5 minutos. A mesma dedupe key dispara no máximo uma vez por janela.
- Repetir-em-ainda-disparando. Padrão 1 hora. Se um incidente ainda está aberto após essa janela, o canal recebe um re-notify para que não se perca no histórico do chat.
- Mensagem de recovery. Disparada quando o alerta limpa.
A função do Sender é empurrar os bytes. A função do pipeline é decidir quando.
Sem retry automático em falha de transporte
Se o receiver está fora no momento que o Sender posta, a mensagem está perdida (logada como erro de entrega, exposto nas estatísticas de entrega por canal). O pipeline não enfileira e re-tenta depois. O raciocínio: um alerta com 5 minutos de stale entregue três minutos após o recovery é mais confuso que um ausente. Use um receiver robusto, ou camade sua própria fila entre o Ongrid e um downstream instável.
Configuração
- Suba um endpoint HTTP que aceite
POST+application/json. Escolha um secret forte que você compartilhará com o Ongrid. - No Ongrid: Settings → Channels → New → Provider =
webhook→ Endpoint = sua URL → Secret = o secret compartilhado. - Clique Test. A mensagem de teste é a sintética
notify.Message{Severity: "info", Subject: "Ongrid test", …}. Verifique o header de assinatura nos logs do seu receiver.
Receitas
Microsoft Teams (incoming webhook)
O incoming webhook do Teams aceita um formato de payload diferente (um MessageCard). O caminho mais simples é colocar um adapter pequeno entre o Ongrid e o Teams:
# server.py - listens on /ongrid-to-teams
@app.post("/ongrid-to-teams")
def relay(msg: dict):
teams_url = os.environ["TEAMS_WEBHOOK"]
card = {
"@type": "MessageCard",
"@context": "https://schema.org/extensions",
"themeColor": {"critical":"D92F2F","warning":"F2C037","info":"36A64F"}.get(msg["severity"], "6F7A87"),
"title": msg["subject"],
"text": msg["body"],
}
requests.post(teams_url, json=card)Aponte o canal webhook do Ongrid para https://your-relay/ongrid-to-teams.
PagerDuty Events API v2
Mesmo padrão: um pequeno relay que traduz {severity, subject, body, dedupe_key} num payload events.v2 do PagerDuty. O campo dedup_key do PD mapeia 1:1 ao dedupe_key do Ongrid, então o mesmo incidente no Ongrid é o mesmo alerta no PD.
Splunk HEC / Elastic ingest
Aponte o canal para o endpoint HEC com o token Splunk na URL. O Splunk aceita o JSON canônico do Ongrid; indexe em severity e labels.rule para busca downstream.
Referência de campos
Veja Message para a fonte de verdade. Campos estáveis:
| Campo | Tipo | Notas |
|---|---|---|
severity | string | critical / warning / info. |
subject | string | Nome da rule + alvo — uma linha. |
body | string | Detalhe multi-linha. Pode conter newlines. |
source | string | alert / test / etc. |
labels | map[string]string | rule, incident_id, device_id, custom. |
dedupe_key | string | pipeline:rule:label-set. Estável por identidade. |
occurred_at | string RFC3339 | UTC. O momento em que o evaluator de alerta disparou. |
Campos novos são aditivos — receivers devem ignorar chaves desconhecidas.
Relacionado
- Visão geral dos canais
- Schema de rule de alerta — para o que
labels.rulesignifica e como dedupe keys são construídas.