Webhooks

Um webhook de entrada permite a outro sistema publicar num canal, enviando JSON para uma URL secreta. Um webhook de saída faz o oposto: envia mensagens para a sua URL, as que começam com uma palavra de ativação ou as que as pessoas escolhem no menu de mensagens, e publica aquilo que responder.

Onde gerir webhooks

Abra o seu avatar no canto superior direito e, em seguida, Integrações. As páginas de integrações da comunidade atual listam Webhooks de entrada e Webhooks de saída. Cada webhook pertence a uma única comunidade.

Por omissão, os administradores da comunidade e os administradores do sistema podem criar webhooks. Um administrador do sistema pode conceder esse direito a outros papéis nas definições de permissões do Consola do sistema. Um administrador do sistema também pode desativar cada tipo em Consola do sistema › Integrações › Gestão de integrações: Ativar webhooks de entrada e Ativar webhooks de saída estão ambos ativados por omissão.

A channel with messages from a CI bot and from GitHub, shown as cards with fields, pipeline stages and a button
Mensagens de integração desenhadas como cartões: campos, etapas do pipeline e um botão de ação.

Criar um webhook de entrada

  1. Em Integrações, escolha Webhooks de entrada e, em seguida, Adicionar webhook de entrada.
  2. Preencha um Título e uma Descrição: estes apenas aparecem nas páginas de integrações.
  3. Escolha o Canal para onde as mensagens são enviadas por omissão. Para um canal privado, tem de ser membro dele.
  4. Ative Fixar a este canal se os remetentes não puderem publicar noutros canais.
  5. Defina, opcionalmente, um Nome de utilizador e uma URL de Imagem do perfil para as suas mensagens (ver Nome e ícone).
  6. Escolha Guardar. Copie a URL apresentada: https://chat.example.com/hooks/<key>.
A URL é o único segredo: qualquer pessoa que a tenha pode publicar no canal. Mantenha-a fora de repositórios públicos e elimine o webhook se for exposta.

Com a API: POST /api/v4/hooks/incoming com {"channel_id", "display_name", "description", "username", "icon_url", "channel_locked"}; o id da resposta é a chave. A partir da linha de comandos: amctl webhook create-incoming --channel <channel id> --user <user> --display-name "CI".

Enviar uma mensagem para um webhook de entrada

curl -X POST https://chat.example.com/hooks/xgh5ycrx1jf7mbs9w8z4c8e7ho \
  -H 'Content-Type: application/json' \
  -d '{"text": "Deploy of **api 2.4.0** finished :white_check_mark:"}'

O servidor responde com 200 e o corpo ok. Também aceita application/x-www-form-urlencoded com o JSON num campo payload, tal como os webhooks do Slack, pelo que as ferramentas feitas para o Slack geralmente funcionam sem alterações.

CampoSignificado
textA mensagem, em Markdown. Obrigatório, a menos que existam attachments. Um texto mais longo que o limite de mensagem do servidor é dividido em várias mensagens.
channelOutro canal da comunidade do webhook, pelo nome (ops-alerts ou #ops-alerts), ou @username para uma mensagem direta do proprietário do webhook. Recusado quando o webhook está bloqueado no seu canal. O nome antigo town-square continua a chegar ao canal predefinido da comunidade, agora chamado general.
username, icon_url, icon_emojiO nome e a imagem a mostrar, quando o servidor permite substituições.
attachmentsAnexos da mensagem, apresentados como cartões (abaixo).
propsPropriedades adicionais guardadas na mensagem, para aplicações que as leem.
typeUm tipo de mensagem personalizado, como custom_poll, para uma aplicação que o apresenta. Os tipos que começam com system_ são recusados.
root_idO ID da primeira mensagem de um tópico, para responder nesse tópico.
priorityPrioridade da mensagem, por exemplo {"priority": "urgent"}.
silenttrue envia a mensagem sem notificações push.

Toda a mensagem publicada por um webhook tem uma etiqueta BOT ao lado do nome, mesmo quando aparece com o nome do seu criador, para que as pessoas a distinguam de uma mensagem de uma pessoa real. As mensagens de webhook nunca ativam webhooks de saída.

Nome e ícone

Um webhook publica como a pessoa que o criou, a menos que o servidor permita substituições. Duas definições em Consola do sistema › Integrações › Gestão de integrações, ambas desativadas por predefinição, alteram isso:

  • Ativar integrações para substituir nomes de utilizador (ServiceSettings.EnablePostUsernameOverride): o Nome de utilizador do webhook ou o username da solicitação são apresentados; na ausência de ambos, o nome é webhook.
  • Permitir que as integrações substituam os ícones das fotos de perfil (ServiceSettings.EnablePostIconOverride): a Imagem do perfil do webhook, ou o icon_url ou icon_emoji da solicitação, são apresentados.

Um valor na solicitação tem prioridade sobre o guardado no webhook.

Anexos

Os anexos adicionam conteúdo estruturado abaixo do texto. Seguem o formato de anexos de mensagem do Slack:

{
  "text": "Build failed on `main`",
  "attachments": [{
    "fallback": "main #1902 failed",
    "color": "#d24b4e",
    "author_name": "GitHub Actions",
    "author_icon": "https://chat.example.com/static/ci.png",
    "title": "main #1902 failed",
    "title_link": "https://ci.example.com/runs/1902",
    "text": "Commit `0f4d8e2` by Jonas Weber",
    "fields": [
      {"title": "Branch", "value": "zephyr-3.7", "short": true},
      {"title": "Duration", "value": "3m 40s", "short": true}
    ],
    "footer": "Lumen CI",
    "ts": 1760000000
  }]
}
CampoSignificado
fallbackTexto simples para locais que não podem mostrar o cartão, como notificações.
colorA barra de cor: uma cor em hexadecimal, ou good, warning, danger.
pretextTexto acima do cartão.
author_name, author_link, author_iconUma linha de cabeçalho pequena.
title, title_linkO título do cartão, opcionalmente um link.
textO corpo do cartão, em Markdown.
fieldsUma lista de {"title", "value", "short"}; os campos short ficam lado a lado.
image_url, thumb_urlUma imagem grande abaixo, ou uma pequena ao lado.
footer, footer_icon, tsUma linha de rodapé e um tempo (segundos Unix).
actionsBotões e menus: ver Mensagens interativas.

Cartões: etapas e gráficos

O Antimatter adiciona dois campos aos anexos, desenhados pela interface predefinida:

  • stages: as etapas de um pipeline, apresentadas como pílulas, até 20. Cada uma é {"name", "status"}, com um estado de passed, failed, running, pending, skipped ou canceled.
  • chart: um pequeno gráfico de linha com os valores mais recentes, {"values": [2 to 200 numbers], "threshold": number, "label": "…"}. O limite é representado por uma linha tracejada, que corresponde ao limite de alerta.
{
  "attachments": [{
    "color": "danger",
    "title": "main #1902 failed",
    "stages": [
      {"name": "build", "status": "failed"},
      {"name": "unit", "status": "skipped"},
      {"name": "publish", "status": "skipped"}
    ]
  }, {
    "color": "warning",
    "title": "API latency above 400 ms",
    "chart": {"values": [210, 230, 260, 390, 450, 470], "threshold": 400, "label": "p95 latency, ms"}
  }]
}

A interface clássica e outros clientes ignoram estes dois campos, por isso coloque também o essencial em text ou fields.

Criar um webhook de saída

Um webhook de saída vigia os canais Público de uma comunidade. Envia uma mensagem para a sua URL quando a primeira palavra da mensagem corresponde a uma das suas palavras de ativação, ou quando alguém o escolhe no menu da mensagem (uma ação de mensagem).

  1. Em Integrações, escolha Webhooks de saída e, em seguida, Adicionar webhook de saída.
  2. Preencha o Título e a Descrição.
  3. Escolha o Tipo de conteúdo da solicitação: application/x-www-form-urlencoded ou application/json.
  4. Escolha um Canal, insira Palavras de gatilho (uma por linha) ou ambos. Com um canal e sem palavras de gatilho, todas as mensagens desse canal são enviadas. Com ambos, a mensagem tem de corresponder a ambos.
  5. Em Gatilhar quando, escolha A primeira palavra corresponde exatamente a uma palavra de ativação, A primeira palavra começa com uma palavra de ativação ou A partir do menu de mensagens. Com A partir do menu de mensagens, as palavras de ativação desaparecem, o Canal é opcional (sem ele, a ação é oferecida em todos os canais públicos da comunidade) e o Título é obrigatório: é o nome da ação no menu.
  6. Insira as URLs de callback (uma por linha). Cada URL recebe todas as mensagens correspondentes.
  7. Defina opcionalmente o Nome de utilizador e a Imagem do perfil usados para as respostas. Para uma ação a partir do menu de mensagens, o campo Ícone substitui Imagem do perfil: a URL de uma imagem .png ou .jpg ou, na interface predefinida do Antimatter, uma imagem carregada. O menu mostra-a a cinzento; sem uma, a ação recebe um ícone neutro.
  8. Escolha Guardar e copie o token apresentado. O seu serviço usa-o para verificar que as solicitações provêm do seu servidor.

Através da API: POST /api/v4/hooks/outgoing com {"team_id", "channel_id", "trigger_words", "trigger_when", "callback_urls", "content_type", "display_name"} (trigger_when: 0 para correspondência exata, 1 para "começa com", 2 para "a partir do menu da mensagem"). Um webhook com trigger_when 2 não requer palavras de gatilho e necessita de um display_name. Carregue o respetivo ícone com POST /api/v4/hooks/outgoing/{hook_id}/icon (multipart, campo image: PNG, JPEG ou GIF, armazenado como PNG de 128 px); o respetivo icon_url passa então a apontar para GET /api/v4/hooks/outgoing/{hook_id}/icon.

Se o seu serviço estiver numa rede privada, um administrador do sistema tem de permitir o seu endereço em Permitir ligações internas não fiáveis a (ServiceSettings.AllowedUntrustedInternalConnections). O servidor não chama endereços privados caso contrário.

O pedido de saída

O Antimatter envia um POST com estes campos, como JSON ou codificado em formulário, consoante o tipo de conteúdo:

{
  "token": "6kd1ofo5ojr5zgu5ipfipybgnc",
  "team_id": "…",
  "team_domain": "lumen-labs",
  "channel_id": "…",
  "channel_name": "ops-alerts",
  "timestamp": 1760010000000,
  "user_id": "…",
  "user_name": "priya",
  "post_id": "…",
  "text": "deploy api 2.4.0",
  "trigger_word": "deploy",
  "file_ids": ""
}

Compare token com o token do webhook antes de fazer qualquer coisa. Na versão codificada em formulário, timestamp está em segundos, não em milissegundos. O servidor aguarda a sua resposta pelo tempo que Tempo limite de pedido de integração permitir (30 segundos por omissão, ServiceSettings.OutgoingIntegrationRequestsTimeout).

Responder a um webhook de saída

Para publicar uma resposta, responda com JSON. Para não publicar nada, responda com um corpo vazio.

{
  "text": "Deploying **api 2.4.0** to staging…",
  "response_type": "comment",
  "username": "deploy-bot",
  "icon_url": "https://chat.example.com/static/deploy.png"
}
  • text e attachments: a resposta. Sem nenhum dos dois, nada é publicado.
  • response_type: comment publica a resposta no tópico da mensagem que a desencadeou; caso contrário, vai para o canal.
  • username, icon_url: mostrados quando o servidor permite substituições; caso contrário, são usados os próprios valores do webhook.
  • props, type, priority: como nos webhooks de entrada.

A resposta é publicada como o criador do webhook, com o nome substituído, quando houver. Para qualquer coisa mais longa do que uma resposta rápida, responda de imediato e publique mais tarde com um webhook de entrada ou com a API.

Ações da mensagem

Uma ação da mensagem é um webhook de saída com Gatilhar quando definido como A partir do menu da mensagem. Nunca é ativada por mensagens publicadas. Em vez disso, as pessoas veem-na no menu de uma mensagem: Ações da mensagem (⋯) › Mais ações, sob o título Integrações, com o respetivo ícone. A interface clássica não mostra ações da mensagem. Ao selecioná-la, essa mensagem é enviada para as URLs de callback, e uma notificação breve indica Enviado para o nome da ação, ou mostra o seu ephemeral_text.

  • Onde é mostrada: no canal do webhook ou, na ausência de canal, em todos os canais públicos da respetiva comunidade. Apenas os canais públicos têm ações da mensagem.
  • Os Convidados apenas obtêm as ações ligadas a um canal em que estão, e não as de âmbito comunitário.
  • Canais arquivados e de apenas leitura: a mensagem ainda pode ser enviada. Arquivar um canal elimina os webhooks ligados a ele, pelo que um canal arquivado mantém apenas as ações de âmbito comunitário. Uma resposta que a pessoa não pôde publicar é-lhe mostrada apenas a ela.
  • Limite de taxa: cada pessoa pode executar 10 ações da mensagem por minuto, após um impulso de 5. Acima disso, o servidor responde com 429.
  • Redes privadas: tal como para outros webhooks de saída, os endereços internos devem ser permitidos em Permitir ligações internas não fiáveis a.

O pedido

O payload é enviado como JSON ou codificado em formulário, conforme o tipo de conteúdo do webhook:

{
  "token": "6kd1ofo5ojr5zgu5ipfipybgnc",
  "trigger": "message_action",
  "team_id": "…",
  "team_domain": "lumen-labs",
  "channel_id": "…",
  "channel_name": "ops-alerts",
  "timestamp": 1760010000000,
  "user_id": "…",
  "user_name": "priya",
  "post_id": "…",
  "post_root_id": "",
  "post_create_at": 1760009000000,
  "post_user_id": "…",
  "post_user_name": "jonas",
  "permalink": "https://chat.example.com/lumen-labs/pl/…",
  "text": "The staging database is full again",
  "files": [{"id": "…", "name": "df.txt", "link": "https://chat.example.com/api/v4/files/…"}],
  "priority": "",
  "requested_ack": false,
  "burn_on_read": false
}
  • user_id e user_name são a pessoa que selecionou a ação; post_user_id e post_user_name são o autor da mensagem.
  • Apenas essa mensagem é enviada, nunca o resto do respetivo tópico.
  • files lista os ficheiros da mensagem apenas quando a pessoa que selecionou a ação pode descarregá-los. Descarregar um link requer uma sessão ou um token, tal como qualquer ficheiro da API.
  • Uma mensagem que se queima após a leitura é enviada sem o seu texto nem ficheiros, com burn_on_read definido como true.
  • Em codificação de formulário, os ficheiros são enviados como campos repetidos file_id, file_name e file_link, na mesma ordem.

A resposta

Responder como para qualquer webhook de saída (ver Responder a um webhook de saída): text ou attachments publica uma resposta no canal, e "response_type": "comment" publica-a no tópico da mensagem. Também pode responder com ephemeral_text (limitado a 300 caracteres): é mostrado apenas à pessoa que selecionou a ação, na notificação. Um corpo vazio não publica nada.

{"ephemeral_text": "Ticket OPS-412 created"}

Com a API

  • GET /api/v4/channels/{channel_id}/message_actions: as ações que pode selecionar num canal, com id, display_name, description e icon_url. Não são devolvidos tokens nem URLs de callback.
  • POST /api/v4/posts/{post_id}/message_actions/{hook_id}: executa uma ação numa mensagem que pode ler. Responde com {"ephemeral_text"}.

Quando um webhook não funciona

  • 501 do URL do hook: os webhooks de entrada estão desativados no servidor.
  • 400 "Failed to decode the payload…": o JSON é inválido, ou o campo do formulário não se chama payload. "No text specified." significa que o pedido não tem nem text nem attachments.
  • 403: o webhook está bloqueado para outro canal, ou o seu proprietário já não pode ler o canal.
  • Um webhook de saída nunca é disparado: o canal é privado, o gatilho não é a primeira palavra, a mensagem veio de um webhook, ou o webhook está definido para ser disparado A partir do menu da mensagem.
  • Uma ação de mensagem não está no menu: o canal é privado, a ação está associada a outro canal, a pessoa é um convidado e a ação não tem canal, ou os webhooks de saída estão desativados.
  • 404 ao executar uma ação de mensagem: a ação não existe, ou não cobre o canal dessa mensagem para essa pessoa. 429: a pessoa executou mais de 10 ações num minuto. 502: nenhuma das URLs de callback pôde ser alcançada (o registo contém o motivo).
  • Registos: com Ativar depuração de webhooks ligado (LogSettings.EnableWebhookDebugging) e o nível de registo em DEBUG, o servidor regista os payloads dos pedidos de entrada com falha. As chamadas de saída com falha são registadas como Outgoing Webhook POST failed.