Skip to main content
Recibes callbacks POST de cualquier servicio externo (pasarelas de pago, GitHub, CRMs, etc.), validas el payload y procesas el evento. Patrón: ruta personalizada + solo POST + MCP desactivado + validación de payload.
index.ts
import { define, z } from "@jelou/functions";

export default define({
  name: "webhook-receiver",
  description: "Recibe y procesa webhooks de servicios externos",
  input: z.object({
    event: z.string(),
    data: z.object({
      id: z.string(),
      status: z.string(),
      metadata: z.record(z.unknown()).optional(),
    }),
  }),
  config: {
    path: "/webhooks/events",
    methods: ["POST"],
    mcp: false,
  },
  handler: async (input, ctx) => {
    ctx.log("Webhook recibido", {
      event: input.event,
      id: input.data.id,
      requestId: ctx.requestId,
    });

    const secret = ctx.env.get("WEBHOOK_SECRET");

    switch (input.event) {
      case "payment.completed": {
        ctx.log("Pago completado", { id: input.data.id });
        return { acknowledged: true, action: "payment_processed" };
      }
      case "user.created": {
        ctx.log("Usuario creado", { id: input.data.id });
        return { acknowledged: true, action: "user_synced" };
      }
      default: {
        ctx.log("Evento no manejado", { event: input.event });
        return { acknowledged: true, action: "ignored" };
      }
    }
  },
});

Prueba local

curl -X POST http://localhost:3000/webhooks/events \
  -H "Content-Type: application/json" \
  -d '{"event": "payment.completed", "data": {"id": "pay_123", "status": "success"}}'

Por qué funciona así

  • config.methods: ["POST"] — rechaza GET, PUT, etc. Los webhooks siempre son POST.
  • config.mcp: false — no tiene sentido exponer un webhook como herramienta de IA.
  • config.path — ruta fija que configuras en el servicio externo.
  • El esquema input valida la estructura del payload antes de que llegue al handler.
Este ejemplo omite la verificación de firma del webhook por brevedad. En producción, valida la firma (por ejemplo, HMAC-SHA256 con el header x-webhook-signature) antes de procesar el evento.