{"record":{"id":"43b020068ec472e3","repo":"calcom/cal.diy","slug":"bad-request","errorCode":null,"errorMessage":"Bad Request","messagePattern":"Bad Request","errorType":"http","errorClass":"HttpCode","httpStatus":400,"severity":"warning","filePath":"packages/app-store/alby/api/webhook.ts","lineNumber":33,"sourceCode":"  api: {\n    bodyParser: false,\n  },\n};\n\nexport default async function handler(req: NextApiRequest, res: NextApiResponse) {\n  try {\n    if (req.method !== \"POST\") {\n      throw new HttpCode({ statusCode: 405, message: \"Method Not Allowed\" });\n    }\n\n    const bodyRaw = await getRawBody(req);\n    const headers = req.headers;\n    const bodyAsString = bodyRaw.toString();\n\n    const parseHeaders = webhookHeadersSchema.safeParse(headers);\n    if (!parseHeaders.success) {\n      console.error(parseHeaders.error);\n      throw new HttpCode({ statusCode: 400, message: \"Bad Request\" });\n    }\n\n    const { data: parsedHeaders } = parseHeaders;\n\n    const parse = eventSchema.safeParse(JSON.parse(bodyAsString));\n    if (!parse.success) {\n      console.error(parse.error);\n      throw new HttpCode({ statusCode: 400, message: \"Bad Request\" });\n    }\n\n    const { data: parsedPayload } = parse;\n\n    if (parsedPayload.metadata?.payer_data?.appId !== \"cal.com\") {\n      throw new HttpCode({ statusCode: 204, message: \"Payment not for cal.com\" });\n    }\n\n    const payment = await prisma.payment.findFirst({\n      where: {","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/packages/app-store/alby/api/webhook.ts#L15-L51","documentation":"Thrown when webhookHeadersSchema.safeParse(headers) fails - the request is missing one or more of the required Svix headers 'svix-id', 'svix-timestamp', 'svix-signature' used to authenticate webhook delivery. The parse error is console.error'd before the throw.","triggerScenarios":"A request to the webhook not originating from Svix (curl without svix headers); a proxy/load-balancer stripping custom headers; Svix relay misconfiguration; a replay tool omitting headers.","commonSituations":"Local testing without the Svix CLI; corporate proxy filtering svix-* headers; Alby webhook endpoint pointed at a URL behind a CDN that drops custom headers.","solutions":["Ensure requests come from Svix/Alby carrying all three svix-* headers.","Check that no proxy/CDN strips svix-id, svix-timestamp, or svix-signature.","Use 'svix listen' (Svix CLI) for local testing so headers are populated correctly."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const required = ['svix-id', 'svix-timestamp', 'svix-signature'];\nconst missing = required.filter((h) => !req.headers[h]);\nif (missing.length) {\n  // reject early with a clearer message than 'Bad Request'\n}","typeGuard":"const hasSvixHeaders = (h: Record<string, unknown>): h is Record<string, string> =>\n  typeof h['svix-id'] === 'string' &&\n  typeof h['svix-timestamp'] === 'string' &&\n  typeof h['svix-signature'] === 'string';","tryCatchPattern":null,"preventionTips":["Ensure no proxy strips svix-* headers.","Use the Svix CLI ('svix listen') for local testing.","Confirm the Alby webhook endpoint URL is correct."],"tags":["alby","webhook","svix","headers","zod","validation"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}