Saltar al contenido

Webhooks firmados: entérate de todo al momento

Añadir un endpoint, elegir eventos, verificar la firma en Node.js, Python y PHP, reintentos, idempotencia y la lista completa de eventos.

10 min de lectura4 capítulosPara foundersAvanzadoActualizada el 17 oct 2026

Capítulo 1Añadir un endpoint

  1. En API y webhooks, pulsa «Añadir endpoint»
  2. Escribe la URL de tu servidorTiene que ser https y pública (no localhost ni IPs internas). No seguimos redirecciones.
  3. Elige los eventos«Todos los eventos», o los que quieras. También vale un comodín por grupo, como milestone.*.
  4. Copia el secreto de firmaGuárdalo como KROWS_WEBHOOK_SECRET.
  5. Pulsa «Enviar evento de prueba»Llega un test.ping. Mira el resultado en el registro.

Cada endpoint es de un modo: prueba o real, según el selector.

Capítulo 2Verificar la firma

Cada envío es un POST con Content-Type: application/json, User-Agent: Krows-Webhooks/1.0 y la cabecera Krows-Signature: t=<marca de tiempo>,v1=<firma>. La firma es HMAC-SHA256 en hexadecimal de t + "." + cuerpo con el secreto de tu endpoint. Usa el cuerpo en bruto, tal cual llega.

import crypto from 'node:crypto';
import express from 'express';
const app = express();

// Use the raw body: the signature is computed over the exact bytes we send
app.post('/webhooks/krows', express.raw({ type: 'application/json' }), (req, res) => {
  const header = req.get('Krows-Signature') ?? '';
  const { t, v1 } = Object.fromEntries(header.split(',').map((x) => x.split('=')));
  const expected = crypto.createHmac('sha256', process.env.KROWS_WEBHOOK_SECRET)
    .update(`${t}.${req.body}`).digest('hex');
  const valid = v1 && v1.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
  // Reject anything older than 5 minutes to stop replays
  if (!valid || Date.now() / 1000 - Number(t) > 300) return res.sendStatus(400);

  const event = JSON.parse(req.body);
  switch (event.type) {
    case 'investment.confirmed':
      // event.data is the investment: amount, tokens, investor, reference, metadata…
      break;
    case 'round.succeeded':
      break;
  }
  res.sendStatus(200); // answer 2xx within 10 seconds
});

Capítulo 3Respuestas y reintentos

  • Responde 2xx en menos de 10 segundos. Si tienes trabajo largo, encólalo y responde ya.
  • Si falla, reintentamos a 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas y 24 horas: hasta 6 reintentos.
  • Puede llegar el mismo evento más de una vez: guarda el id del evento y procésalo una sola vez.
  • El orden no está garantizado: usa created y consulta la API si lo necesitas.
  • Si te perdiste algo, GET /v1/events devuelve el historial.

Capítulo 4Todos los eventos

EventoCuándo
investment.confirmedAlguien ha invertido
investment.withdrawnUn inversor retira su inversión
checkout.session.completedSe completa una sesión creada por API
round.succeededLa ronda llega al 100 %
round.failedLa ronda no llega o se cancela
tokens.deliveredUn inversor recibe sus tokens
refund.completedSe devuelve el USDC a un inversor
milestone.requestedPides liberar un hito y se abre la votación
milestone.approvedLos inversores aprueban un hito y se libera el dinero
milestone.rejectedLos inversores rechazan un hito
report.deliveredEntregas un informe
referendum.openedSe abre una votación para cerrar la startup
referendum.closedTermina la votación de cierre
free_withdrawal.requestedPides una retirada libre y empieza el plazo de veto
free_withdrawal.paidNadie la veta y el contrato te paga
free_withdrawal.vetoedLos inversores la vetan
test.pingEvento de prueba desde el panel
¿Te ha ayudado?