API

Webhooks

Receber notificações de eventos em tempo real via webhooks.

Webhooks

Em breve na API pública

O gerenciamento de webhooks via API (/api/v1/webhooks) ainda não está disponível. Por enquanto, webhooks podem ser configurados diretamente pelo painel do Ecosys Auto em Configurações → Webhooks.

Para dúvidas, entre em contato: suporte@ecosysauto.ai

Webhooks permitem que você receba notificações em tempo real sobre eventos importantes: novos veículos cadastrados, negócios ganhos/perdidos, atividades vencendo e muito mais.

Como Funciona

┌─────────────────────┐     ┌─────────────────────┐     ┌─────────────────────┐
│    Evento Ocorre    │────>│     Ecosys Auto      │────>│   Seu Servidor      │
│  (ex: negócio       │     │   envia POST        │     │   processa e        │
│   ganho)            │     │   para sua URL      │     │   responde 200 OK   │
└─────────────────────┘     └─────────────────────┘     └─────────────────────┘
  1. Um evento ocorre na plataforma (veículo cadastrado, negócio ganho, etc.)
  2. O Ecosys Auto envia um POST para a URL configurada
  3. Seu servidor processa o evento e responde com 200 OK
  4. Se falhar, tentamos novamente com backoff exponencial

Eventos Disponíveis

Eventos de Veículos

EventoQuando é Disparado
vehicle.createdNovo veículo cadastrado
vehicle.updatedDados/fase/preço alterados
vehicle.soldStatus alterado para vendido
vehicle.phase_changedVeículo movido no kanban
vehicle.price_changedPreço atualizado
vehicle.removedVeículo arquivado

Eventos de Negócios

EventoQuando é Disparado
deal.createdNovo negócio criado
deal.updatedDados ou fase alterados
deal.phase_changedNegócio movido no pipeline
deal.wonMarcado como ganho
deal.lostMarcado como perdido

Eventos de Clientes

EventoQuando é Disparado
client.createdNovo cliente cadastrado
client.updatedDados alterados
client.deactivatedCliente desativado

Eventos de Vendas

EventoQuando é Disparado
sale.deliveredVeículo entregue ao comprador (inclui prazos de garantia e transferência)
sale.cancelledVenda cancelada, inclusive depois da entrega

Eventos de Pós-venda

EventoQuando é Disparado
aftersale.ticket_openedOcorrência de pós-venda aberta para uma venda entregue (vício oculto, garantia, documentação etc.)
aftersale.survey_answeredPesquisa de satisfação (CSAT ou NPS) respondida pelo cliente. Dispara ao consolidar a resposta e de novo a cada edição consolidada

Eventos de Atividades

EventoQuando é Disparado
activity.createdNova atividade criada
activity.completedAtividade concluída
activity.due_soon24h antes do vencimento
activity.overduePrazo expirado

Eventos Financeiros

EventoQuando é Disparado
financial.createdNovo registro financeiro
financial.paidPagamento confirmado
financial.overdueVencimento expirado

Formato do Payload

Todas as notificações seguem este formato:

{
  "id": "evt_abc123def456",
  "type": "vehicle.created",
  "api_version": "v1",
  "created_at": "2026-02-04T14:30:00Z",
  "data": {
    "object": {
      "id": "uuid-do-veiculo",
      "title": "Honda Civic EXL 2.0 2024",
      "brand": "Honda",
      "model": "Civic",
      "price": 142900.00,
      "phase": "entrada",
      "status": "ativo",
      "created_at": "2026-02-04T14:30:00Z"
    },
    "previous_attributes": null
  },
  "team_id": "uuid-do-time",
  "webhook_id": "uuid-do-webhook"
}

Para eventos de atualização, previous_attributes contém os valores anteriores:

{
  "id": "evt_update123",
  "type": "vehicle.updated",
  "data": {
    "object": {
      "id": "uuid-do-veiculo",
      "phase": "em-vitrine",
      "price": 139900.00
    },
    "previous_attributes": {
      "phase": "entrada",
      "price": 142900.00
    }
  }
}

Exemplo: aftersale.ticket_opened

O objeto traz a ocorrência com o número da venda. O prazo cdc_due_at (30 dias a partir do recebimento do veículo, CDC art. 18 §1º) fica null até o veículo ser recebido pela loja.

{
  "id": "evt_ticket123",
  "type": "aftersale.ticket_opened",
  "api_version": "v1",
  "created_at": "2026-09-16T13:05:00Z",
  "data": {
    "object": {
      "id": "uuid-da-ocorrencia",
      "number": "OC-2026-0007",
      "case_id": "uuid-do-acompanhamento",
      "sale_id": "uuid-da-venda",
      "sale_number": "VND-2026-0042",
      "store_id": "uuid-da-loja",
      "client_id": "uuid-do-cliente",
      "vehicle_id": "uuid-do-veiculo",
      "seller_user_id": "uuid-do-vendedor",
      "owner_user_id": "uuid-do-responsavel",
      "type": "vicio_oculto",
      "status": "aberto",
      "severity": "alta",
      "coverage": "legal",
      "source": "staff",
      "title": "Barulho na suspensão dianteira",
      "description": "Cliente relata ruído ao passar em lombadas",
      "reported_at": "2026-09-16T13:05:00Z",
      "defect_discovered_on": "2026-09-14",
      "within_legal_warranty": true,
      "vehicle_received_at": null,
      "cdc_due_at": null,
      "first_response_due_at": "2026-09-17T13:05:00Z",
      "created_at": "2026-09-16T13:05:00Z"
    }
  },
  "team_id": "uuid-do-time"
}
CampoValores
typevicio_oculto, garantia_legal, garantia_contratual, garantia_estendida, documentacao, transferencia, financeiro, atendimento, arrependimento, outro
statusaberto, em_analise, aguardando_veiculo, em_reparo, aguardando_cliente, resolvido, recusado, cancelado
severitybaixa, media, alta
coveragelegal, contratual, estendida, cortesia, sem_cobertura ou null
sourcestaff, survey, whatsapp, customer_portal

Exemplo: aftersale.survey_answered

A resposta chega consolidada: a primeira nota vale, o cliente pode editar por até 10 minutos (no máximo 2 vezes) e cada edição consolidada dispara o evento de novo com edit_count maior. Use id + edit_count para saber se é a mesma resposta.

{
  "id": "evt_survey123",
  "type": "aftersale.survey_answered",
  "api_version": "v1",
  "created_at": "2026-09-20T18:12:00Z",
  "data": {
    "object": {
      "id": "uuid-da-resposta",
      "request_id": "uuid-do-pedido-de-pesquisa",
      "case_id": "uuid-do-acompanhamento",
      "sale_id": "uuid-da-venda",
      "sale_number": "VND-2026-0042",
      "store_id": "uuid-da-loja",
      "client_id": "uuid-do-cliente",
      "seller_user_id": "uuid-do-vendedor",
      "type": "csat",
      "score": 5,
      "classification": "promoter",
      "comment": "Entrega rápida e carro impecável",
      "source": "web_link",
      "respondent_verified": true,
      "suspected_bot": false,
      "answered_at": "2026-09-20T18:11:40Z",
      "edit_count": 0
    }
  },
  "team_id": "uuid-do-time"
}
CampoValores
typecsat (nota de 1 a 5, sobre a entrega) ou nps (nota de 0 a 10, 30 dias depois)
classificationdetractor, passive ou promoter — calculada pela escala do type
sourceweb_link e web_form (página da pesquisa), whatsapp_text (cliente digitou o número), whatsapp_button (tocou no botão) ou staff (alguém da loja registrou)
respondent_verifiedtrue quando houve interação confirmada do cliente (não é só a abertura do link)
suspected_bottrue quando as regras de varredura acharam que a abertura foi de um scanner de link, não de uma pessoa
edit_count0 na primeira resposta; aumenta a cada edição consolidada

Verificando a Assinatura

Para garantir que a notificação veio do Ecosys Auto, verifique a assinatura no header X-Ecosys-Auto-Signature.

Headers Enviados

HeaderDescrição
X-Ecosys-Auto-SignatureAssinatura HMAC-SHA256
X-Ecosys-Auto-TimestampTimestamp Unix do envio
X-Ecosys-Auto-Event-IdID único do evento
Content-Typeapplication/json

Verificação em Node.js

import crypto from 'crypto';
import express from 'express';

const app = express();
const WEBHOOK_SECRET = process.env.ECOSYS_AUTO_WEBHOOK_SECRET;

app.use('/webhook', express.raw({ type: 'application/json' }));

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-ecosys-auto-signature'];
  const timestamp = req.headers['x-ecosys-auto-timestamp'];
  const payload = req.body.toString();

  // Verificar timestamp (previne replay attacks — janela de 5 minutos)
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    return res.status(401).json({ error: 'Timestamp expirado' });
  }

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

  // Comparação segura (previne timing attacks)
  if (!crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  )) {
    return res.status(401).json({ error: 'Assinatura inválida' });
  }

  // Processar evento de forma assíncrona
  const event = JSON.parse(payload);

  switch (event.type) {
    case 'vehicle.created':
      // Publicar no portal de anúncios, enviar notificação, etc.
      break;
    case 'deal.won':
      // Atualizar métricas, gerar contrato, etc.
      break;
    case 'activity.overdue':
      // Enviar alerta, criar ticket, etc.
      break;
  }

  // Responda RAPIDAMENTE com 200 OK
  res.status(200).json({ received: true });
});

Verificação em Python

import hmac
import hashlib
import time
import os
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = os.environ['ECOSYS_AUTO_WEBHOOK_SECRET']

@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Ecosys-Auto-Signature')
    timestamp = request.headers.get('X-Ecosys-Auto-Timestamp')
    payload = request.get_data(as_text=True)

    # Verificar timestamp
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return jsonify({'error': 'Timestamp expirado'}), 401

    # Calcular assinatura
    signed_payload = f"{timestamp}.{payload}"
    expected_signature = hmac.new(
        WEBHOOK_SECRET.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()

    # Comparação segura
    if not hmac.compare_digest(signature, expected_signature):
        return jsonify({'error': 'Assinatura inválida'}), 401

    event = request.json
    # Processar evento...

    return jsonify({'received': True}), 200

Política de Retentativas

Se sua URL não responder com 2xx, tentaremos novamente:

TentativaIntervalo
1aImediato
2a5 minutos
3a30 minutos
4a2 horas
5a12 horas
6a24 horas

Após 6 tentativas falhas consecutivas, o webhook é desativado automaticamente. Você receberá um email de alerta.

Boas Práticas

  • Responda rapidamente — Retorne 200 OK em até 5 segundos
  • Processe assincronamente — Use filas (Bull, Celery, etc.) para tarefas demoradas
  • Implemente idempotência — O mesmo evento pode ser entregue mais de uma vez (salve o id do evento)
  • Verifique a assinatura — Sempre valide a autenticidade antes de processar
  • Use HTTPS — Obrigatório para receber webhooks

Roadmap

Planejamos disponibilizar os seguintes endpoints para gerenciar webhooks via API:

GET    /api/v1/webhooks         — Listar webhooks configurados
POST   /api/v1/webhooks         — Criar webhook
PUT    /api/v1/webhooks/:id     — Atualizar webhook
DELETE /api/v1/webhooks/:id     — Remover webhook

On this page