calcom/cal.diy · error · HttpCode
Invoice not found
Error message
Invoice not found
What it means
Thrown when parseInvoice returns null, which happens when Svix Webhook.verify throws inside parseInvoice - i.e. signature verification failed. The webhook_endpoint_secret stored on the credential does not match the secret Alby used to sign the event. Returns HTTP 204.
Source
Thrown at packages/app-store/alby/api/webhook.ts:87
},
},
},
});
if (!payment) throw new HttpCode({ statusCode: 204, message: "Payment not found" });
const key = payment.booking?.user?.credentials?.[0].key;
if (!key) throw new HttpCode({ statusCode: 204, message: "Credentials not found" });
const parseCredentials = albyCredentialKeysSchema.safeParse(key);
if (!parseCredentials.success) {
console.error(parseCredentials.error);
throw new HttpCode({ statusCode: 500, message: "Credentials not valid" });
}
const credentials = parseCredentials.data;
const albyInvoice = await parseInvoice(bodyAsString, parsedHeaders, credentials.webhook_endpoint_secret);
if (!albyInvoice) throw new HttpCode({ statusCode: 204, message: "Invoice not found" });
if (albyInvoice.amount !== payment.amount) {
throw new HttpCode({ statusCode: 400, message: "invoice amount does not match payment amount" });
}
const traceContext = distributedTracing.createTrace("alby_webhook", {
meta: { paymentId: payment.id, bookingId: payment.bookingId },
});
return await handlePaymentSuccess({
paymentId: payment.id,
bookingId: payment.bookingId,
appSlug: "alby",
traceContext,
});
} catch (_err) {
const err = getServerErrorFromUnknown(_err);
console.error(`Webhook Error: ${err.message}`);
return res.status(err.statusCode).send({
message: err.message,View on GitHub (pinned to 176037d0af)
Solutions
- Confirm credentials.webhook_endpoint_secret matches the current Alby endpoint signing secret.
- Re-run Alby setup to capture the latest endpoint secret into the credential.
- Only treat the 204 as a benign 'duplicate webhook' after ruling out a secret mismatch.
Defensive patterns
Strategy: validation
Validate before calling
const ok = parseInvoice(bodyAsString, parsedHeaders, credentials.webhook_endpoint_secret);
if (!ok) {
// the stored secret likely differs from Alby's current signing secret
} Type guard
const isInvoice = (v: unknown): v is { amount: number } =>
typeof v === 'object' && v !== null && typeof (v as any).amount === 'number'; Prevention
- Re-run Alby setup after rotating the Alby endpoint to refresh the secret.
- Store the webhook_endpoint_secret exactly as Alby provides it.
- Use one credential per Alby endpoint to avoid secret cross-talk.
When it happens
Trigger: webhook_endpoint_secret was rotated in Alby but not refreshed in cal.com; secret copied incorrectly; replay/tampering that breaks the signature; Alby sending events for a different webhook endpoint than the one whose secret is stored.
Common situations: Re-installing Alby creates a new endpoint with a new secret while the old credential key is retained; copy-paste error in the secret; multiple Alby endpoints sharing one credential.
Related errors
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/2d7c1ed8f619dbce.
Report an issue: GitHub.