Skip to content
ver .md original

Mensageria

Envie mensagens do bot para usuários, canais e times. As respostas em thread são limitadas a 10 por mensagem pai, com um único nível de aninhamento. É possível editar uma mensagem em até 72h reenviando o mesmo `bot_message_id`.

POST/v1/send-message/Chave de APICLI Authsend_message_cli

Enviar uma mensagem do bot

Envia uma mensagem para usuários, canais ou times. Anexe até 25 botões interativos por mensagem — links, URLs de callback assinadas, modais, formulários internos, comandos, prompts de IA, workflows e auto-respostas. O mesmo endpoint atende respostas em thread (até 10 respostas por mensagem-pai, um nível de profundidade), impersonação do bot via `platform_settings` (nome/avatar/ephemeral no Slack) e edição de mensagens — reenviar com o mesmo `bot_message_id` em até 72 horas edita a mensagem original.

Corpo da requisição

Destinatários

NomeTipoObrigatórioDescrição
target_usersarray<string>OpcionalUUIDs de usuário, e-mails ou IDs externos. É necessário pelo menos um de target_users / target_channels / target_teams.
target_channelsarray<string | object>OpcionalIDs de canal como strings, ou objetos { id, channel_type?, thread? }. Use thread (timestamp/id pai) para responder em uma thread existente.
target_teamsarray<string>OpcionalUUIDs de time. Todos os membros do time recebem a mensagem.
skip_users_on_time_offbooleanOpcionalQuando true, usuários marcados como OOO / de folga são ignorados.

Conteúdo

NomeTipoObrigatórioDescrição
messagestringOpcionalTexto simples ou um subconjunto HTML seguro. Obrigatório se messages não for enviado.
messagesarrayOpcionalPayloads específicos por plataforma (avançado). Obrigatório se message for omitido.
image_urlstring (https)OpcionalURL HTTPS de imagem para anexar à mensagem.
metadataobjectOpcionalMetadata personalizada livre anexada à mensagem e ecoada nos corpos de callback.

Threads e edição

NomeTipoObrigatórioDescrição
bot_message_idstringOpcionalID de idempotência personalizado. Reenvie o mesmo valor em até 72 horas para editar a mensagem original.
thread_responsesarray<{ message: string }>OpcionalAté 10 respostas publicadas sob o mesmo pai em uma única chamada. Apenas um nível de aninhamento.

Identidade e impersonação

NomeTipoObrigatórioDescrição
send_as_userstring (UUID)OpcionalSó Slack. Só admin. Publica com o nome e avatar de outro membro. Mutuamente exclusivo com platform_settings.bot_username, bot_icon_url e bot_icon_emoji.

Botões

NomeTipoObrigatórioDescrição
buttonsarray<Button>OpcionalAté 25 botões interativos ou de link. Contrato completo campo a campo: Objeto Button ↓. O servidor gera button_id como $btn/<uuid4> (ids do cliente são sobrescritos).

Configurações de plataforma

NomeTipoObrigatórioDescrição
platform_settingsobjectOpcionalOverrides de identidade/ephemeral só Slack: { bot_username?, bot_icon_url? | bot_icon_emoji?, is_ephemeral? }. Outras plataformas ignoram o bloco. Mutuamente exclusivo com send_as_user.

Objeto Button (`buttons[]`)

Contrato completo de cada entrada em buttons (máx. 25 por mensagem). Os cinco callbacks (callback_url, callback_form, callback_command, callback_prompt, callback_workflow) são mutuamente exclusivos. modal_body combina com callback_url OU callback_workflow. response combina com tudo. Veja também a seção Interactive buttons da página para a matriz, verificação de assinatura e receitas.

NomeTipoObrigatórioDescrição
labelstring (≤ 40)ObrigatórioTexto exibido no botão.
label_after_clickstring (≤ 40)OpcionalSubstitui label após o clique (ou após enviar o modal). Padrão: igual a label.
button_type"link" | "interactive"Obrigatóriolink abre url no navegador. interactive dispara uma das capacidades de callback abaixo.
urlstring (https)OpcionalObrigatório quando button_type = "link". URL que o usuário abre.
valuestring (≤ 2000)OpcionalObrigatório quando button_type = "interactive". Ecoado nos POSTs de callback. Também como {{trigger.button_value}} em workflows.
callback_urlstring (https, ≤ 2048)OpcionalPOST assinado de saída no clique e no envio do modal. Mutuamente exclusivo com os outros quatro callbacks. Ver prosa Interactive buttons para verificação HMAC.
modal_bodyobjectOpcionalAbre um modal no clique. Combina com callback_url OU callback_workflow. Modais com inputs exigem um desses (input_without_callback). Contrato completo: objeto modal_body ↓ e blocks ↓.
callback_formstring (form UUID)OpcionalAbre um formulário interno do Dailybot. Apenas UUID (liste via /v1/forms/). Mutuamente exclusivo. Desconhecido/arquivado → 400 button_callback_form_not_found.
callback_commandstring (≤ 200)OpcionalExecuta um comando conhecido do Dailybot como o clicker (ex. help). O prefixo legado "prompt: …" é rejeitado — use callback_prompt. Mutuamente exclusivo.
callback_promptstring (≤ 2000)OpcionalPrompt de IA em texto livre executado como o usuário que clica (sua cota/permissões). Vazio ou longo demais → 400 button_callback_prompt_invalid. Mutuamente exclusivo.
callback_workflowstring (workflow UUID)OpcionalDispara um workflow ativo api_trigger na org do chamador. Apenas UUID. Desconhecido/inativo/outra org → 400 button_callback_workflow_not_found. Mutuamente exclusivo.
responseobjectOpcionalAuto-resposta ao clicker: { message (≤2000, obrigatório), buttons? (recursivo), replace_original?, ephemeral? }. Combina com qualquer forma de botão. Contrato completo: objeto response ↓.
callback_authobjectOpcionalAuth de transporte estática para o POST de saída — só válida com callback_url. Aditiva à assinatura HMAC. Credenciais somente escrita. Contrato completo: objeto callback_auth ↓.
destroy_buttonbooleanOpcionalPadrão true. Se false, o botão permanece clicável; cada clique é um dispatch novo com um X-Dailybot-Delivery novo.
button_idstring ($btn/<uuid4>)OpcionalGerado pelo servidor. Ecoado em cada callback. Valores do cliente são sobrescritos — não envie este campo.
platform_settingsobjectOpcionalOverrides raros por plataforma (legado). Prefira os campos top-level do botão.
payloadobjectOpcionalPayload opaco livre. Os novos campos interativos devem ficar no top-level do botão, não dentro de payload.

Objeto modal_body

Anexado a um Button interactive para abrir um modal leve. Tamanho serializado ≤ 8 KiB. Os blocos estão detalhados em [modal_body.blocks[] ↓](#send-message-modal-block).

NomeTipoObrigatórioDescrição
titlestring (≤ 200)ObrigatórioTítulo do modal.
submit_labelstring (≤ 200)OpcionalRótulo do botão de envio. Padrão: "Submit".
blocksarray (1..10)ObrigatórioExatamente os tipos text, input e divider (1–10). modal_body serializado ≤ 8 KiB. Slack renderiza nativamente; outras plataformas coletam conversacionalmente. Campos do bloco: [modal_body.blocks[] ↓](#send-message-modal-block).

Item de modal_body.blocks[]

1–10 blocos dentro de modal_body.blocks. Apenas text, input e divider são permitidos.

NomeTipoObrigatórioDescrição
type"text" | "input" | "divider"ObrigatórioTipo de bloco. Apenas esses três valores são aceitos.
textstring (≤ 3000)OpcionalObrigatório para type: "text". Texto somente de exibição.
namestringOpcionalObrigatório para type: "input". Deve coincidir com ^[a-z][a-z0-9_]{0,62}$ e ser único no modal. Os valores chegam como modal_fields.<name> / {{trigger.fields.<name>}}.
labelstring (≤ 200)OpcionalObrigatório para type: "input". Rótulo do campo mostrado ao usuário.
multilinebooleanOpcionalSomente input. Padrão false.
requiredbooleanOpcionalSomente input. Padrão false.
placeholderstring (≤ 200)OpcionalSomente input. Placeholder opcional.
max_lengthinteger (1..3000)OpcionalSomente input. Comprimento máximo opcional do valor.
defaultstringOpcionalSomente input. Valor pré-preenchido opcional.

Objeto response

Auto-resposta opcional em qualquer Button. Com callback_url, é enviada imediatamente em paralelo ao POST de saída (padrão slow-approval).

NomeTipoObrigatórioDescrição
messagestring (≤ 2000)ObrigatórioTexto de auto-resposta mostrado ao clicker.
buttonsarray<Button>OpcionalBotões aninhados recursivos com o mesmo schema Button. Limites: profundidade ≤ 3, ≤ 25 botões/nível, ≤ 16 KiB por botão top-level. Campos Button: Objeto Button ↑.
replace_originalbooleanOpcionalSubstitui a mensagem original em vez de publicar uma nova. Padrão false.
ephemeralbooleanOpcionalSó Slack; ignorado em outras plataformas. Padrão false.

Objeto callback_auth

Auth de transporte estática opcional em um Button. Só válida junto com callback_url. Sempre aditiva a X-Dailybot-Signature.

NomeTipoObrigatórioDescrição
type"bearer" | "basic" | "custom_header"ObrigatórioTipo de auth. Exatamente os campos do tipo escolhido — extras são rejeitados (button_callback_auth_invalid).
tokenstring (≤ 4096)OpcionalObrigatório para type: "bearer". Enviado como Authorization: Bearer <token>.
usernamestringOpcionalObrigatório para type: "basic" (com password). Enviado como Authorization: Basic <base64>.
passwordstringOpcionalObrigatório para type: "basic" (com username).
header_namestring (RFC 7230 token)OpcionalObrigatório para type: "custom_header". Nomes negados: host, content-length, content-type, transfer-encoding, connection, user-agent, e qualquer coisa que comece com x-dailybot-.
header_valuestringOpcionalObrigatório para type: "custom_header". Enviado como <header_name>: <header_value>.

Resposta

NomeTipoObrigatórioDescrição
bot_message_idstringObrigatórioO ID que você enviou, ou um gerado $db/<uuid>. Reutilize em 72h para editar.
thread_responsesarray<string>OpcionalIDs de respostas em thread (só se você enviou thread_responses). Cada um se edita como bot_message_id.

Erros

StatusQuando
400Erro de validação — veja o campo `code`: button_link_and_callback_conflict, button_callback_conflict, button_callback_url_invalid, button_modal_body_invalid, button_callback_form_not_found, button_callback_command_invalid, button_callback_prompt_invalid, button_callback_workflow_not_found, button_response_invalid, button_callback_auth_invalid, buttons_count_out_of_range; além de send_as_user_conflict, send_as_user_invalid_uuid, send_as_user_not_found, invalid_thread_responses, missing_targets.
401Credencial ausente, inválida ou expirada.
403Autenticado, mas sem permissão — `org_admin_required` quando um não-admin usa `send_as_user`; o scoping por role da CLI retorna `cli_send_message_target_not_allowed`.
429Rate-limit atingido. Respeite o header `Retry-After`.
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 done","target_channels":["C0123456789"]}'

Testar

Este é um ajudante apenas de cópia — a requisição não é enviada do seu navegador. Cole o comando no seu terminal para executá-lo.

  • Threads: passe `thread_responses` (máx. 10) para publicar pai + respostas em uma única chamada. Responda a uma mensagem existente adicionando `thread` dentro de um objeto em `target_channels`: `{ id, thread: <ts_ou_id_pai> }`.
  • Editar: reenvie com o mesmo `bot_message_id` em até 72h. Campos de identidade (`bot_username`, `bot_icon_*`) são ignorados na edição — a plataforma mantém a identidade original.
  • Impersonação: `platform_settings.bot_username` mais `bot_icon_url` OU `bot_icon_emoji` mudam a aparência do bot para essa mensagem. Apenas Slack hoje; outras plataformas ignoram o bloco de identidade.
  • Ephemeral (só Slack): use `platform_settings.is_ephemeral: true` junto com `target_users` para enviar uma mensagem privada dentro do canal, visível somente para esses usuários.
  • Scoping por role da CLI: admin/manager alcança qualquer usuário/time/canal público; membro de time alcança colegas + canais públicos; convidado só envia mensagens para si mesmo.
  • Multi-plataforma: funciona no Slack, Microsoft Teams, Discord e Google Chat. O threading em DMs varia — Teams / Discord / Google Chat publicam planos em DMs.
  • Botões interativos: os cinco callbacks (`callback_url`, `callback_form`, `callback_command`, `callback_prompt`, `callback_workflow`) são mutuamente exclusivos em um único botão (`button_callback_conflict`). `modal_body` combina com `callback_url` OU `callback_workflow` (não com callbacks internos). Auto-respostas `response` combinam com qualquer forma de botão.
  • Callbacks assinados: botões com `callback_url` recebem POSTs assinados com HMAC via `X-Dailybot-Signature` (e `X-Dailybot-Event`, `X-Dailybot-Delivery`, `X-Dailybot-Timestamp`). Google Chat é reportado como plataforma `hangouts` no corpo do callback — compare com esse valor, não com "gchat" ou "googlechat".
  • Consulte a seção Interactive buttons na página para snippets de verificação de assinatura (Node.js / Python), o schema de blocos de `modal_body`, a matriz de compatibilidade e os tipos de callback-auth.
POST/v1/open-conversation/Chave de API

Open a conversation (API-Key only)

Open a conversation (API-Key only)

Erros

StatusQuando
401Missing/invalid/expired credential
403Authenticated but not permitted
429Throttled - Retry-After header set
400Validation error
curl -sS -X POST 'https://api.dailybot.com/v1/open-conversation/' -H 'X-API-KEY: $DAILYBOT_API_KEY'

Testar

Este é um ajudante apenas de cópia — a requisição não é enviada do seu navegador. Cole o comando no seu terminal para executá-lo.

POST/v1/send-email/Chave de API

Send an email (API-Key only)

Body: to, subject, body_html, body_text.

Erros

StatusQuando
401Missing/invalid/expired credential
403Authenticated but not permitted
429Throttled - Retry-After header set
400Validation error
curl -sS -X POST 'https://api.dailybot.com/v1/send-email/' -H 'X-API-KEY: $DAILYBOT_API_KEY'

Testar

Este é um ajudante apenas de cópia — a requisição não é enviada do seu navegador. Cole o comando no seu terminal para executá-lo.

Construtor de botões

Componha botões interativos visualmente e copie o JSON exato de buttons, o comando dailybot chat send, ou o body do curl.

Corrija antes de enviar

  • #1: O rótulo é obrigatório.
  • #1: Botões interativos exigem um value.

Saída gerada

[
  {
    "label": "",
    "button_type": "interactive"
  }
]

A API pública do Dailybot expõe endpoints de mensageria para entregar mensagens do bot, emails e conversas privadas no Slack, Microsoft Teams, Discord e Google Chat. Esta página inclui as tabelas de campos estruturadas de POST /v1/send-message/, POST /v1/send-email/ e POST /v1/open-conversation/ (incluindo o contrato completo de Button / modal_body / response / callback_auth), além da substituição de identidade send_as_user e o guia detalhado de botões interativos.

Construtor de botões

Abaixo da referência de send-message, um Construtor de botões interativo permite compor botões visualmente — de link, simples, aprovar/rejeitar, gatilho de workflow, formulário, comando, prompt e modal — e copiar o array buttons JSON exato, o comando dailybot chat send equivalente (usando --link-button, --button, --approve-button/--reject-button com --callback-url/--callback-bearer, ou --workflow-button, recorrendo a --buttons '<json>' para o que esses flags não conseguem expressar), ou um body de curl pronto para executar contra POST /v1/send-message/.

Substituição de identidade send_as_user (apenas Slack)

send_as_user permite que um chamador autorizado publique uma mensagem que parece vir de outro usuário — a mensagem exibe o nome de exibição e o avatar do Slack desse usuário em vez da identidade do bot. É útil para workflows automatizados que precisam apresentar mensagens na perspectiva de um membro específico da equipe.

Requisitos:

  • O chamador deve ser administrador da organização ou ter uma chave de propriedade de um administrador.
  • O usuário alvo (UUID de send_as_user) deve estar ativo e na mesma organização.
  • Aplica-se apenas no Slack — outras plataformas ignoram o campo e voltam à identidade padrão do bot.
  • send_as_user é mutuamente exclusivo com bot_username, bot_icon_url e bot_icon_emoji. Combiná-los retorna 400 com code: "send_as_user_conflict".

Quando send_as_user está definido, o Dailybot busca a identidade do Slack conectada do usuário e a usa como autora da mensagem. A mensagem ainda é enviada pelo token do bot do Dailybot — não usa o token do Slack do usuário.

curl -X POST "https://api.dailybot.com/v1/send-message/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "The sprint is closed and all tickets are resolved.",
    "target_channels": ["C0123456789"],
    "send_as_user": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }'

Códigos de erro para send_as_user:

code HTTP Quando
send_as_user_conflict 400 send_as_user combinado com bot_username, bot_icon_url ou bot_icon_emoji
send_as_user_invalid_uuid 400 O formato do UUID é inválido
send_as_user_not_found 400 Usuário não encontrado, inativo ou em outra organização
org_admin_required 403 O chamador não é administrador

Botões interativos

Cada mensagem pode carregar até 25 botões renderizados como elementos interativos nativos no Slack, Microsoft Teams, Discord e Google Chat. Dois valores de button_type são suportados:

  • link — ao clicar abre a url no navegador do usuário.
  • interactive — ao clicar aciona uma das quatro capacidades descritas abaixo.

Campos do botão

Campo Tipo Quando Notas
label string (≤ 40 chars) obrigatório Renderizado no botão.
label_after_click string (≤ 40) opcional Substitui label após o clique. Padrão: label.
button_type "link" | "interactive" obrigatório Veja abaixo.
url string (https) obrigatório quando button_type = "link" A URL que o usuário abre.
value string (≤ 2000) obrigatório quando button_type = "interactive" Enviado de volta no corpo do POST de callback para o chamador saber qual botão foi clicado.
callback_url string (https, ≤ 2048) opcional (interactive) URL de callback que recebe um POST assinado no clique e no envio do modal. Mutuamente exclusivo com callback_form e callback_command. Veja URLs de callback interativas abaixo.
modal_body object opcional (interactive) Abre um modal no clique. Requer callback_url se o modal tiver campos de entrada. Veja Botões modais abaixo.
callback_form string (form UUID) opcional (interactive) Aciona um formulário interno do Dailybot, referenciado pelo UUID. Mutuamente exclusivo com callback_url e callback_command. Veja Gatilhos de formulários internos abaixo.
callback_command string (≤ 200) opcional (interactive) Aciona um comando interno CONHECIDO do Dailybot (ex.: help). O prefixo legado "prompt: …" é rejeitado — use callback_prompt. Mutuamente exclusivo com os outros callbacks.
callback_prompt string (≤ 2000) opcional (interactive) Prompt de IA em texto livre executado como o usuário que clica. Mutuamente exclusivo com os outros callbacks. Veja Gatilhos de prompts de IA abaixo.
callback_workflow string (workflow UUID) opcional (interactive) Aciona um workflow interno do Dailybot, referenciado pelo UUID. Mutuamente exclusivo com os outros callbacks. Veja Gatilhos de workflows abaixo.
response object opcional Resposta automática ao usuário que clica: {message (≤2000, required), buttons? (recursive), replace_original?, ephemeral?}. Compõe com QUALQUER forma de botão. Veja Respostas automáticas abaixo.
callback_auth object opcional (requer callback_url) Auth de transporte estática para o POST de saída (bearer | basic | custom_header) — aditiva à assinatura HMAC sempre ativa. Veja Autenticação de callback abaixo.
destroy_button bool opcional (padrão true) Quando false, o botão permanece clicável após o primeiro clique.
platform_settings object opcional Overrides por plataforma (raramente necessários).
payload object opcional Payload extra livre; opaco para a API. Os novos campos acima devem ficar no nível superior, não dentro de payload.

Todo botão interativo do lado do servidor carrega um button_id = "$btn/<uuid4>" gerado pelo servidor que é ecoado em todo corpo de callback para o chamador identificar cada clique de forma única. Valores de button_id fornecidos pelo cliente são silenciosamente sobrescritos.

Nota de compatibilidade: payload.callback_config e campos value de ações de integrações antigas ainda funcionam e são aditivos — prefira os campos de nível superior acima para novas integrações.

Matriz de compatibilidade

✔ = permitidos juntos, ✖ = rejeitado. Os cinco callbacks são mutuamente exclusivos entre si (button_callback_conflict).

callback_url callback_form callback_command callback_prompt callback_workflow modal_body response
callback_url
callback_form
callback_command
callback_prompt
callback_workflow
modal_body
response

Um modal com blocos input requer callback_url OU callback_workflow (400 button_modal_body_invalid, detail input_without_callback).

label_after_click e destroy_button compõem com tudo (incluindo response): destroy_button: false mantém o botão clicável — cada clique é um novo despacho com um id X-Dailybot-Delivery novo; em botões modais a troca de label_after_click ocorre no ENVIO do modal, não na abertura, então um modal cancelado deixa o botão clicável. Um botão interativo simples pode carregar response sem nenhum callback (o caso “Skip”).

URLs de callback interativas

Um botão interactive com callback_url recebe um HTTP POST do Dailybot quando o usuário clica. É a forma canônica para fluxos de aprovação, feedback e triagem leve construídos sobre /v1/send-message/.

Exemplo de requisição:

curl -X POST "https://api.dailybot.com/v1/send-message/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Juan wants to buy a coffee. Approve?",
    "target_users": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
    "buttons": [
      { "label": "Yes", "button_type": "interactive", "value": "approve",
        "callback_url": "https://approvals.acme.example/v1/decisions/req_9812" },
      { "label": "No",  "button_type": "interactive", "value": "deny",
        "callback_url": "https://approvals.acme.example/v1/decisions/req_9812" }
    ]
  }'

Corpo do POST de callback — o Dailybot dispara isto quando o usuário clica:

POST https://approvals.acme.example/v1/decisions/req_9812
Content-Type: application/json
User-Agent: Dailybot-Chatbot/1.0
X-Dailybot-Event: button_click
X-Dailybot-Delivery: 5e2d…-uuid
X-Dailybot-Timestamp: 1782001872
X-Dailybot-Signature: t=1782001872, v1=6d9a3e…hex_hmac
{
  "event": "button_click",
  "bot_message_id": "$db/ae007b43-dde2-4fa9-bce3-71fb0975a249",
  "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"
}

Valores de platform. Um de slack, msteams, discord ou hangouts. O Google Chat é entregue como hangouts (o identificador histórico de plataforma) — corresponda a esse valor, não a “gchat” ou “googlechat”. sent_at é marcado quando a mensagem da plataforma foi realmente entregue; clicked_at quando o usuário clicou. metadata retorna verbatim da sua chamada original a /v1/send-message/.

Verificação de assinatura. Todo POST carrega uma assinatura HMAC-SHA256 calculada com o segredo de assinatura de callback da sua organização sobre a string ASCII "{unix_timestamp}.{raw_body}".

Node.js:

const crypto = require('crypto');
function verify(rawBody, sigHeader, secretB64) {
  const secret = Buffer.from(secretB64, 'base64url');
  const parts = Object.fromEntries(sigHeader.split(',').map(s => s.trim().split('=')));
  const ts = parseInt(parts.t, 10);
  if (Math.abs(Date.now()/1000 - ts) > 300) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(parts.v1, 'hex'));
}

Python:

import hmac, hashlib, base64, time
def verify(raw_body: bytes, sig_header: str, secret_b64: str) -> bool:
    secret = base64.urlsafe_b64decode(secret_b64 + '==')
    parts = dict(p.strip().split('=', 1) for p in sig_header.split(','))
    ts = int(parts['t'])
    if abs(time.time() - ts) > 300:  # 5 min replay window
        return False
    expected = hmac.new(secret, f"{ts}.{raw_body.decode()}".encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts['v1'])

Idempotência. X-Dailybot-Delivery é único por tentativa de clique. Se o POST de saída do Dailybot for reenviado, o mesmo id de entrega é enviado — use-o como chave de deduplicação.

Reenvios. O Dailybot reenvia uma vez com backoff de 500 ms em erros 5xx / 429 / de rede. 2xx bem-sucedido completa o fanout; qualquer outro 4xx é tratado como terminal e descartado.

Botões modais

Adicione um objeto modal_body a um botão interactive para abrir um modal leve no clique. No envio, os valores de entrada do modal são publicados em callback_url junto com event: "modal_submit" e o value do botão clicado.

Esquema de modal_body:

Os tipos de bloco suportados são exatamente text, input e divider. O modal_body serializado deve ser ≤ 8 KiB. Valores name de entrada devem corresponder a ^[a-z][a-z0-9_]{0,62}$ e ser únicos dentro do modal.

{
  "title": "string, ≤ 200 chars, required",
  "submit_label": "string, ≤ 200 chars, default 'Submit'",
  "blocks": [
    // 1..10 blocks; supported block types:
    { "type": "text", "text": "≤ 3000 chars" },
    {
      "type": "input",
      "name": "snake_case_identifier",   // required, unique per modal; regex ^[a-z][a-z0-9_]{0,62}$
      "label": "Field label, ≤ 200",     // required
      "multiline": true,                  // optional, default false
      "required": true,                   // optional, default false
      "max_length": 1000,                 // optional, ≤ 3000
      "placeholder": "≤ 200",             // optional
      "default": "prefilled value"        // optional
    },
    { "type": "divider" }
  ]
}

Exemplo de requisição (aprovar com comentário):

curl -X POST "https://api.dailybot.com/v1/send-message/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Sprint retro — how did it feel?",
    "target_teams": ["team_engineering_uuid"],
    "buttons": [
      { "label": "🚀 Great",   "button_type": "interactive", "value": "great",
        "callback_url": "https://retros.acme.example/v1/sprint-42" },
      { "label": "😐 Fine",    "button_type": "interactive", "value": "fine",
        "callback_url": "https://retros.acme.example/v1/sprint-42" },
      {
        "label": "📝 Add feedback",
        "button_type": "interactive",
        "value": "feedback",
        "callback_url": "https://retros.acme.example/v1/sprint-42",
        "modal_body": {
          "title": "Sprint feedback",
          "submit_label": "Send",
          "blocks": [
            { "type": "text", "text": "Anything on your mind about this sprint?" },
            {
              "type": "input",
              "name": "sentiment_note",
              "label": "Your feedback",
              "multiline": true,
              "required": true,
              "max_length": 1000,
              "placeholder": "One or two sentences is perfect."
            }
          ]
        }
      }
    ]
  }'

Corpo do POST de envio de modal (o Dailybot publica isto em callback_url):

{
  "event": "modal_submit",
  "bot_message_id": "$db/…",
  "button":       { "id": "$btn/…", "value": "feedback" },
  "user":         { "id": "…", "email": "…", "display_name": "…", "external_id": "U…" },
  "organization": { "id": "…", "name": "…" },
  "platform":     "slack",
  "channel":      { "id": "C…", "type": "channel" },
  "modal_fields": {
    "sentiment_note": "Retro was great, ship-per-day cadence is working."
  },
  "metadata":     { },
  "sent_at":      "2026-07-22T14:30:00Z",
  "clicked_at":   "2026-07-22T14:31:12Z"
}

Mesmos cabeçalhos / assinatura que button_click; apenas event e modal_fields diferem.

Modais somente de exibição (blocos contêm apenas text e divider, sem input) são permitidos sem callback_url — clicar em Submit apenas fecha o modal e nenhum POST é disparado.

Gatilhos de formulários internos

Um botão interactive com callback_form abre um formulário do Dailybot no clique. Nenhum serviço externo é chamado; o callback_url do chamador é ignorado (e rejeitado se ambos estiverem definidos).

curl -X POST "https://api.dailybot.com/v1/send-message/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Need to request access to a system?",
    "target_channels": ["C0AF0FBT23D"],
    "buttons": [
      { "label": "Open access-request form",
        "button_type": "interactive",
        "value": "open_access_form",
        "callback_form": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
    ]
  }'

callback_form deve ser o UUID de um formulário do Dailybot na organização do chamador (liste seus formulários via /v1/forms/ para encontrá-lo). Nomes e slugs são rejeitados — nomes de formulário são mutáveis e ambíguos. Formulários desconhecidos, arquivados ou inacessíveis → 400 button_callback_form_not_found. O formulário é preenchido pelo usuário pelo fluxo de formulários existente — veja /v1/forms/ para ler as respostas resultantes.

Gatilhos de comandos internos

Um botão interactive com callback_command executa um comando interno CONHECIDO do Dailybot no clique (ex.: help, kudos, checkin). Máx. 200 caracteres.

curl -X POST "https://api.dailybot.com/v1/send-message/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Need help getting started?",
    "target_users": ["…"],
    "buttons": [
      { "label": "Open help",
        "button_type": "interactive",
        "value": "help",
        "callback_command": "help" }
    ]
  }'

Sem prefixo prompt:. A convenção legada "prompt: <text>" foi removida. Um callback_command que começa com prompt: (sem distinção de maiúsculas) é rejeitado com 400 button_callback_command_invalid — use o campo dedicado callback_prompt.

Atribuição. O comando roda sob a identidade do usuário que clica (escopo de papel, atribuição de uso) — não do remetente. Chamadores não podem usar callback_command para fazer outro usuário executar um comando que não tem permissão.

Comandos desconhecidos (não na lista de comandos integrados) falham suavemente com uma resposta localizada “That command isn’t available” ao usuário que clicou — sem erro para o remetente.

Gatilhos de prompts de IA

Um botão interactive com callback_prompt envia um prompt de texto livre (≤ 2000 caracteres) ao assistente de IA do Dailybot no clique, atribuído ao usuário que clica (sua cota de IA, suas permissões).

curl -X POST "https://api.dailybot.com/v1/send-message/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Curious about last week?",
    "target_users": ["…"],
    "buttons": [
      { "label": "Ask the assistant",
        "button_type": "interactive",
        "value": "ask_ai",
        "callback_prompt": "Summarize incidents from the past 7 days." }
    ]
  }'

Prompts em branco ou acima do tamanho → 400 button_callback_prompt_invalid.

Gatilhos de workflows

Um botão interactive com callback_workflow aciona um workflow interno do Dailybot no clique, atribuído ao usuário que clica. callback_workflow deve ser o UUID de um workflow na organização do chamador — nomes/slugs são rejeitados. O workflow deve existir, pertencer à organização e estar ativo; caso contrário 400 button_callback_workflow_not_found.

curl -X POST "https://api.dailybot.com/v1/send-message/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Deploy is ready.",
    "target_users": ["…"],
    "buttons": [
      { "label": "Run release workflow",
        "button_type": "interactive",
        "value": "run_release",
        "callback_workflow": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
    ]
  }'

Nenhum POST externo é disparado e nenhum material de assinatura está envolvido — o gatilho é totalmente interno.

Variáveis de gatilho. Passos do workflow acionado podem referenciar o contexto do clique pelo namespace {{trigger.*}}: {{trigger.source}} (api | button_click | modal_submit), {{trigger.button_value}}, {{trigger.button_id}}, {{trigger.fields.<name>}} (entradas do modal), {{trigger.clicked_at}}, {{trigger.body.*}} (payload bruto), {{trigger.user.*}} (uuid, full_name, first_name, email, role do usuário que clicou) e {{trigger.triggered_by_user_uuid}}. Para ramificação simples por valor, aponte vários botões ao MESMO workflow com values diferentes e ramifique em {{trigger.button_value}}. Veja /developers/api/workflows para a referência completa de variáveis de gatilho.

modal_body compõe com callback_workflow: o clique abre o modal e, no envio, os valores dos campos são entregues ao workflow como {{trigger.fields.<input.name>}} — sem servidor externo.

curl -X POST "https://api.dailybot.com/v1/send-message/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Report an incident:",
    "target_users": ["…"],
    "buttons": [
      { "label": "Report", "button_type": "interactive", "value": "report",
        "callback_workflow": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "modal_body": {
          "title": "New incident",
          "blocks": [
            { "type": "input", "name": "summary", "label": "What happened?",
              "multiline": true, "required": true }
          ]
        } }
    ]
  }'

O workflow (tipo de gatilho api_trigger) pode então usar {{trigger.fields.summary}} em qualquer passo — pré-preencher uma resposta de formulário, compor uma mensagem, alimentar um prompt de IA. Botões modal-para-workflow não carregam segredo de assinatura (rota interna). Um modal com blocos input e SEM callback_url nem callback_workflow é rejeitado (input_without_callback).

Respostas automáticas

Qualquer botão (incluindo um botão interativo simples sem callback) pode carregar um objeto response — uma resposta automática mostrada ao usuário que clica:

{
  "message": "string — required, ≤ 2000 chars",
  "buttons": [],
  "replace_original": false,
  "ephemeral": false
}

O caso “Skip” — um botão cuja única função é reconhecer:

curl -X POST "https://api.dailybot.com/v1/send-message/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Time for your weekly reflection!",
    "target_users": ["…"],
    "buttons": [
      { "label": "Start", "button_type": "interactive", "value": "start",
        "callback_form": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" },
      { "label": "Skip this week", "button_type": "interactive", "value": "skip",
        "response": { "message": "No problem — see you next week! 👋" } }
    ]
  }'

Momento. Com callback_url, a resposta é enviada imediatamente no clique, em paralelo com o POST de saída — é feedback instantâneo ao usuário (“Processing your approval…”), nunca condicionado ao resultado do despacho. Este é o padrão canônico de aprovação lenta: reconhecer instantaneamente via response, depois seu servidor acompanhar no próprio ritmo com um novo POST /v1/send-message/ ou editando a mensagem original dentro da janela de 72 h de bot_message_id. Com modal_body, a resposta é enviada após o ENVIO do modal. Com os callbacks internos (callback_form / callback_command / callback_prompt / callback_workflow), a resposta é enviada ANTES da entrega.

replace_original — substitui a mensagem de origem em vez de publicar uma nova. ephemeral — apenas Slack; ignorado em outros lugares.

Botões recursivos. response.buttons usa o mesmo esquema Button e é validado recursivamente: profundidade máxima de aninhamento 3, ≤ 25 botões por nível, button_id gerado pelo servidor em cada profundidade e um limite de tamanho serializado total de 16 KiB por botão de nível superior. Violações → 400 button_response_invalid com detail um de response_message_required, response_message_too_long, nesting_too_deep, too_many_buttons, button_too_large.

Autenticação de callback

Todo POST de callback é SEMPRE assinado com o cabeçalho HMAC X-Dailybot-Signature (veja Segredo de assinatura de callback). Se seu endpoint adicionalmente exige credenciais estáticas — token de API gateway, basic auth, cabeçalho fixo — anexe um objeto callback_auth. Só é válido junto com callback_url.

curl -X POST "https://api.dailybot.com/v1/send-message/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Approve the vendor payment?",
    "target_users": ["…"],
    "buttons": [
      { "label": "Approve", "button_type": "interactive", "value": "approve",
        "callback_url": "https://approvals.acme.example/v1/req_77",
        "callback_auth": { "type": "bearer", "token": "acme-gw-token-…" } }
    ]
  }'

Tipos (exatamente os campos do tipo escolhido — extras são rejeitados):

type Campos Enviado como
bearer token (≤ 4096) Authorization: Bearer <token>
basic username, password Authorization: Basic <base64>
custom_header header_name, header_value <header_name>: <header_value>

header_name deve ser um token de cabeçalho RFC 7230 válido e não pode ser um de host, content-length, content-type, transfer-encoding, connection, user-agent, nem qualquer coisa que comece com x-dailybot-. Violações → 400 button_callback_auth_invalid.

Credenciais são somente de escrita: nunca são retornadas por nenhuma API de leitura e nunca aparecem em logs. A assinatura HMAC permanece obrigatória — verifique-a mesmo quando também exigir auth estática.

Códigos de erro de botões interativos

Todos são HTTP 400. A forma da resposta segue o envelope existente ({ "detail": "…", "code": "…" }).

code Quando
button_link_and_callback_conflict Um botão link foi combinado com value, modal_body, response, callback_auth ou qualquer campo de callback.
button_callback_conflict Um botão interactive combina mais de um de callback_url, callback_form, callback_command, callback_prompt, callback_workflow, ou combina modal_body com um callback interno.
button_callback_url_invalid callback_url sem esquema, não https, malformada, maior que 2048 caracteres ou resolve para um intervalo privado / loopback / IMDS.
button_modal_body_invalid modal_body falhou na validação estrutural — tipo de bloco incorreto, ≥ 11 blocos, name ausente / duplicado, campo grande demais, ou modal com entradas mas sem callback_url / callback_workflow (input_without_callback).
button_callback_form_not_found callback_form não resolve para um formulário na organização do chamador.
button_callback_command_invalid callback_command tem mais de 200 caracteres ou usa o prefixo prompt: removido (use callback_prompt).
button_callback_prompt_invalid callback_prompt está em branco ou tem mais de 2000 caracteres.
button_callback_workflow_not_found callback_workflow não é um UUID ou não resolve para um workflow ativo na organização do chamador.
button_response_invalid response falhou na validação — veja detail: response_message_required, response_message_too_long, nesting_too_deep, too_many_buttons, button_too_large.
button_callback_auth_invalid callback_auth sem callback_url, tipo desconhecido, campos incorretos/extra ou header_name negado/inválido.

Os comportamentos existentes relacionados a botões (limite de buttons.length de 25, erros send_as_user_*, invalid_thread_responses, etc.) permanecem inalterados.

Segredo de assinatura de callback

Cada organização tem um único segredo de assinatura de callback usado para HMAC. É gerado automaticamente na primeira vez que uma mensagem com callback_url é enviada. O segredo nunca é retornado pela API pública; obtenha-o do administrador da sua organização (um endpoint de rotação / recuperação está no roadmap). Guarde-o no armazenamento de segredos do seu serviço; trate-o como um segredo de cliente OAuth.

Veja em ação

Receitas de automação construídas sobre a API send-message, a CLI e a skill de agentes — cada uma com exemplos de curl, CLI e prompts prontos para copiar e colar.