Concepts

Webhooks

stubkit exposes three webhook endpoints on api.stubkit.com — one per provider. Each request is signature-verified, durably archived, queued for asynchronous processing, and acknowledged in under 50 milliseconds.

The ingress pipeline

  1. The endpoint receives the request.
  2. It verifies the provider signature (JWS for Apple, OpenID Connect for Google, HMAC for Stripe).
  3. The raw body is written to an immutable archive so we can replay any event for up to two years after it landed.
  4. A normalized message is pushed onto an asynchronous processing queue.
  5. The queue worker reads the message, calls the receipt validator (which can cross-check with the provider's authoritative API), and applies the event to the per-user state engine.

Why archive before queue

The archive write is the durability anchor. If a downstream worker crashes after the queue message is acknowledged, the raw payload is still there and can be replayed. Apple, Google, and Stripe retry webhook deliveries automatically, so double-processing must be idempotent — which it is, via the idempotency key.

Why queue-then-process instead of synchronous

Provider dashboards penalize slow webhook responses by rate- limiting retries. A sub-50-millisecond acknowledgment keeps us in the good-neighbor tier. The actual receipt validation can take hundreds of milliseconds — Apple Server API round-trip, Google OAuth token exchange — so it runs asynchronously with at-least-once semantics.