Skip to main content
O plugin TED envia webhooks para notificar sua aplicação sobre eventos relevantes, como conclusão de transferências, falhas e recebimentos. Isso permite que você reaja em tempo real sem precisar consultar a API repetidamente.

Configuração


Configure o webhook na sua organização:
CampoDescrição
webhookUrlURL HTTPS que receberá os eventos
webhookSecretChave secreta para validação da assinatura
A configuração é feita por organização, permitindo que cada tenant tenha seu próprio endpoint.

Exemplo de configuração

Exemplo — substitua pelas suas credenciais reais:
{
  "webhookUrl": "https://api.suaempresa.com/webhooks/ted",
  "webhookSecret": "<SEU_SEGREDO_WEBHOOK>"
}

Tipos de evento


O plugin emite quatro tipos de evento:
EventoDisparado quando
transfer.completedTransferência TED OUT ou P2P concluída
transfer.failedTransferência falhou ou foi rejeitada
transfer.incomingTransferência TED IN recebida e creditada
transfer.cancelledTransferência cancelada pelo usuário

Estrutura do payload


Todos os webhooks seguem a mesma estrutura base:
{
  "event": "transfer.completed",
  "timestamp": "2026-01-21T14:35:00-03:00",
  "organizationId": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    // dados específicos do evento
  }
}
CampoTipoDescrição
eventStringTipo do evento
timestampISO 8601Momento em que o evento ocorreu
organizationIdUUIDOrganização à qual o evento pertence
dataObjectDados específicos do evento

Eventos detalhados


transfer.completed

Enviado quando uma transferência TED OUT ou P2P é concluída.
{
  "event": "transfer.completed",
  "timestamp": "2026-01-21T14:35:00-03:00",
  "organizationId": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "transferId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "type": "TED_OUT",
    "status": "COMPLETED",
    "confirmationNumber": "20260121001",
    "controlNumber": "JD20260121000001",
    "amount": 1500.00,
    "feeAmount": 8.50,
    "totalAmount": 1508.50,
    "sender": {
      "accountId": "550e8400-e29b-41d4-a716-446655440000",
      "holderName": "João Santos"
    },
    "recipient": {
      "ispb": "00000000",
      "branch": "0001",
      "account": "123456",
      "holderName": "Maria Silva"
    },
    "completedAt": "2026-01-21T14:35:00-03:00"
  }
}

transfer.failed

Enviado quando uma transferência falha ou é rejeitada.
{
  "event": "transfer.failed",
  "timestamp": "2026-01-21T14:40:00-03:00",
  "organizationId": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "transferId": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
    "type": "TED_OUT",
    "status": "REJECTED",
    "amount": 2000.00,
    "feeAmount": 8.50,
    "totalAmount": 2008.50,
    "sender": {
      "accountId": "550e8400-e29b-41d4-a716-446655440000",
      "holderName": "João Santos"
    },
    "recipient": {
      "ispb": "60746948",
      "branch": "1234",
      "account": "567890",
      "holderName": "Carlos Oliveira"
    },
    "failureReason": "Conta destinatária não existe",
    "failureCode": "STR0010R2-AC03",
    "refundedAt": "2026-01-21T14:40:05-03:00",
    "failedAt": "2026-01-21T14:40:00-03:00"
  }
}
O campo failureCode contém o código de erro do SPB quando aplicável.

transfer.incoming

Enviado quando uma transferência TED IN é recebida e creditada.
{
  "event": "transfer.incoming",
  "timestamp": "2026-01-21T10:15:05-03:00",
  "organizationId": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "transferId": "c3d4e5f6-a7b8-9012-cdef-345678901234",
    "type": "TED_IN",
    "status": "COMPLETED",
    "controlNumber": "JD20260121000042",
    "amount": 5000.00,
    "feeAmount": 0.00,
    "netAmount": 5000.00,
    "sender": {
      "ispb": "60746948",
      "branch": "1234",
      "account": "567890",
      "holderName": "Carlos Oliveira",
      "holderDocument": "98765432100"
    },
    "recipient": {
      "accountId": "660e8400-e29b-41d4-a716-446655440001",
      "holderName": "Empresa ABC Ltda",
      "holderDocument": "12345678000199"
    },
    "creditedAt": "2026-01-21T10:15:05-03:00"
  }
}

transfer.cancelled

Enviado quando uma transferência é cancelada pelo usuário.
{
  "event": "transfer.cancelled",
  "timestamp": "2026-01-21T14:31:00-03:00",
  "organizationId": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "transferId": "d4e5f6a7-b8c9-0123-defg-456789012345",
    "type": "TED_OUT",
    "status": "CANCELLED",
    "amount": 1000.00,
    "feeAmount": 8.50,
    "sender": {
      "accountId": "550e8400-e29b-41d4-a716-446655440000",
      "holderName": "João Santos"
    },
    "cancellationReason": "Solicitação do cliente",
    "cancelledAt": "2026-01-21T14:31:00-03:00"
  }
}

Validação da assinatura


Todos os webhooks incluem uma assinatura HMAC-SHA256 no header X-Signature. Valide essa assinatura para garantir que o webhook é autêntico.

Header de assinatura

X-Signature: sha256=a1b2c3d4e5f6...

Validação em código

const crypto = require('crypto');

function validateWebhook(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  const receivedSignature = signature.replace('sha256=', '');

  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(receivedSignature)
  );
}

// Uso
app.post('/webhooks/ted', (req, res) => {
  const payload = JSON.stringify(req.body);
  const signature = req.headers['x-signature'];

  if (!validateWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  // Processar webhook...
  res.status(200).send('OK');
});
import hmac
import hashlib

def validate_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()

    received = signature.replace('sha256=', '')

    return hmac.compare_digest(expected, received)
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "strings"
)

func validateWebhook(payload []byte, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(payload)
    expected := hex.EncodeToString(mac.Sum(nil))

    received := strings.TrimPrefix(signature, "sha256=")

    return hmac.Equal([]byte(expected), []byte(received))
}

Política de retry


Se seu endpoint não responder com status 2xx, o plugin reenvia o webhook com backoff exponencial:
TentativaIntervalo
1Imediato
25 segundos
325 segundos
460 segundos
5120 segundos
Após 5 tentativas sem sucesso, o webhook é movido para uma dead-letter queue (DLQ).

Requisitos do endpoint

Para garantir entrega confiável:
  • Responda com status 2xx em até 30 segundos
  • Use HTTPS com certificado válido
  • Evite processamento pesado no handler (use filas)

Idempotência


Webhooks podem ser entregues mais de uma vez em cenários de retry. Implemente idempotência usando o transferId:
app.post('/webhooks/ted', async (req, res) => {
  const { event, data } = req.body;
  const { transferId } = data;

  // Verifica se já processou este evento
  const processed = await cache.get(`webhook:${transferId}:${event}`);
  if (processed) {
    return res.status(200).send('Already processed');
  }

  // Processa o webhook
  await processWebhook(event, data);

  // Marca como processado (TTL de 24h)
  await cache.set(`webhook:${transferId}:${event}`, true, 86400);

  res.status(200).send('OK');
});

Monitoramento


O plugin expõe métricas sobre entrega de webhooks:
webhook_delivery_total{event, status}
webhook_delivery_latency_ms{event}
webhook_retry_total{event, attempt}
webhook_dlq_total{event}
Use essas métricas para monitorar a saúde da integração e identificar problemas de entrega.

Testando webhooks


Durante o desenvolvimento, você pode usar ferramentas como:
  • webhook.site — endpoint temporário para testes
  • ngrok — expõe localhost para a internet
  • RequestBin — inspeciona requests recebidos
Configure a URL de teste no ambiente de staging antes de ir para produção.

Boas práticas


PráticaDescrição
Valide a assinaturaSempre verifique o header X-Signature
Responda rápidoRetorne 200 antes de processar; use filas
Implemente idempotênciaO mesmo webhook pode chegar mais de uma vez
Monitore a DLQConfigure alertas para webhooks não entregues
Use HTTPSWebhooks só são enviados para endpoints seguros
Trate todos os eventosMesmo que ignore alguns, retorne 200