Feito

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.

Leads

Gerencie leads programaticamente. Você pode buscar leads existentes e criar novos leads de forma idempotente.

Buscar Lead

GET/api/v1/leadsBuscar lead por telefone

Parâmetros de Query

ParâmetroTipoObrigatórioDescrição
phonestringSimNúmero de telefone no formato E.164 (ex: +5511999999999)

Exemplo de Requisição

curl -X GET "https://feito.io/api/v1/leads?phone=%2B5511999999999" \
  -H "Authorization: Bearer fsk_sua_chave_api"

Resposta (200)

{
  "lead": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "phone": "+5511999999999",
    "contactId": "crm_123",
    "name": "João Silva",
    "state": "active",
    "handler": "ai",
    "createdAt": "2024-01-15T10:00:00Z"
  }
}

Resposta (404)

{
  "error": "Lead not found"
}

Criar Lead

POST/api/v1/leadsCriar ou retornar lead existente

Crie um novo lead ou retorne um existente (idempotente). Se um lead com o mesmo phone ou contactId já existir, ele será retornado.

Parâmetros da Requisição

ParâmetroTipoObrigatórioDescrição
contactIdstringSimSeu identificador externo para o contato (ex: ID do CRM)
phonestringNãoNúmero de telefone no formato E.164. Necessário para enviar templates.
namestringNãoNome de exibição do contato.

Exemplo de Requisição

curl -X POST https://feito.io/api/v1/leads \
  -H "Authorization: Bearer fsk_sua_chave_api" \
  -H "Content-Type: application/json" \
  -d '{
    "contactId": "crm_123",
    "phone": "+5511999999999",
    "name": "João Silva"
  }'

Resposta (201 - Lead criado)

{
  "lead": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "phone": "+5511999999999",
    "contactId": "crm_123",
    "name": "João Silva",
    "state": "new",
    "handler": "ai",
    "createdAt": "2024-01-15T10:00:00Z"
  },
  "created": true
}

Resposta (200 - Lead existente)

{
  "lead": { ... },
  "created": false
}

Webhook lead.created

Quando um novo lead é criado (de qualquer fonte: API, dashboard ou WhatsApp), um evento lead.created é enviado para seu webhook. Veja mais detalhes na seção Tipos de Eventos.

Enviar Template

POST/api/v1/leads/:id/send-templateEnviar mensagem template aprovada

Envie uma mensagem de template aprovado pelo WhatsApp para um lead. Útil para iniciar conversas fora da janela de 24 horas ou para mensagens padronizadas.

Parâmetros de URL

ParâmetroTipoObrigatórioDescrição
idstringSimUUID do lead no Feito.

Parâmetros da Requisição

ParâmetroTipoObrigatórioDescrição
template_namestringSimNome técnico do template aprovado.
variablesobjectNãoValores das variáveis por posição (ex: {"1": "João", "2": "Empresa XYZ"}).

Exemplo de Requisição

curl -X POST https://feito.io/api/v1/leads/550e8400.../send-template \
  -H "Authorization: Bearer fsk_sua_chave_api" \
  -H "Content-Type: application/json" \
  -d '{
    "template_name": "welcome_message",
    "variables": {
      "1": "João",
      "2": "Empresa XYZ"
    }
  }'

Resposta (200)

{
  "success": true,
  "messageSid": "SMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

Erros Comuns

StatusDescrição
400Lead não tem telefone válido (foi criado apenas com contactId)
400Template não está aprovado
403Lead não pertence a esta conta
404Lead ou template não encontrado

Requisito de Telefone

Para enviar templates, o lead deve ter um número de telefone válido. Leads criados apenas com contactId (sem phone) não podem receber templates.

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.

Eventos de Lead

lead.created

Disparado quando um novo lead é criado de qualquer fonte (API externa, dashboard ou mensagem de WhatsApp).

{
  "id": "evt_xyz789abc123",
  "type": "lead.created",
  "api_version": "2024-01-01",
  "created_at": "2024-01-15T10:30:00.000Z",
  "account_id": "acc_xyz789",
  "data": {
    "object": {
      "lead_id": "550e8400-e29b-41d4-a716-446655440000",
      "external_id": "crm_123",
      "phone": "+5511999999999",
      "profile_name": "João Silva",
      "source": "external_api"
    }
  }
}

Campos do Evento lead.created

ParâmetroTipoObrigatórioDescrição
lead_idstringSimUUID interno do lead no Feito.
external_idstringNãoSeu contactId (se fornecido na criação).
phonestringSimNúmero de telefone (ou placeholder ext_* se não fornecido).
profile_namestringNãoNome de exibição do lead.
sourcestringSimOrigem: external_api, dashboard ou whatsapp_inbound.

Eventos de Resposta

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.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.