API

Webhooks

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

Webhooks

Receba notificações em tempo real sobre eventos importantes no Juspilot: novos processos, prazos vencendo, atividades concluídas e mais.

Por que usar Webhooks?

Webhooks permitem que você seja notificado automaticamente quando eventos ocorrem, eliminando a necessidade de polling na API. Ideal para integrações com CRMs, sistemas de gestão e automações.

Como Funciona

┌─────────────────────┐     ┌─────────────────────┐     ┌─────────────────────┐
│    Evento Ocorre    │────▶│     Juspilot        │────▶│   Seu Servidor      │
│  (ex: prazo         │     │   envia POST        │     │   processa e        │
│   vencendo)         │     │   para sua URL      │     │   responde 200 OK   │
└─────────────────────┘     └─────────────────────┘     └─────────────────────┘
  1. Um evento ocorre na plataforma (processo criado, prazo vencendo, etc.)
  2. O Juspilot 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

GET /v1/webhooks

Lista todos os webhooks configurados.

Request

curl -X GET https://api.juspilot.co/v1/webhooks \
  -H "Authorization: Bearer jp_live_sua_chave_aqui"

Response

{
  "success": true,
  "data": [
    {
      "id": "wh_abc123def456",
      "url": "https://seu-app.com/api/webhooks/juspilot",
      "events": [
        "case.created",
        "case.updated",
        "activity.due_soon",
        "activity.overdue"
      ],
      "active": true,
      "created_at": "2026-01-10T10:00:00Z",
      "last_triggered_at": "2026-02-04T14:30:00Z",
      "success_rate": 98.5,
      "total_deliveries": 127,
      "failed_deliveries": 2
    }
  ]
}

POST /v1/webhooks

Cria um novo webhook.

Request Body

CampoTipoObrigatórioDescrição
urlstringSimURL que receberá as notificações (HTTPS)
eventsarraySimLista de eventos para escutar
descriptionstringNãoDescrição do webhook

Requisitos da URL

  • Deve usar HTTPS (HTTP não é aceito)
  • Deve ser acessível publicamente
  • Deve responder em menos de 5 segundos
  • Deve retornar status 2xx para confirmar recebimento

Request

curl -X POST https://api.juspilot.co/v1/webhooks \
  -H "Authorization: Bearer jp_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://seu-app.com/api/webhooks/juspilot",
    "events": [
      "case.created",
      "case.updated",
      "case.archived",
      "activity.created",
      "activity.completed",
      "activity.due_soon",
      "activity.overdue",
      "client.created",
      "deal.won",
      "deal.lost"
    ],
    "description": "Integração com CRM interno"
  }'

Response

{
  "success": true,
  "data": {
    "id": "wh_new123abc456",
    "url": "https://seu-app.com/api/webhooks/juspilot",
    "events": ["case.created", "case.updated", "..."],
    "active": true,
    "secret": "whsec_AbCdEfGhIjKlMnOpQrStUvWxYz123456789",
    "created_at": "2026-02-04T10:30:00Z"
  }
}

Guarde o Secret!

O secret é exibido apenas uma vez. Use-o para verificar a autenticidade das notificações recebidas.


PUT /v1/webhooks/:id

Atualiza um webhook.

Request

curl -X PUT https://api.juspilot.co/v1/webhooks/wh_abc123def456 \
  -H "Authorization: Bearer jp_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["case.created", "case.updated"],
    "active": true
  }'

Response

{
  "success": true,
  "data": {
    "id": "wh_abc123def456",
    "events": ["case.created", "case.updated"],
    "active": true,
    "updated_at": "2026-02-04T15:00:00Z"
  }
}

DELETE /v1/webhooks/:id

Remove um webhook.

Request

curl -X DELETE https://api.juspilot.co/v1/webhooks/wh_abc123def456 \
  -H "Authorization: Bearer jp_live_sua_chave_aqui"

Response

{
  "success": true,
  "data": {
    "id": "wh_abc123def456",
    "deleted": true
  }
}

Eventos Disponíveis

Eventos de Processos

EventoDescriçãoQuando é Disparado
case.createdProcesso criadoNovo processo via app ou API
case.updatedProcesso atualizadoAlteração de dados/fase/prioridade
case.archivedProcesso arquivadoStatus alterado para arquivado
case.phase_changedFase alteradaProcesso movido no kanban
case.sla_warningAlerta de SLA24h antes do prazo SLA
case.sla_overdueSLA vencidoPrazo SLA expirado

Eventos de Atividades

EventoDescriçãoQuando é Disparado
activity.createdAtividade criadaNova atividade/tarefa
activity.updatedAtividade atualizadaAlteração de dados
activity.completedAtividade concluídaStatus alterado para completed
activity.due_soonPrazo próximo24h antes do vencimento
activity.overdueAtividade atrasadaPrazo expirado

Eventos de Clientes

EventoDescriçãoQuando é Disparado
client.createdCliente criadoNovo cliente cadastrado
client.updatedCliente atualizadoAlteração de dados
client.deactivatedCliente desativadoStatus alterado para inactive

Eventos de Negócios

EventoDescriçãoQuando é Disparado
deal.createdNegócio criadoNovo negócio no pipeline
deal.updatedNegócio atualizadoAlteração de dados/fase
deal.phase_changedFase alteradaNegócio movido no pipeline
deal.wonNegócio ganhoMarcado como ganho
deal.lostNegócio perdidoMarcado como perdido

Eventos Financeiros

EventoDescriçãoQuando é Disparado
financial.createdRegistro criadoNovo registro financeiro
financial.paidPagamento confirmadoStatus alterado para paid
financial.overduePagamento atrasadoVencimento expirado

Formato do Payload

Todas as notificações seguem este formato:

{
  "id": "evt_abc123def456",
  "type": "case.created",
  "api_version": "v1",
  "created_at": "2026-02-04T14:30:00Z",
  "data": {
    "object": {
      "id": "case_xyz789",
      "title": "Ação de Cobrança - Cliente XYZ",
      "number": "0001234-56.2026.8.26.0100",
      "area": "Cível",
      "phase": "recebido",
      "priority": "alta",
      "status": "ativo",
      "client": {
        "id": "cli_abc123",
        "name": "João da Silva"
      },
      "created_at": "2026-02-04T14:30:00Z"
    },
    "previous_attributes": null
  },
  "team_id": "team_xyz789",
  "webhook_id": "wh_abc123def456"
}

Para eventos de atualização

{
  "id": "evt_update123",
  "type": "case.updated",
  "data": {
    "object": {
      "id": "case_xyz789",
      "phase": "em-andamento",
      "priority": "media"
    },
    "previous_attributes": {
      "phase": "recebido",
      "priority": "alta"
    }
  }
}

Verificando a Assinatura

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

Headers Enviados

HeaderDescrição
X-Juspilot-SignatureAssinatura HMAC-SHA256
X-Juspilot-TimestampTimestamp Unix do envio
X-Juspilot-Event-IdID 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.JUSPILOT_WEBHOOK_SECRET;

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

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

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

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

  // Comparação segura
  if (!crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  )) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Processar evento
  const event = JSON.parse(payload);
  console.log(`Evento recebido: ${event.type}`);

  switch (event.type) {
    case 'case.created':
      handleCaseCreated(event.data.object);
      break;
    case 'activity.overdue':
      handleActivityOverdue(event.data.object);
      break;
    case 'deal.won':
      handleDealWon(event.data.object);
      break;
  }

  res.status(200).json({ received: true });
});

function handleCaseCreated(caseData) {
  console.log(`Novo processo: ${caseData.title}`);
  // Sincronizar com CRM, enviar notificação, etc.
}

function handleActivityOverdue(activity) {
  console.log(`Atividade atrasada: ${activity.title}`);
  // Enviar alerta, criar ticket, etc.
}

function handleDealWon(deal) {
  console.log(`Negócio ganho: ${deal.title} - R$ ${deal.value}`);
  // Atualizar métricas, enviar comemoração, etc.
}

Verificação em Python

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

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

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

    # Verificar timestamp
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return jsonify({'error': 'Timestamp too old'}), 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': 'Invalid signature'}), 401

    # Processar evento
    event = request.json
    print(f"Evento recebido: {event['type']}")

    if event['type'] == 'case.created':
        handle_case_created(event['data']['object'])
    elif event['type'] == 'activity.overdue':
        handle_activity_overdue(event['data']['object'])

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

Política de Retentativas

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

TentativaIntervalo
Imediato
5 minutos
30 minutos
2 horas
12 horas
24 horas

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


Boas Práticas

✅ Faça

  1. Responda rapidamente - Retorne 200 OK em até 5 segundos
  2. Processe assincronamente - Use filas para tarefas demoradas
  3. Implemente idempotência - O mesmo evento pode ser enviado mais de uma vez
  4. Verifique a assinatura - Sempre valide a autenticidade
  5. Use HTTPS - Obrigatório para receber webhooks
  6. Monitore entregas - Acompanhe a taxa de sucesso

❌ Não Faça

  1. Processar no handler - Não execute lógica pesada síncrona
  2. Ignorar duplicados - Mesmo evento pode vir mais de uma vez
  3. Confiar cegamente - Sempre verifique a assinatura
  4. Usar HTTP - Apenas HTTPS é aceito
  5. Timeout longo - Não demore mais que 5 segundos

Erros Comuns

CódigoErroSolução
400Invalid URLUse URL HTTPS válida
400Invalid eventsVerifique lista de eventos
404Webhook not foundVerifique o ID
409URL already registeredURL já existe
422URL not reachableVerifique firewall/DNS

On this page