create_webhook
Register a webhook to receive real-time notifications when QR codes are scanned. Returns a one-time HMAC-SHA256 secret for secure verification of webhook signatures.
Instructions
Register a webhook endpoint to receive real-time notifications when QR codes are scanned. Returns an HMAC-SHA256 secret for verifying webhook signatures — store it securely, it is only shown once.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | The endpoint URL that will receive POST requests with scan event data. | |
| events | No | Events to subscribe to. Currently supported: "qr.scanned". |
Implementation Reference
- packages/mcp/src/tools.ts:659-675 (handler)MCP tool handler for 'create_webhook' - calls the HTTP API's POST /api/webhooks endpoint with url and events parameters.
create_webhook: { description: "Register a webhook endpoint to receive real-time notifications when QR codes are scanned. Returns an HMAC-SHA256 secret for verifying webhook signatures — store it securely, it is only shown once.", inputSchema: z.object({ url: z .string() .url() .describe("The endpoint URL that will receive POST requests with scan event data."), events: z .array(z.enum(["qr.scanned"])) .default(["qr.scanned"]) .describe('Events to subscribe to. Currently supported: "qr.scanned".'), }), handler: async (input: { url: string; events?: string[] }) => { return apiRequest("/api/webhooks", { method: "POST", body: input }); }, }, - packages/mcp/src/tools.ts:662-671 (schema)Zod input schema for the 'create_webhook' MCP tool - accepts a url (string, url format) and optional events array (defaulting to ['qr.scanned']).
inputSchema: z.object({ url: z .string() .url() .describe("The endpoint URL that will receive POST requests with scan event data."), events: z .array(z.enum(["qr.scanned"])) .default(["qr.scanned"]) .describe('Events to subscribe to. Currently supported: "qr.scanned".'), }), - packages/mcp/src/server.ts:21-54 (registration)Generic tool registration loop that registers all tools (including create_webhook) in the MCP server using the McpServer SDK.
for (const [name, tool] of Object.entries(tools)) { server.tool( name, tool.description, tool.inputSchema.shape, async (input: Record<string, unknown>) => { try { const result = await tool.handler(input as any); return { content: [ { type: "text" as const, text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { const message = error instanceof Error ? error.message : String(error); return { content: [ { type: "text" as const, text: JSON.stringify({ error: message, hint: "Check the input parameters and try again. Use list_qr_codes to verify available QR codes.", }), }, ], isError: true, }; } } ); } - packages/mcp/src/api-client.ts:13-40 (helper)Helper function apiRequest used by the handler to make HTTP POST requests to the webhook API endpoint.
export async function apiRequest(path: string, options: RequestOptions = {}) { const { method = "GET", body, query } = options; let url = `${BASE_URL}${path}`; if (query) { const params = new URLSearchParams(); for (const [key, value] of Object.entries(query)) { params.set(key, String(value)); } url += `?${params.toString()}`; } const headers: Record<string, string> = { "X-API-Key": API_KEY, }; if (body) { headers["Content-Type"] = "application/json"; } const res = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : undefined, }); return res.json(); } - Actual service-level implementation that creates a webhook - checks plan quota, generates a secret via nanoid, inserts into the database, and returns the created webhook.
export function createWebhook( url: string, events: string[], apiKeyId: number, plan: Plan = "free" ) { // Check plan quota const limits = PLAN_LIMITS[plan]; if (limits.maxWebhooks !== Infinity) { const [{ total }] = db .select({ total: count() }) .from(webhooks) .where(eq(webhooks.apiKeyId, apiKeyId)) .all(); if (total >= limits.maxWebhooks) { return { error: "WEBHOOK_LIMIT_REACHED" as const, limit: limits.maxWebhooks }; } } const secret = nanoid(32); const inserted = db .insert(webhooks) .values({ apiKeyId, url, secret, events: JSON.stringify(events), isActive: true, }) .returning() .get(); return { id: inserted.id, url: inserted.url, secret: inserted.secret, events: JSON.parse(inserted.events) as string[], is_active: inserted.isActive, created_at: inserted.createdAt, }; }