Skip to content

feat(data): record which event type each webhook processed - #708

Draft
paulvanbrenk wants to merge 1 commit into
fix/stripe-webhook-event-matchingfrom
feat/webhook-event-type
Draft

paulvanbrenk wants to merge 1 commit into
fix/stripe-webhook-event-matchingfrom
feat/webhook-event-type

Conversation

@paulvanbrenk

@paulvanbrenk paulvanbrenk commented Sep 6, 2026 •

Copy link
Copy Markdown
Collaborator

Builds on #707.

What

ProcessedWebhookEvents stored only the provider's event id and a timestamp:

public required string EventId { get; set; }
public DateTimeOffset ProcessedAt { get; set; }

Both webhooks write to that one table — StripeWebhook.cs writes Stripe evt_… ids, StytchWebhook.cs writes Svix msg_… ids — and both formats are opaque. The ledger could say how many events were handled, never which kinds.

Why it matters

That's exactly the gap that let #707's bug hide for months. Four of five Stripe event types were being dropped, and this table recorded nothing that would have revealed it — the rows that existed looked identical to the rows that should have existed.

It's also what makes #707 confirmable in production. The thing to watch for after deploying is invoice.payment_succeeded rows starting to appear, which a bare count can't distinguish from someone signing up.

Details

  • EventType is nullable, capped at 128 to match EventId beside it (repo convention is explicit lengths; the first draft generated nvarchar(max) and was redone).
  • Migrations for both providers, additive and nullable, safe against the existing prod schema.
  • patchnotes-email/prisma/schema.prisma mirrors the same database and is updated to match, or the drift check fails.
  • Stytch splits its type across object_type and action; joined here as user.CREATE so the column reads like Stripe's dotted types.
  • GET /api/admin/webhook-events returns the field. That endpoint exists to answer "did we process that webhook?", which it couldn't do usefully while every row looked the same. OpenAPI spec and generated client regenerated to match.

Rows written before this column exists stay null and can't be backfilled — the id alone doesn't carry the type, and Stripe only serves full event payloads for the last 30 days. The admin endpoint returns them as null rather than omitting them, and there's a test pinning that.

🤖 Generated with Claude Code

@paulvanbrenk
paulvanbrenk force-pushed the feat/webhook-event-type branch 2 times, most recently from e153e9a to 949cb0d Compare September 6, 2026 02:05
@paulvanbrenk
paulvanbrenk force-pushed the feat/webhook-event-type branch 2 times, most recently from 38e371d to 9910928 Compare September 6, 2026 02:48
ProcessedWebhookEvents stored only the provider's event id and a
timestamp. Both webhooks write to it, and both ids are opaque -- evt_...
from Stripe, msg_... from Svix -- so the ledger could say how many
events were handled but never which kinds.

That is precisely the gap that let the previous commit's bug hide. Four
of five Stripe event types were being dropped for months, and the table
recorded nothing that would have shown it: the rows that did exist were
indistinguishable from the rows that should have. It is also what makes
the fix confirmable in production, where the thing to watch for is
invoice.payment_succeeded rows starting to appear rather than a count
going up.

EventType is nullable and capped at 128 like EventId beside it. Stytch
splits its type across object_type and action, joined here so the column
reads the same way as Stripe's dotted types.

Rows written before this column exists stay null. They cannot be
backfilled: the id alone does not carry the type, and Stripe only serves
full event payloads for the last 30 days.

GET /api/admin/webhook-events returns the field too. That endpoint exists to
answer "did we process that webhook?", which it could not do usefully while
every row in it looked the same. The OpenAPI spec and generated client are
regenerated to match.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@paulvanbrenk
paulvanbrenk force-pushed the feat/webhook-event-type branch from 9910928 to 2700cbb Compare September 6, 2026 03:06

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant