Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.jelou.ai/llms.txt

Use this file to discover all available pages before exploring further.

You receive POST callbacks from any external service (payment gateways, GitHub, CRMs, etc.), validate the payload, and process the event. Pattern: custom route + POST only + MCP disabled + payload validation.
index.ts
import { define, z } from "@jelou/functions";

export default define({
  name: "webhook-receiver",
  description: "Receives and processes webhooks from external services",
  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 received", {
      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("Payment completed", { id: input.data.id });
        return { acknowledged: true, action: "payment_processed" };
      }
      case "user.created": {
        ctx.log("User created", { id: input.data.id });
        return { acknowledged: true, action: "user_synced" };
      }
      default: {
        ctx.log("Unhandled event", { event: input.event });
        return { acknowledged: true, action: "ignored" };
      }
    }
  },
});

Local testing

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

Why it works this way

  • config.methods: ["POST"] — rejects GET, PUT, etc. Webhooks are always POST.
  • config.mcp: false — there is no point in exposing a webhook as an AI tool.
  • config.path — fixed route that you configure in the external service.
  • The input schema validates the payload structure before it reaches the handler.
This example omits webhook signature verification for brevity. In production, validate the signature (for example, HMAC-SHA256 with the x-webhook-signature header) before processing the event.