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.
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"}
]
}}
]
}'
modal_body — o contrato completo
| 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
- A API send-message, de ponta a ponta para o contrato completo de botões ao qual
modal_bodyse anexa. - Disparando workflows a partir de um botão de chat para o caminho
callback_workflowem profundidade. /pt/developers/api/messaging#send-messagepara a referência estruturada do campomodal_body.
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.