Skip to content

docs: outbound webhooks design (#562) - #569

Open
mahesh-sangawar wants to merge 2 commits into
mainfrom
docs/webhooks-design
Open

mahesh-sangawar wants to merge 2 commits into
mainfrom
docs/webhooks-design

Conversation

@mahesh-sangawar

Copy link
Copy Markdown
Contributor

Design document for #562, following the repo's docs/superpowers/specs/ convention.

No code. This records the decisions and, more importantly, why the cheaper alternatives were rejected — so a later reader doesn't assume they were overlooked.

The shape of it

The expensive half already exists. outbox_events is a transactional outbox carrying 18 real domain events, drained by an in-process publisher with FOR UPDATE SKIP LOCKED. Webhooks consume that stream rather than instrumenting anything new, which is what makes this contained rather than the multi-week build it first looked like.

Two loops and one new table, running in-process beside the existing publisher:

outbox_events ──(dispatcher: own cursor)──▶ webhook_deliveries ──(worker)──▶ merchant endpoint
                                                    ▲
                                      webhook_subscriptions

Decisions recorded

  1. Notify-and-fetch payloads — no customer PII to merchant-supplied URLs, no stale retries
  2. Strict URL validation, re-resolved at delivery — registration-only checking is defeated by DNS rebinding
  3. Retry → dead-letter → auto-disable — a webhook that silently stopped is worse than one that says so
  4. Every plan — the events already exist; gating is friction
  5. Read-only API widened to Starter — decision 1 is incoherent without it

Two things worth a reviewer's attention

Decision 5 is a plan-entitlement change, not a webhook change. It alters what Studio sells and needs matching pricing copy on both public surfaces. It wants its own issue and must land before or with this.

Deliberately not extending the existing outbox publisher. Fanning out inside ProcessBatch would give exactly-once for free, but welds unbounded-latency network work into the watermark publisher — the component outbox/models.go documents (#336) as subtle to recover from. A merchant's dead endpoint must never stall internal bookkeeping. Idempotency on (outbox_event_id, subscription_id) recovers the guarantee.

The in-process placement is called out as reversible: the fan-out table already decouples dispatch from delivery, so extracting the worker later is a deployment change, not a redesign.

Also flagged

pricing-surfaces-truth.spec.ts currently fails on any pricing surface mentioning webhooks. When this ships that guard should be narrowed, not deleted, so the custom-code-injection half stays pinned.

Custom code injection remains explicitly out of scope.

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