Saltar al contenido principal
El plugin TED envía webhooks para notificar a su aplicación sobre eventos relevantes, como conclusión de transferencias, fallas y recibos. Esto permite reaccionar en tiempo real sin necesidad de consultar la API repetidamente.

Configuración


Configure el webhook en su organización:
CampoDescripción
webhookUrlURL HTTPS que recibirá los eventos
webhookSecretClave secreta para validación de firma
La configuración se hace por organización, permitiendo que cada tenant tenga su propio endpoint.

Ejemplo de configuración

Ejemplo — reemplace con sus credenciales reales:
{
  "webhookUrl": "https://api.suempresa.com/webhooks/ted",
  "webhookSecret": "<SU_SECRETO_WEBHOOK>"
}

Tipos de evento


El plugin emite cuatro tipos de evento:
EventoDisparado cuando
transfer.completedTransferencia TED OUT o P2P completada
transfer.failedTransferencia falló o fue rechazada
transfer.incomingTransferencia TED IN recibida y acreditada
transfer.cancelledTransferencia cancelada por el usuario

Estructura del payload


Todos los webhooks siguen la misma estructura base:
{
  "event": "transfer.completed",
  "timestamp": "2026-01-21T14:35:00-03:00",
  "organizationId": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    // datos específicos del evento
  }
}
CampoTipoDescripción
eventStringTipo del evento
timestampISO 8601Momento en que el evento ocurrió
organizationIdUUIDOrganización a la que pertenece el evento
dataObjectDatos específicos del evento

Eventos detallados


transfer.completed

Enviado cuando una transferencia TED OUT o P2P es completada.
{
  "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 cuando una transferencia falla o es rechazada.
{
  "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": "Cuenta destinataria no existe",
    "failureCode": "STR0010R2-AC03",
    "refundedAt": "2026-01-21T14:40:05-03:00",
    "failedAt": "2026-01-21T14:40:00-03:00"
  }
}
El campo failureCode contiene el código de error del SPB cuando aplica.

transfer.incoming

Enviado cuando una transferencia TED IN es recibida y acreditada.
{
  "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,
    "totalAmount": 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 cuando una transferencia es cancelada por el usuario.
{
  "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": "Solicitud del cliente",
    "cancelledAt": "2026-01-21T14:31:00-03:00"
  }
}

Validación de firma


Todos los webhooks incluyen una firma HMAC-SHA256 en el header X-Signature. Valide esta firma para garantizar que el webhook es auténtico.

Header de firma

X-Signature: sha256=a1b2c3d4e5f6...

Validación en 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');
  }

  // Procesar 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


Si su endpoint no responde con estado 2xx, el plugin reenvía el webhook con backoff exponencial:
IntentoIntervalo
1Inmediato
25 segundos
325 segundos
460 segundos
5120 segundos
Después de 5 intentos sin éxito, el webhook es movido a una dead-letter queue (DLQ).

Requisitos del endpoint

Para garantizar entrega confiable:
  • Responda con estado 2xx en hasta 30 segundos
  • Use HTTPS con certificado válido
  • Evite procesamiento pesado en el handler (use colas)

Idempotencia


Los webhooks pueden ser entregados más de una vez en escenarios de retry. Implemente idempotencia usando el transferId:
app.post('/webhooks/ted', async (req, res) => {
  const { event, data } = req.body;
  const { transferId } = data;

  // Verifica si ya procesó este evento
  const processed = await cache.get(`webhook:${transferId}:${event}`);
  if (processed) {
    return res.status(200).send('Already processed');
  }

  // Procesa el webhook
  await processWebhook(event, data);

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

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

Monitoreo


El plugin expone 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 estas métricas para monitorear la salud de la integración e identificar problemas de entrega.

Probando webhooks


Durante el desarrollo, puede usar herramientas como: Configure la URL de prueba en el ambiente de staging antes de ir a producción.

Buenas prácticas


PrácticaDescripción
Valide la firmaSiempre verifique el header X-Signature
Responda rápidoRetorne 200 antes de procesar; use colas
Implemente idempotenciaEl mismo webhook puede llegar más de una vez
Monitoree la DLQConfigure alertas para webhooks no entregados
Use HTTPSLos webhooks solo son enviados a endpoints seguros
Trate todos los eventosAunque ignore algunos, retorne 200