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

  1. Confirm credentials.webhook_endpoint_secret matches the current Alby endpoint signing secret.
  2. Re-run Alby setup to capture the latest endpoint secret into the credential.
  3. 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

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.