Documentação da API

API REST para sincronizar dados estruturados com o seu bot da Bravos AI.

Introdução

A Webhook API permite sincronizar dados estruturados (JSON ou XML) com um bot da Bravos AI. Você pode fazer uma carga inicial a partir de uma URL e manter os dados atualizados por meio de webhooks HTTP assinados com HMAC-SHA256.

Configuração

Antes de enviar webhooks, você precisa configurar a conexão em Integrações > Webhook personalizado no painel de administração.

  1. 1

    Crie a conexão

    Dê a ela um nome descritivo (ex.: "Clínicas", "Inventário") e informe a URL onde estão os seus dados em formato JSON ou XML (máximo de 50 MB). O sistema baixará os dados e detectará os campos automaticamente.

  2. 2

    Escolha o campo identificador

    Depois de criar a conexão, o sistema detecta os campos dos seus dados. Selecione qual é o identificador único de cada registro (equivalente a uma chave primária). Por exemplo: id, ref, sku. Este campo é obrigatório em cada item que você enviar por webhook.

  3. 3

    Ative e recolha suas credenciais

    Ao ativar, o sistema processa a carga inicial a partir da sua URL. A partir desse momento você tem disponível:

    • Endpoint — a URL para a qual enviar os webhooks
    • Secret — a chave para assinar cada request com HMAC-SHA256
    • Dataset slug e Bot ID — já incluídos no endpoint

Depois de ativada, você pode excluir campos pelo painel para que o chatbot não os use nas respostas.

Autenticação

Cada request deve incluir uma assinatura HMAC-SHA256 no header X-Bravos-Signature. Ela é calculada sobre o body completo usando o seu secret. O formato é hex em minúsculas.

import hmac, hashlib

secret = "SEU_SECRET_AQUI"
body = b'{"action":"upsert","items":[...]}'

signature = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
# Resultado: "a1b2c3d4..." (hex lowercase)

Se a assinatura não coincidir, o servidor responde 401. Depois de 10 tentativas com falha em um minuto, o endpoint é bloqueado temporariamente.

Endpoint

POST https://api.bravos-ai.com/integrations/custom-webhook/{bot_id}/{dataset_slug}/sync

Os valores de bot_id e dataset_slug são gerados ao criar a conexão e ficam visíveis no painel de administração.

Headers obrigatórios

Content-Type: application/json

X-Bravos-Signature: <assinatura HMAC-SHA256 em hex>

Criar e atualizar dados

Envie "action": "upsert" com um array de itens. Se um item com o mesmo identificador já existe, ele é atualizado. Se não existe, é criado.

{
  "action": "upsert",
  "items": [
    {
      "ref": "SP-01",
      "nome": "Clínica São Paulo Centro",
      "endereco": "Av. Paulista, 1000",
      "telefone": "+55 11 3000-0000",
      "especialidades": ["Odontologia", "Ortopedia", "Dermatologia"],
      "horario": {
        "segunda_sexta": "09:00 - 20:00",
        "sabados": "10:00 - 14:00"
      },
      "servicos": [
        {"nome": "Limpeza dental", "preco": 250, "duracao_min": 45},
        {"nome": "Radiografia panorâmica", "preco": 150, "duracao_min": 15},
        {"nome": "Consulta dermatológica", "preco": 350, "duracao_min": 30}
      ]
    }
  ]
}

Cada item deve incluir o campo que você escolheu como identificador ao ativar a conexão (neste exemplo, ref). Os demais campos são livres e são detectados automaticamente.

Dados aninhados

  • Objetos aninhados são achatados com ponto: horario.segunda_sexta
  • Arrays de texto são unidos: "Odontologia, Ortopedia, Dermatologia"
  • Arrays de objetos são indexados como sub-registros, o que permite ao chatbot filtrar por sub-campos (ex.: "serviços de menos de 200 reais")

Excluir dados

Envie "action": "delete" com os identificadores dos registros a excluir. A exclusão inclui os sub-registros associados.

{
  "action": "delete",
  "items": [
    { "ref": "SP-01" }
  ]
}

Excluir um registro inexistente não produz erro. A operação é idempotente.

Respostas

Sucesso (200)

{
  "ok": true,
  "request_id": "req_7a3f1b2c",
  "action": "upsert",
  "stats": {
    "processed": 1,
    "created": 1,
    "updated": 0,
    "skipped": 0,
    "children_created": 3,
    "children_deleted": 0,
    "chunks_regenerated": 1
  }
}
  • skipped — o conteúdo do item não mudou (os embeddings não são regenerados)
  • children_created/deleted — sub-registros de arrays de objetos
  • chunks_regenerated — textos recalculados para a busca semântica

Erro (4xx/5xx)

{
  "detail": "Invalid signature"
}

O campo detail descreve o erro em inglês. Os códigos HTTP indicam a categoria (veja a tabela de erros abaixo).

Códigos de erro

CódigoCausaO que fazer
400JSON malformado, falta o campo identificador, ou estrutura inválidaRevise o payload. Não tente de novo sem corrigir.
401A assinatura HMAC não coincideVerifique se está usando o secret correto e assinando exatamente o body que envia.
403Request enviado por HTTP em vez de HTTPSUse sempre HTTPS.
404O bot ou dataset não existeConfira bot_id e dataset_slug no painel.
409O dataset ainda não foi ativadoComplete a ativação no painel de administração.
413O body passa de 5 MBDivida o envio em lotes menores.
429Outro webhook está sendo processado ou houve tentativas demais com falhaEspere 30 segundos e tente de novo.
500Erro interno do servidorTente de novo com backoff exponencial (1s, 2s, 4s, 8s, máx. 5 tentativas).

Limites

50 MB

Carga inicial (URL)

5 MB

Body por webhook

500

Itens por webhook

HTTPS

Obrigatório

Atualizações em massa: se você precisa atualizar mais de 500 registros via webhook, envie-os em lotes sequenciais de até 500 cada. A carga inicial por URL não tem esse limite.

Novas tentativas: diante de um 5xx, tente de novo com backoff exponencial. Diante de um 4xx, corrija o payload antes de tentar. Diante de um 429, espere 30 segundos.

Idempotência: enviar o mesmo upsert duas vezes não duplica dados — o segundo request atualiza o que já existe.

Exemplo completo

Um script completo que envia dados, assina com HMAC e trata a resposta.

import hmac, hashlib, json, requests

SECRET = "SEU_SECRET_AQUI"
ENDPOINT = "https://api.bravos-ai.com/integrations/custom-webhook/{BOT_ID}/{DATASET_SLUG}/sync"

clinicas = [
    {
        "ref": "SP-01",
        "nome": "Clínica São Paulo Centro",
        "endereco": "Av. Paulista, 1000",
        "servicos": [
            {"nome": "Limpeza dental", "preco": 250},
            {"nome": "Consulta dermatológica", "preco": 350},
        ],
    },
    {
        "ref": "RJ-01",
        "nome": "Clínica Rio Copacabana",
        "endereco": "Av. Atlântica, 500",
        "servicos": [
            {"nome": "Revisão geral", "preco": 200},
            {"nome": "Fisioterapia", "preco": 180},
        ],
    },
]

body = json.dumps({"action": "upsert", "items": clinicas}).encode()
signature = hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest()

r = requests.post(ENDPOINT, data=body, headers={
    "Content-Type": "application/json",
    "X-Bravos-Signature": signature,
}, timeout=30)

data = r.json()
if data.get("ok"):
    print(f"Processados: {data['stats']['processed']}")
    print(f"Criados: {data['stats']['created']}, Atualizados: {data['stats']['updated']}")
else:
    print(f"Erro {r.status_code}: {data.get('detail')}")