Skip to content
Menu Academia

A API send-message a fundo: botões, callbacks e modais

Um guia de implementação completo do POST /v1/send-message/ — anatomia do request, threads, identidade, os cinco tipos de callback, modal_body, códigos de erro e os flags de CLI que os geram.

deep-diveDesenvolvedorOps11 min read

POST /v1/send-message/ é um único endpoint que faz cinco trabalhos: envia uma mensagem, encadeia um relatório em thread, personifica uma identidade, edita um envio anterior e — a parte que a maioria das equipes subutiliza — transforma a mensagem em uma pequena peça de UI. Este guia é a versão em nível de implementação do caso de uso principal de automação de chat: cada campo, cada tipo de callback, cada código de erro e os flags de CLI que montam o mesmo payload sem escrever JSON manualmente.

Anatomia do request

O corpo do request se agrupa em seis frentes. Você raramente usará todas em uma única chamada, mas toda integração em produção acaba tocando a maioria delas ao longo do tempo.

Grupo Campos
Targeting target_users, target_channels, target_teams, skip_users_on_time_off
Conteúdo message, messages, image_url, metadata
Threads bot_message_id, thread_responses
Identidade send_as_user, platform_settings
Botões buttons (array de objetos Button)
Configurações de plataforma platform_settings (overrides exclusivos do Slack)

É necessário pelo menos um de target_users / target_channels / target_teams (missing_targets caso contrário). target_channels aceita uma string simples com o id do canal, ou um objeto { id, channel_type?, thread? } — use thread para responder dentro de uma thread existente da plataforma em vez de iniciar uma nova. É necessário message (string) ou messages (array de payloads por plataforma).

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":"Deploy finished","target_channels":["C0123456789"]}'

A resposta é {"bot_message_id": "$db/<uuid>"} — um id gerado pelo servidor que você guarda para editar depois, ou o valor que você mesmo enviou no request para controlá-lo.

Threads em uma única chamada

thread_responses publica uma mensagem pai mais até 10 respostas, um nível de profundidade, em um único request — sem chamadas adicionais:

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": "Daily report — 2026-07-24",
    "target_channels": ["C0123456789"],
    "bot_message_id": "daily-report-2026-07-24",
    "thread_responses": [
      {"message": "alice: shipped the billing migration"},
      {"message": "bob: on PTO, no updates"}
    ]
  }'

A resposta retorna bot_message_id para o pai mais um id por resposta em thread_responses — cada um editável de forma independente depois, com o mesmo truque de reutilizar bot_message_id. O threading é renderizado nativamente em canais e DMs do Slack; no Microsoft Teams, Discord e Google Chat, as respostas se aninham dentro de canais mas chegam planas em DMs (ainda chegam, só sem aninhamento visual).

Editando no lugar

Faça POST novamente com o mesmo bot_message_id dentro de 72 horas para editar aquela mensagem em vez de enviar uma nova:

curl -sS -X POST 'https://api.dailybot.com/v1/send-message/' \
  -H 'X-API-KEY: $DAILYBOT_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"target_channels":["C0123456789"],"bot_message_id":"daily-report-2026-07-24","message":"Daily report — 2026-07-24 (updated)"}'

Os overrides de identidade (bot_username, bot_icon_url, bot_icon_emoji) são ignorados ao editar — a plataforma mantém a identidade original da mensagem. Os botões são preservados (round-trip): os existentes sobrevivem à edição a menos que você envie novos flags de botões.

Identidade: bot personalizado vs. send-as-user

Duas formas independentes de mudar de quem parece vir uma mensagem:

  • Identidade de bot personalizada (qualquer plano) — platform_settings.bot_username mais bot_icon_url ou bot_icon_emoji (somente Slack, requer o escopo chat:write.customize; sem ele, o Slack cai silenciosamente na identidade de bot padrão e a chamada ainda retorna ok: true).
  • send_as_user (somente Slack, somente admin) — publica com o nome e avatar do Slack de um colega real. A mensagem ainda sai pelo token do bot do Dailybot, não pelo do usuário.

São mutuamente exclusivos (send_as_user_conflict). Um UUID inválido em send_as_user falha no lado do cliente; um UUID bem formado que não resolve a um usuário retorna send_as_user_not_found; um chamador que não é admin recebe 403 org_admin_required.

dailybot chat send -c C0123 -m "Deploying the hotfix now" \
  --send-as-user 294bf2cc-e3c7-401d-a1d6-bf20aa64bb33

Os cinco tipos de callback

Cada botão interativo carrega exatamente um dos cinco callbacks — combinar dois retorna 400 button_callback_conflict:

Callback Dispara Assinado?
callback_url Seu próprio endpoint HTTPS Sim — HMAC + callback_auth opcional
callback_form Abre um formulário interno do Dailybot Não — totalmente interno
callback_command Executa um comando ChatOps conhecido como quem clicou Não — totalmente interno
callback_prompt Envia um prompt de texto livre para a IA do Dailybot como quem clicou Não — totalmente interno
callback_workflow Dispara um workflow api_trigger como quem clicou Não — totalmente interno

callback_url — seu próprio servidor

{"label": "Approve", "button_type": "interactive", "value": "approve",
 "callback_url": "https://ci.example.com/hooks/deploy",
 "callback_auth": {"type": "bearer", "token": "$DEPLOY_CALLBACK_TOKEN"}}

Ao clicar, o Dailybot faz POST de um corpo assinado para o seu callback_url:

{
  "event": "button_click",
  "bot_message_id": "$db/ae007b43-...",
  "button": {"id": "$btn/...", "value": "approve"},
  "user": {"id": "...", "email": "...", "display_name": "...", "external_id": "U01ABCDEFG"},
  "organization": {"id": "...", "name": "..."},
  "platform": "slack",
  "channel": {"id": "D0123456", "type": "im"},
  "modal_fields": null,
  "metadata": {"campaign": "coffee-approvals"},
  "sent_at": "2026-07-22T14:30:00Z",
  "clicked_at": "2026-07-22T14:31:12Z"
}

platform é um de slack, msteams, discord ou hangouts — o Google Chat é reportado como hangouts, compare com esse valor. callback_auth (bearer / basic / custom_header) é auth de transporte estática adicional, válida apenas com callback_url; a assinatura HMAC está sempre ativa independentemente disso.

callback_form — abrir um formulário interno

{"label": "Submit expense", "button_type": "interactive", "value": "expense_form",
 "callback_form": "$EXPENSE_FORM_UUID"}

Somente UUID — nomes e slugs são rejeitados. Formulário desconhecido/arquivado → 400 button_callback_form_not_found. Nenhum request externo é disparado; o próprio ciclo de vida do formulário (veja o aprofundamento de formulários) assume o controle a partir daí.

callback_command — executar um comando ChatOps

{"label": "Run help", "button_type": "interactive", "value": "help",
 "callback_command": "help"}

Executa como o usuário que clicou (suas permissões, sua cota) — máximo 200 caracteres. O prefixo legado "prompt: …" é rejeitado (400 button_callback_command_invalid); use callback_prompt em vez disso.

callback_prompt — repassar o clique para a IA

{"label": "Summarize thread", "button_type": "interactive", "value": "summarize",
 "callback_prompt": "Summarize the last 20 messages in this channel."}

Em branco ou muito longo (> 2000 caracteres) → 400 button_callback_prompt_invalid.

callback_workflow — disparar um workflow

{"label": "Trigger rollback", "button_type": "interactive", "value": "rollback",
 "callback_workflow": "$ROLLBACK_WORKFLOW_UUID"}

Somente workflows com tipo de evento api_trigger são elegíveis — encontre-os com dailybot workflow list --filter api_trigger. UUID desconhecido/inativo/de outra organização → 400 button_callback_workflow_not_found. Os passos do workflow disparado leem o contexto do clique via {{trigger.*}}: {{trigger.button_value}}, {{trigger.button_id}}, {{trigger.fields.<name>}} (inputs do modal), {{trigger.user.*}} e mais — referência completa em /pt/developers/api/workflows.

Anexe modal_body a um botão interativo para abrir primeiro um modal leve. Existem apenas três tipos de bloco: text, input, divider — de 1 a 10 blocos, tamanho serializado ≤ 8 KiB.

{
  "title": "Log an update",
  "submit_label": "Save",
  "blocks": [
    {"type": "text", "text": "Share a quick status update."},
    {"type": "input", "name": "update_text", "label": "Update", "multiline": true, "required": true},
    {"type": "divider"}
  ]
}

name deve corresponder a ^[a-z][a-z0-9_]{0,62}$ e ser único no modal — os valores enviados chegam como modal_fields.<name> em callback_url, ou {{trigger.fields.<name>}} em callback_workflow. Um modal com blocos input requer callback_url ou callback_workflow (input_without_callback); um modal somente de exibição (text/divider) não requer nenhum dos dois.

response — auto-respostas instantâneas

Qualquer botão pode carregar um objeto response, mostrado a quem clicou imediatamente (em paralelo com o dispatch do callback_url, nunca condicionado ao seu resultado — o padrão canônico de aprovação lenta):

{"message": "Approved — deploying now.", "buttons": [], "replace_original": false, "ephemeral": false}

response.buttons é recursivo (mesmo schema Button), com limite de profundidade de aninhamento de 3, 25 botões por nível, 16 KiB por botão de nível superior.

Códigos de erro em resumo

code Significado
button_link_and_callback_conflict Um botão link misturou um callback ou value junto com url
button_callback_conflict Mais de um dos cinco callbacks no mesmo botão
buttons_count_out_of_range Mais de 25 botões em uma mensagem
button_modal_body_invalid Tipo de bloco inválido, ≥ 11 blocos, ou um modal com inputs sem callback
button_callback_auth_invalid callback_auth configurado sem callback_url, ou malformado
send_as_user_conflict send_as_user combinado com bot_username/bot_icon_*

Flags de CLI: os mesmos payloads sem JSON escrito à mão

# Fluxo de aprovação
dailybot chat send -c C0123456789 -m "Deploy 4.12.0 to production?" \
  --approve-button "Approve=approved" --reject-button "Reject=rejected" \
  --callback-url "https://ci.example.com/hooks/deploy" \
  --callback-bearer "$DEPLOY_CALLBACK_TOKEN"

# Botão que dispara um workflow
dailybot chat send -c C0123456789 -m "Ready to redeploy staging?" \
  --workflow-button "Redeploy staging=$REDEPLOY_WORKFLOW_UUID"

# Qualquer coisa que os flags abreviados não conseguem expressar — pass-through completo de JSON
dailybot chat send -c C0123456789 -m "Log a quick update" \
  --buttons '[{"label":"Add update","button_type":"interactive","value":"log_update",
    "callback_url":"https://ci.example.com/hooks/log-update",
    "modal_body":{"title":"Log an update","submit_label":"Save",
      "blocks":[{"type":"input","name":"update_text","label":"Update","multiline":true,"required":true}]}}]'

--buttons repassa as chaves sem alterá-las, incluindo campos futuros que os flags abreviados ainda não expõem — é a via de escape para tudo que este artigo mostra como JSON puro acima.

Tudo isso mapeia 1:1 com a demo interativa e as receitas de copiar-e-colar do caso de uso de automação de chat — leia essa página para a versão orientada a produto do mesmo fluxo, ou volte aqui sempre que precisar do campo exato, do código de erro exato, ou do flag de CLI exato.

FAQ

Qual é o corpo mínimo de request para POST /v1/send-message/?
Pelo menos um de target_users, target_channels ou target_teams, mais message (uma string) ou messages (um array de payloads específicos por plataforma). Todo o resto — threads, botões, overrides de identidade — é opcional.
Quantos botões interativos uma mensagem pode ter e quais limites se aplicam?
Até 25 botões por mensagem. Cada botão é exatamente button_type: "link" ou "interactive". Um botão interativo carrega no máximo um dos cinco callbacks (callback_url, callback_form, callback_command, callback_prompt, callback_workflow) — combinar dois retorna 400 button_callback_conflict.
Como verifico se um POST em callback_url realmente veio do Dailybot?
Todo POST de callback carrega X-Dailybot-Signature: t={unix_timestamp}, v1={hex_hmac}. Calcule HMAC-SHA256 sobre a string ASCII "{timestamp}.{raw_body}" usando o segredo de assinatura de callbacks da sua organização, compare em tempo constante, e rejeite assinaturas com timestamp de mais de 5 minutos.
Como edito uma mensagem que já enviei?
Faça POST novamente em /v1/send-message/ com o mesmo bot_message_id que você recebeu (ou enviou) da primeira vez, dentro de uma janela de 72 horas. A plataforma mantém a identidade original do bot — bot_username e bot_icon_* são ignorados em edições.
Qual é a diferença entre callback_workflow e callback_form em um botão?
callback_workflow dispara um workflow interno do Dailybot (apenas tipo api_trigger) totalmente no lado do servidor — sem POST externo, sem assinatura. callback_form abre um formulário interno do Dailybot para quem clicou preencher. Ambos são mutuamente exclusivos com callback_url e entre si.