Conceptos

Webhooks

Los webhooks te avisan en el momento exacto en que algo pasa on-chain, sin polling. Registra tu endpoint en el Portal de Comercios, guarda el secret whsec_ y verifica la firma HMAC-SHA256 de cada entrega antes de procesarla.

Cómo funciona el flujo

Detectamos el evento en la red, generamos el payload, lo firmamos y lo entregamos a tu endpoint. Si tu servidor no responde 2xx, reintentamos con backoff exponencial.

  1. 1

    Transacción detectada on-chain

    El motor de Vaultcore observa las redes soportadas (Polygon, Base, Tron y más) y detecta depósitos y confirmaciones de transferencias.

  2. 2

    Payload firmado con HMAC-SHA256

    Se genera el cuerpo JSON del evento y se firma con el secret de tu webhook. La firma viaja en el header X-Vaultcore-Signature.

  3. 3

    POST a tu endpoint HTTPS

    Enviamos un POST a la URL registrada. Verifica la firma sobre el raw body y responde 200 OK en menos de 5 segundos.

  4. 4

    Reintento automático ante fallas

    Si respondes 5xx o hay timeout, programamos reintentos asíncronos con backoff exponencial y jitter hasta 4 veces.

Seguridad: firma HMAC-SHA256

Cada endpoint registrado recibe un Webhook Secret único con prefijo whsec_ (por ejemplo whsec_e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0). Firmamos el raw body exacto con ese secret y enviamos el resultado hexadecimal en X-Vaultcore-Signature.

signature
Signature = HMAC_SHA256(key = webhook.secret, data = raw_body)
No parsees el JSON antes de verificar

Cambios en el orden de las propiedades o espacios introducidos por un parser JSON alteran el hash y romperán la verificación. Usa siempre el buffer del raw body y compara con una función de tiempo constante (crypto.timingSafeEqual, hmac.compare_digest, hash_equals).

Headers de cada entrega

Toda solicitud POST enviada a tu URL incluye estos encabezados.

Headers HTTP

Content-Typestring

Siempre application/json.

X-Vaultcore-Signaturestringrequerido

Firma hexadecimal HMAC-SHA256 del raw body (64 caracteres).

X-Vaultcore-Eventstring

Tipo de evento notificado, por ejemplo deposit.confirmed.

X-Vaultcore-Delivery-Idstring

Identificador único del intento de entrega (del_01J2K3L4M5N6P7Q8R9S0). Útil para deduplicar.

X-Vaultcore-Simulationstring

Presente solo en eventos de prueba o simulación enviados desde el portal.

User-Agentstring

vaultcore-webhook-dispatcher/1.0

request
POST https://tu-dominio.com/webhooks/vaultcore
Content-Type: application/json
X-Vaultcore-Signature: a1b2c3d4e5f6...   # 64 caracteres hex
X-Vaultcore-Event: deposit.confirmed
X-Vaultcore-Delivery-Id: del_01J2K3L4M5N6P7Q8R9S0
User-Agent: vaultcore-webhook-dispatcher/1.0

Eventos soportados

Suscríbete solo a los eventos que tu backend necesita procesar.

Eventos

deposit.detectedevent

Depósito entrante detectado en mempool o bloque reciente (0 a N-1 confirmaciones). Muestra "depósito en camino", pero no acredites saldo disponible todavía.

deposit.confirmedevent

El depósito alcanzó las confirmaciones requeridas y fue acreditado en el libro contable. Este es el evento definitivo para acreditar al usuario o entregar bienes y servicios.

transfer.confirmedevent

Retiro o transferencia saliente confirmada en la blockchain con su tx_hash final. Marca la orden como completada.

transfer.failedevent

La transferencia falló o fue cancelada; los fondos se reintegraron al balance contable. Notifica al usuario o reintenta con nuevos parámetros.

gas_tank.low_balanceevent

El balance de la wallet de gas está por debajo del umbral operativo. Recarga fondos nativos (POL, ETH, TRX) para no bloquear barridos ni retiros.

Ejemplos de payload

Todos los eventos comparten la misma envoltura: id, event, created_at y data.

deposit.detected
{
  "id": "evt_01J9X1A2B3C4D5E6F7G8H9J0",
  "event": "deposit.detected",
  "created_at": "2026-08-20T21:15:30.120Z",
  "data": {
    "deposit_id": "dep_01J9X1A2B3C4D5E6F7G8H9J0",
    "merchant_id": "mrc_01HZX89ABCDEF1234567890",
    "end_user_id": "usr_internal_9381",
    "network": "POLYGON",
    "asset": "USDT",
    "amount": "250.000000",
    "tx_hash": "0x4f8a9b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a",
    "deposit_address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
    "confirmations": 2,
    "required_confirmations": 12,
    "detected_at": "2026-08-20T21:15:30.000Z"
  }
}

Idempotencia y deduplicación

Por la naturaleza de las redes distribuidas y los reintentos automáticos, tu receptor debe ser idempotente.

  • Guarda los eventos procesados: almacena data.deposit_id, data.transfer_id o X-Vaultcore-Delivery-Id con una restricción UNIQUE.
  • Ignora duplicados: si el identificador ya fue procesado, responde 200 OK sin volver a acreditar fondos ni duplicar acciones.
  • Responde rápido: devuelve un 2xx en menos de 5 segundos y mueve el trabajo pesado (emails, sincronizaciones) a workers o colas.
  • Los eventos pueden llegar desordenados: no asumas que deposit.detected llega siempre antes que deposit.confirmed.

Reintentos y resiliencia

Si respondes fuera del rango 200-299 (500, 502, 504) o la conexión supera 10 segundos de timeout, reintentamos hasta 4 veces con backoff exponencial y jitter.

Calendario de reintentos

1er reintento~60 s

Aproximadamente 1 minuto después del fallo.

2do reintento~5 min

300 segundos después del intento anterior.

3er reintento~15 min

900 segundos después del intento anterior.

4to reintento~1 h

3600 segundos después; es el intento final.

EXHAUSTEDstatus

Si tras 4 intentos sigue fallando, la entrega queda en EXHAUSTED. Puedes inspeccionar el error y hacer un re-envío manual desde el Portal de Comercios.

Mantén además un job de conciliación sobre GET /v1/deposits cada pocos minutos: webhooks y conciliación se complementan.

Verificar la firma en tu servidor

Implementaciones listas para copiar en Node.js, Python, Go y PHP.

Node.js / TypeScript
import express, { Request, Response } from "express";
import crypto from "crypto";

const app = express();
const WEBHOOK_SECRET = process.env.VAULTCORE_WEBHOOK_SECRET || "whsec_...";

// IMPORTANTE: capturar el raw body como Buffer para verificar la firma
app.post(
  "/webhooks/vaultcore",
  express.raw({ type: "application/json" }),
  (req: Request, res: Response) => {
    const signatureHeader = req.headers["x-vaultcore-signature"] as string;
    const eventType = req.headers["x-vaultcore-event"] as string;
    const deliveryId = req.headers["x-vaultcore-delivery-id"] as string;

    if (!signatureHeader) {
      return res.status(401).json({ error: "Missing X-Vaultcore-Signature header" });
    }

    const rawBody = req.body; // Buffer en crudo

    const computedSignature = crypto
      .createHmac("sha256", WEBHOOK_SECRET)
      .update(rawBody)
      .digest("hex");

    const signatureBuffer = Buffer.from(signatureHeader, "utf8");
    const computedBuffer = Buffer.from(computedSignature, "utf8");

    if (
      signatureBuffer.length !== computedBuffer.length ||
      !crypto.timingSafeEqual(signatureBuffer, computedBuffer)
    ) {
      return res.status(401).json({ error: "Invalid webhook signature" });
    }

    const payload = JSON.parse(rawBody.toString("utf8"));
    console.log(`Webhook verificado [${eventType}] - Delivery ID: ${deliveryId}`);

    switch (eventType) {
      case "deposit.confirmed":
        // Acredita saldo de forma idempotente usando payload.data.deposit_id
        break;
      case "transfer.confirmed":
        // Marca el retiro como completado con payload.data.tx_hash
        break;
      default:
        break;
    }

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

app.listen(3000);

Configuración y monitoreo en el portal

La gestión de webhooks no requiere integración por API: se administra de forma visual desde el Portal de Comercios.

  1. 1

    Configura tus endpoints

    Entra a Desarrolladores > Webhooks, haz clic en Crear Webhook, indica tu URL HTTPS receptora y selecciona los eventos a los que quieres suscribirte.

  2. 2

    Guarda el secret whsec_

    Al crear el webhook se genera tu Webhook Secret. Guárdalo en el gestor de secretos de tu servidor para verificar X-Vaultcore-Signature.

  3. 3

    Simula eventos en tiempo real

    Usa Probar / Simular Webhook para enviar eventos de prueba instantáneos y validar tu lógica de verificación antes de ir a producción.

  4. 4

    Revisa el historial de entregas

    El visor muestra headers enviados, código HTTP recibido, cuerpo devuelto y latencia en milisegundos. Puedes presionar Re-enviar sobre cualquier entrega fallida.

Rotación de secretos

Rota el secret desde el Portal de Comercios si sospechas que se filtró; durante la rotación acepta temporalmente la firma antigua y la nueva.