Skip to content
Menu Academia

Projetando um fluxo de intake de incidentes com modal_body

Componha um botão de 'Reportar incidente' guiado por modal, valide seus blocos e encaminhe as respostas enviadas para o seu próprio servidor ou direto para um workflow — contrato completo do modal_body.

deep-diveDesenvolvedorOps9 min read

Um botão de intake de incidentes que apenas diz “Reportar incidente” e publica uma mensagem em branco em um canal perde a coisa mais importante: estrutura. modal_body transforma esse botão em um pequeno formulário — de 1 a 10 campos, aberto inline, encaminhado para onde quer que viva o seu processo de triagem.

O botão e seu modal

Anexe modal_body a um botão interactive. O modal abre no clique, antes de qualquer callback disparar:

curl -sS -X POST 'https://api.dailybot.com/v1/send-message/' \
  -H 'X-API-KEY: $DAILYBOT_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "message": "Something wrong? Report it here.",
    "target_channels": ["C0123456789"],
    "buttons": [
      {"label": "Report incident", "button_type": "interactive", "value": "report_incident",
       "callback_url": "https://ops.example.com/hooks/incident-intake",
       "modal_body": {
         "title": "New incident",
         "submit_label": "Submit",
         "blocks": [
           {"type": "text", "text": "Give us the essentials — triage picks it up from here."},
           {"type": "input", "name": "summary", "label": "What happened?", "multiline": true, "required": true},
           {"type": "input", "name": "severity", "label": "Severity (1-4)", "required": true},
           {"type": "divider"}
         ]
       }}
    ]
  }'
Campo Tipo Obrigatório Notas
title string, ≤ 200 caracteres sim Título do modal
submit_label string, ≤ 200 caracteres não Padrão "Submit"
blocks array, 1–10 itens sim Veja os tipos de bloco abaixo

Existem apenas três tipos de bloco — nada mais é validado:

type Propósito Campos-chave
text Texto apenas para exibição text (≤ 3000 caracteres)
input Um campo rotulado name, label, multiline?, required?, placeholder?, max_length? (≤ 3000), default?
divider Separador visual

input.name deve corresponder a ^[a-z][a-z0-9_]{0,62}$ e ser único dentro do modal — é a chave sob a qual cada valor enviado retorna. O modal_body inteiro, serializado, deve ter ≤ 8 KiB. O Slack renderiza o modal nativamente; outras plataformas coletam os mesmos campos de forma conversacional (um prompt por campo, na ordem dos blocos).

Exigindo um callback — ou não

Um modal com qualquer bloco input exige callback_url ou callback_workflow no mesmo botão — o Dailybot precisa de algum lugar para entregar as respostas. Omitir ambos com um input presente resulta em 400 button_modal_body_invalid, com o detalhe input_without_callback. Um modal apenas de exibição (só blocos text e divider, sem input) não precisa de nenhum dos dois: clicar em Enviar simplesmente o fecha.

Outras falhas de validação recaem no mesmo código button_modal_body_invalid: um type de bloco não suportado, 11 ou mais blocos, ou um name ausente ou duplicado.

Encaminhando para o seu próprio servidor (callback_url)

No envio, o Dailybot faz POST para callback_url com event: "modal_submit" e as respostas em modal_fields:

{
  "event": "modal_submit",
  "bot_message_id": "$db/...",
  "button": {"id": "$btn/...", "value": "report_incident"},
  "user": {"id": "...", "email": "...", "display_name": "...", "external_id": "U..."},
  "organization": {"id": "...", "name": "..."},
  "platform": "slack",
  "channel": {"id": "C0123456789", "type": "channel"},
  "modal_fields": {
    "summary": "Checkout API returning 500s intermittently since 14:05 UTC.",
    "severity": "2"
  },
  "metadata": {},
  "sent_at": "2026-07-22T14:30:00Z",
  "clicked_at": "2026-07-22T14:31:12Z"
}

Isso carrega o mesmo header HMAC X-Dailybot-Signature que qualquer outro dispatch de callback_url — verifique-o antes de confiar em modal_fields, exatamente como faria com um clique de botão simples (veja o aprofundamento sobre gates de aprovação de CI/CD para o trecho de verificação completo). Seu serviço de intake lê modal_fields.summary e modal_fields.severity, abre um ticket e pode responder de forma assíncrona editando a mensagem original (reutilizando bot_message_id dentro de 72 horas) ou enviando uma nova atualização em thread.

Encaminhando direto para um workflow (callback_workflow)

Pule o servidor externo por completo e roteie os envios para um workflow api_trigger em vez disso — os valores dos campos chegam como {{trigger.fields.<name>}}:

{"label": "Report incident", "button_type": "interactive", "value": "report_incident",
 "callback_workflow": "$INCIDENT_TRIAGE_WORKFLOW_UUID",
 "modal_body": {
   "title": "New incident",
   "blocks": [
     {"type": "input", "name": "summary", "label": "What happened?", "multiline": true, "required": true},
     {"type": "input", "name": "severity", "label": "Severity (1-4)", "required": true}
   ]
 }}

Dentro dos passos do workflow, {{trigger.fields.summary}} e {{trigger.fields.severity}} ficam disponíveis para compor uma mensagem de acompanhamento, pré-preencher a resposta de um formulário, ou alimentar um prompt de triagem de IA — sem segredo de assinatura envolvido, já que o disparo permanece inteiramente interno ao Dailybot.

Escolhendo entre callback_url e callback_workflow para intake

callback_url callback_workflow
Onde vive a lógica de triagem No seu próprio serviço Dentro de um workflow api_trigger do Dailybot
Exige verificação de assinatura Sim — sempre Não — interno, sem assinatura
Melhor para Sistemas de ticketing existentes, integrações estilo PagerDuty Manter todo o fluxo dentro do Dailybot (rotear para um canal, abrir um formulário, notificar uma equipe de plantão)

Ambos são mutuamente exclusivos entre si e com callback_form / callback_command / callback_prompt — um botão carrega exatamente um.

CLI: pass-through completo de JSON

As flags de botão ergonômicas (--approve-button, --workflow-button) não expressam um modal — use --buttons para o contrato completo:

dailybot chat send -c C0123456789 -m "Something wrong? Report it here." \
  --buttons '[{"label":"Report incident","button_type":"interactive","value":"report_incident",
    "callback_url":"https://ops.example.com/hooks/incident-intake",
    "modal_body":{"title":"New incident","submit_label":"Submit",
      "blocks":[
        {"type":"text","text":"Give us the essentials — triage picks it up from here."},
        {"type":"input","name":"summary","label":"What happened?","multiline":true,"required":true},
        {"type":"input","name":"severity","label":"Severity (1-4)","required":true}
      ]}}]'

Referências cruzadas

Navegue pelo hub de Soluções para o passeio voltado ao produto de um fluxo de intake de incidentes guiado por modal.

FAQ

Quais tipos de bloco um modal_body pode usar?
Exatamente três: text (texto apenas para exibição), input (um campo rotulado, opcionalmente multiline e required) e divider. De 1 a 10 blocos no total, e o modal_body serializado deve ter 8 KiB ou menos.
Onde acabam as respostas enviadas pelo modal?
Como modal_fields.<name> no corpo do POST de callback_url (junto com event: "modal_submit"), ou como {{trigger.fields.<name>}} dentro dos passos de um workflow disparado quando o botão usava callback_workflow em vez disso.
Um modal pode existir sem nenhum callback?
Somente se não tiver blocos input — um modal apenas de exibição (só blocos text e divider) não precisa de callback_url nem de callback_workflow; clicar em Enviar simplesmente o fecha. Um modal com qualquer bloco input e sem callback retorna 400 button_modal_body_invalid com o detalhe input_without_callback.
Quais são as regras de nomenclatura para um bloco input?
name deve corresponder a ^[a-z][a-z0-9_]{0,62}$ e ser único dentro do modal. Violar isso, ter 11 ou mais blocos, um tipo de bloco não suportado, ou um name ausente ou duplicado — tudo isso retorna 400 button_modal_body_invalid.
Posso combinar um formulário interno com um modal para o mesmo fluxo de intake?
Não no mesmo botão — callback_form e modal_body atendem a propósitos diferentes. callback_form abre um formulário completo do Dailybot (com seu próprio ciclo de vida, estados de workflow e canais de relatório); modal_body é uma captura leve, de 1 a 10 campos, anexada diretamente a um botão e encaminhada para callback_url ou callback_workflow. Escolha modal_body para uma captura estruturada rápida, e o padrão callback_form do aprofundamento de formulários quando precisar do ciclo de vida completo do formulário.