When to use
Useconfig: { public: true } when the caller can’t send X-Jelou-Token:
- Webhooks from external services (Stripe, GitHub, Twilio)
- Callbacks from payment gateways
- Public APIs accessible from the browser
config: { public: true } when the caller can’t send X-Jelou-Token:
define() modeexport default define({
description: "Stripe webhook",
input: z.object({ type: z.string(), data: z.object({ id: z.string() }) }),
config: { public: true, methods: ["POST"], mcp: false },
handler: async (input, ctx) => {
ctx.log("Webhook received", { type: input.type });
return { received: true };
},
});
app() mode — per-toolexport default app({
tools: {
webhook: define({
config: { public: true, mcp: false },
handler: async (input) => ({ ok: true }),
}),
admin: define({
// protected by default — requires X-Jelou-Token
handler: async (input) => ({ ok: true }),
}),
},
});
export default app({
config: { public: true },
tools: {
publicTool: define({ handler: async () => ({}) }),
protectedTool: define({
config: { public: false }, // override: requires token
handler: async () => ({}),
}),
},
});
handler: async (input, ctx, request) => {
const signature = request.headers.get("x-webhook-signature");
const secret = ctx.env.get("WEBHOOK_SECRET");
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw", encoder.encode(secret),
{ name: "HMAC", hash: "SHA-256" }, false, ["verify"],
);
const body = await request.clone().text();
const sigBytes = Uint8Array.from(atob(signature!), (c) => c.charCodeAt(0));
const valid = await crypto.subtle.verify("HMAC", key, sigBytes, encoder.encode(body));
if (!valid) return { error: "invalid_signature" };
return { acknowledged: true };
}
Was this page helpful?