Working as designed unless they should have access: growth-api requires role='domain_owner' in the internal org (GROWTH_INTERNAL_CLERK_ORG_ID). Being signed in — even being a domain owner of some customer org — is not enough. Verified behaviour: demo-estate users all 403 here. Fix by granting the person the Domain Owner role in the internal Clerk org.
  1. Check Cal.com’s webhook delivery log for cal-webhook — signature failures return 401 (secret mismatch: CAL_WEBHOOK_SECRET).
  2. Only BOOKING_CREATED creates leads; other triggers are ignored by design.
  3. Custom fields empty on the lead? The event type’s booking questions must have identifiers exactly organisation, role, team_size — a renamed question silently resolves to NULL (insert still succeeds).
Work through the chain in order:
  1. Clerk dashboard → the clerk-to-resend endpoint’s delivery log (it is a separate endpoint from clerk-webhook, with its own signing secret — check you are looking at the right one).
  2. Resend dashboard → Events: did codex:user_signed_up / codex:subscription_plan_changed arrive?
  3. Resend → Automations: is an Automation active and triggering on that event name? Remember: no application code schedules sends — if the event arrived and nothing sent, the Automation is the problem. codex:subscription_cancelled now has an automation (added 2026-08-13, “Sorry to see you go”) — see Resend environment → Subscription Cancelled.
  4. Validate templates and variable bindings without sending: pnpm test:emails (render mode).
Known, unconfirmed finding: the code sends a lowercase first_name payload key, templates expect RECIPIENT_FIRST_NAME — see Resend environment → Gap 1 for the exact probe to confirm.
resend-webhook isn’t receiving events: check Resend’s webhook delivery log and the RESEND_WEBHOOK_SIGNING_SECRET. Because handlers are lazy upserts, replaying missed events from the Resend dashboard is safe and idempotent (duplicates only add benign email_events rows).
Should be impossible by construction — clerk-to-resend is isolated from clerk-webhook and never writes codex tables. If both broke at once, the shared ingredient is the CLERK_SECRET_KEY function secret or the Supabase platform itself, not the pipeline code.

Quick data checks