Diseñar un flujo de reporte de incidentes con modal_body
Compón un botón de 'Report incident' impulsado por un modal, valida sus bloques, y reenvía las respuestas enviadas a tu propio servidor o directamente a un workflow — contrato completo de modal_body.
Un botón de reporte de incidentes que solo dice “Report incident” y publica un mensaje en blanco en un canal pierde lo que más importa: la estructura. modal_body convierte ese botón en un pequeño formulario — de 1 a 10 campos, abierto en línea, reenviado a donde sea que viva tu proceso de triage.
El botón y su modal
Adjunta modal_body a un botón interactive. El modal se abre al hacer clic, antes de que dispare cualquier callback:
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 — el contrato completo
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
title |
string, ≤ 200 caracteres | sí | Título del modal |
submit_label |
string, ≤ 200 caracteres | no | Por defecto "Submit" |
blocks |
arreglo, 1–10 elementos | sí | Ver tipos de bloque abajo |
Solo existen tres tipos de bloque — nada más valida:
type |
Propósito | Campos clave |
|---|---|---|
text |
Copy solo de visualización | text (≤ 3000 caracteres) |
input |
Un campo con etiqueta | name, label, multiline?, required?, placeholder?, max_length? (≤ 3000), default? |
divider |
Separador visual | — |
input.name debe coincidir con ^[a-z][a-z0-9_]{0,62}$ y ser único dentro del modal — es la clave bajo la cual llega de vuelta cada valor enviado. Todo el modal_body, serializado, debe pesar ≤ 8 KiB. Slack renderiza el modal de forma nativa; otras plataformas recolectan los mismos campos de forma conversacional (un prompt por campo, en el orden de los bloques).
Requerir un callback — o no
Un modal con algún bloque input requiere callback_url o callback_workflow en el mismo botón — Dailybot necesita algún lugar donde entregar las respuestas. Omitir ambos con un input presente devuelve 400 button_modal_body_invalid, con el detalle input_without_callback. Un modal solo de visualización (solo bloques text y divider, sin input) no necesita ninguno: hacer clic en Submit simplemente lo cierra.
Otras fallas de validación colapsan al mismo código button_modal_body_invalid: un type de bloque no soportado, 11 o más bloques, o un name faltante o duplicado.
Reenviar a tu propio servidor (callback_url)
Al enviarse, Dailybot hace POST a callback_url con event: "modal_submit" y las respuestas bajo 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"
}
Esto lleva el mismo header HMAC X-Dailybot-Signature que cualquier otro envío de callback_url — verifícalo antes de confiar en modal_fields, exactamente como lo harías con el clic de un botón simple (ver la guía a fondo de puertas de aprobación de CI/CD para el snippet completo de verificación). Tu servicio de intake lee modal_fields.summary y modal_fields.severity, abre un ticket, y puede responder de forma asíncrona editando el mensaje original (reutilizando bot_message_id dentro de las 72 horas) o enviando una actualización nueva en hilo.
Reenviar directamente a un workflow (callback_workflow)
Sáltate el servidor externo por completo y enruta los envíos a un workflow api_trigger en su lugar — los valores de los campos llegan 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 de los pasos del workflow, {{trigger.fields.summary}} y {{trigger.fields.severity}} están disponibles para componer un mensaje de seguimiento, prellenar una respuesta de formulario, o alimentar un prompt de IA de triage — sin ningún secreto de firma involucrado, ya que el disparo se mantiene enteramente interno a Dailybot.
Elegir entre callback_url y callback_workflow para intake
callback_url |
callback_workflow |
|
|---|---|---|
| Dónde vive la lógica de triage | Tu propio servicio | Dentro de un workflow api_trigger de Dailybot |
| Necesita verificación de firma | Sí — siempre | No — es interno, sin firma |
| Ideal para | Sistemas de ticketing existentes, integraciones estilo PagerDuty | Mantener todo el flujo dentro de Dailybot (enrutar a un canal, abrir un formulario, notificar a un equipo on-call) |
Ambos son mutuamente excluyentes entre sí y con callback_form / callback_command / callback_prompt — un botón lleva exactamente uno.
CLI: pass-through completo de JSON
Los flags ergonómicos de botón (--approve-button, --workflow-button) no expresan un modal — usa --buttons para el 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}
]}}]'
Referencias cruzadas
- La API send-message, de punta a punta para el contrato completo de botones al que se adjunta
modal_body. - Disparar workflows desde un botón de chat para el camino de
callback_workflowa fondo. /es/developers/api/messaging#send-messagepara la referencia estructurada del campomodal_body.
Explora el hub de Soluciones para el recorrido orientado a producto de un flujo de reporte de incidentes impulsado por un modal.
FAQ
- ¿Qué tipos de bloque puede usar un modal_body?
- Exactamente tres: text (copy solo de visualización), input (un campo con etiqueta, opcionalmente multilínea y requerido), y divider. De 1 a 10 bloques en total, y el modal_body serializado debe pesar 8 KiB o menos.
- ¿A dónde llegan las respuestas enviadas del modal?
- Como modal_fields.<name> en el cuerpo del POST a callback_url (junto con event: "modal_submit"), o como {{trigger.fields.<name>}} dentro de los pasos de un workflow disparado cuando el botón usó callback_workflow en su lugar.
- ¿Puede existir un modal sin ningún callback?
- Solo si no tiene bloques input — un modal solo de visualización (solo bloques text y divider) no necesita ni callback_url ni callback_workflow; hacer clic en Submit simplemente lo cierra. Un modal con algún bloque input y sin callback devuelve 400 button_modal_body_invalid con el detalle input_without_callback.
- ¿Cuáles son las reglas de nombres para un bloque input?
- name debe coincidir con ^[a-z][a-z0-9_]{0,62}$ y ser único dentro del modal. Violar eso, tener 11 o más bloques, un tipo de bloque no soportado, o un name faltante o duplicado, todos devuelven 400 button_modal_body_invalid.
- ¿Puedo combinar un formulario interno con un modal para el mismo flujo de intake?
- No en el mismo botón — callback_form y modal_body cumplen trabajos distintos. callback_form abre un formulario completo de Dailybot (con su propio ciclo de vida, estados de workflow y canales de reporte); modal_body es una captura ligera, de 1 a 10 campos, adjunta directamente a un botón y reenviada a callback_url o callback_workflow. Elige modal_body para una captura estructurada rápida, y el patrón callback_form de la guía a fondo de formularios cuando necesites el ciclo de vida completo del formulario.