API Externa

Integre o agente de IA do Feito aos seus próprios sistemas. Envie mensagens e receba respostas sugeridas pela IA via callback de webhook.

Visão Geral

A API Externa permite que você utilize as capacidades do agente de IA do Feito sem nossa integração com WhatsApp ou interface. Isso é útil para integrar respostas com IA em suas próprias plataformas de mensagens, CRMs ou aplicações personalizadas.

Como funciona

  1. 1Você envia uma mensagem para nossa API com o ID do contato e o conteúdo da mensagem
  2. 2Nossa IA processa a conversa e gera uma resposta sugerida
  3. 3Enviamos a resposta para sua URL de webhook para você processar

Autenticação

Todas as requisições à API requerem autenticação usando um Bearer token.
Já é cliente? Gere sua chave em Configurações da Conta. Se não, fale com a gente.

Authorization: Bearer fsk_sua_chave_api_aqui

Formato da Chave API

As chaves API começam com fsk_ seguido de uma string aleatória. Mantenha sua chave segura e nunca a exponha em código do lado do cliente.

Enviar Mensagem

POST/api/v1/replies

Envie uma mensagem de um contato para obter uma resposta sugerida pela IA. A resposta será entregue de forma assíncrona para sua URL de webhook.

Parâmetros da Requisição

ParâmetroTipoObrigatórioDescrição
contactIdstringSimSeu identificador único para este contato. Usado para manter o histórico da conversa.
messagestringSimA mensagem recebida do contato.
contactNamestringNãoNome de exibição do contato. Atualizado a cada requisição, se fornecido.
webhookUrlstringNãoSobrescreve a URL de webhook padrão para esta requisição.

Exemplo de Requisição

curl -X POST https://feito.io/api/v1/replies \
  -H "Authorization: Bearer fsk_sua_chave_api" \
  -H "Content-Type: application/json" \
  -d '{
    "contactId": "user_123",
    "contactName": "João Silva",
    "message": "Olá, gostaria de agendar uma consulta"
  }'

Resposta

Retorna imediatamente com um ID do job. A resposta real será enviada para seu webhook.

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued"
}

Formato de Eventos

Quando o processamento é concluído, fazemos um POST do resultado para sua URL de webhook usando um formato padronizado de eventos. Todos os webhooks seguem a mesma estrutura de envelope.

Estrutura do Evento

{
  "id": "evt_abc123def456",
  "type": "reply.generated",
  "api_version": "2024-01-01",
  "created_at": "2024-01-15T10:30:00.000Z",
  "account_id": "acc_xyz789",
  "data": {
    "object": { ... }
  }
}

Campos do Envelope

ParâmetroTipoObrigatórioDescrição
idstringSimID único do evento (prefixo: evt_). Use para idempotência.
typestringSimTipo do evento (ex: reply.generated, reply.skipped).
api_versionstringSimVersão da API no formato YYYY-MM-DD.
created_atstringSimTimestamp ISO 8601 de quando o evento foi criado.
account_idstringSimID da conta que gerou este evento.
data.objectobjectSimPayload específico do tipo de evento.

Cabeçalhos do Webhook

ParâmetroTipoObrigatórioDescrição
X-Feito-EventstringSimTipo do evento (ex: reply.generated).
X-Feito-Request-IdstringSimID único do evento.
X-Feito-TimestampstringSimTimestamp Unix da requisição.
X-Feito-SignaturestringNãoAssinatura HMAC-SHA256 (se segredo configurado).

Tipos de Eventos

Diferentes tipos de eventos são enviados dependendo do resultado do processamento da IA.

reply.generated

A IA gerou uma resposta com sucesso.

{
  "id": "evt_abc123def456",
  "type": "reply.generated",
  "api_version": "2024-01-01",
  "created_at": "2024-01-15T10:30:00.000Z",
  "account_id": "acc_xyz789",
  "data": {
    "object": {
      "request_id": "550e8400-e29b-41d4-a716-446655440000",
      "contact_id": "user_123",
      "reply": "Olá João! Claro, ficarei feliz em ajudá-lo...",
      "confidence": 0.92
    }
  }
}

reply.skipped

A IA determinou que nenhuma resposta é necessária (ex: o contato apenas disse "ok" ou "obrigado").

{
  "id": "evt_abc456def789",
  "type": "reply.skipped",
  "api_version": "2024-01-01",
  "created_at": "2024-01-15T10:30:00.000Z",
  "account_id": "acc_xyz789",
  "data": {
    "object": {
      "request_id": "550e8400-e29b-41d4-a716-446655440000",
      "contact_id": "user_123",
      "reason": "Contato confirmou mensagem anterior"
    }
  }
}

reply.blocked

A resposta foi bloqueada por guardrails de segurança.

{
  "id": "evt_abc789def101",
  "type": "reply.blocked",
  "api_version": "2024-01-01",
  "created_at": "2024-01-15T10:30:00.000Z",
  "account_id": "acc_xyz789",
  "data": {
    "object": {
      "request_id": "550e8400-e29b-41d4-a716-446655440000",
      "contact_id": "user_123",
      "reason": "Mensagem continha conteúdo proibido",
      "violations": ["pricing_commitment"]
    }
  }
}

reply.failed

O processamento falhou devido a um erro.

{
  "id": "evt_abcdef123456",
  "type": "reply.failed",
  "api_version": "2024-01-01",
  "created_at": "2024-01-15T10:30:00.000Z",
  "account_id": "acc_xyz789",
  "data": {
    "object": {
      "request_id": "550e8400-e29b-41d4-a716-446655440000",
      "contact_id": "user_123",
      "error": "Timeout na orquestração"
    }
  }
}

Verificação de Assinatura

Se você configurou um segredo de webhook, verifique a autenticidade das requisições usando a assinatura HMAC-SHA256.

Formato da Assinatura

A assinatura é calculada sobre timestamp.payload e enviada no header com o prefixo sha256=.

Exemplo de Verificação

const crypto = require('crypto');

function verifyWebhookSignature(req, secret) {
  const signature = req.headers['x-feito-signature'];
  const timestamp = req.headers['x-feito-timestamp'];
  const payload = JSON.stringify(req.body);

  // Verifica se timestamp está dentro de 5 minutos
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    return false; // Replay attack prevention
  }

  // Calcula assinatura esperada
  const signedPayload = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Extrai valor da assinatura (remove prefixo sha256=)
  const sigValue = signature.replace('sha256=', '');

  return crypto.timingSafeEqual(
    Buffer.from(sigValue),
    Buffer.from(expected)
  );
}

Importante

Sempre verifique o timestamp para prevenir ataques de replay. Rejeite requisições com timestamps mais antigos que 5 minutos.

Tratamento de Erros

A API usa códigos de status HTTP padrão. Respostas de erro incluem uma mensagem descritiva.

Códigos de Status HTTP

StatusDescrição
202Requisição aceita e enfileirada para processamento
400Requisição inválida — parâmetros ausentes ou inválidos
401Não autorizado — chave API inválida ou ausente
500Erro interno do servidor

Formato de Resposta de Erro

{
  "error": "contactId is required"
}

Falhas de Webhook

Se não conseguirmos entregar ao seu webhook, tentaremos novamente com backoff exponencial. Entregas com falha são visíveis na visualização da conversa:

J

Olá, quero saber mais sobre seus serviços

21:54

Olá, João! Tudo bem? Que ótimo receber seu contato. Nós ajudamos profissionais e empresas a economizar tempo e agendar mais clientes, usando uma inteligência artificial que responde e qualifica os interessados no seu WhatsApp 24h por dia. Para eu te ajudar melhor, me conta: qual é o seu maior desafio hoje ao usar o WhatsApp para o seu negócio?

Webhook callback error: fetch failed

21:55

A mensagem de erro mostra o motivo da falha na entrega do webhook. Causas comuns incluem URLs inacessíveis, problemas de certificado SSL ou timeouts.