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
- The endpoint receives the request.
- It verifies the provider signature (JWS for Apple, OpenID Connect for Google, HMAC for Stripe).
- The raw body is written to an immutable archive so we can replay any event for up to two years after it landed.
- A normalized message is pushed onto an asynchronous processing queue.
- 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.