Process signup webhooks into a sequenced queue that completes account setup and delivers initial user value without dropped steps or duplicate work.
Validate the incoming webhook before any work begins
Store the raw webhook payload and its signature in an append-only log before you touch any business logic. Recompute the HMAC with your shared secret and reject the request if the signature does not match; this prevents both replay attacks and processing of malformed events from a misconfigured sender. Log the event ID and timestamp so you can later correlate retries with the original delivery attempt.
Return a 2xx status only after the payload passes schema validation and the event type matches one you expect to act on. Any other response keeps the sender retrying, which is the correct behavior when your parser cannot yet understand the payload.
Enqueue idempotent jobs for each provisioning step
Map the validated signup event to a fixed sequence of jobs: create organization record, provision storage bucket, issue initial API key, and seed default configuration. Store each job with a deterministic key derived from the user ID and step name so that duplicate deliveries produce the same outcome. Use a queue that supports at-least-once delivery and record job completion in a separate table before you acknowledge the message.
If a job fails because of a transient error such as a database connection timeout, let the queue retry with exponential backoff while preserving the original event context. For permanent errors such as an invalid region parameter, move the job to a dead-letter queue and emit an alert that includes the exact failure reason and the event ID so an operator can inspect the payload directly.
Sequence steps so first value arrives before optional work
Order the queue so that the minimal set of resources required for the user to perform their first action is created first. For example, create the workspace and grant access before you allocate secondary resources such as custom domains or third-party integrations. Track completion of this minimal set with a single status flag that the front end can poll; once the flag is set, the user can proceed without waiting for the rest of the pipeline.
When a later step depends on an external vendor, wrap the call in a circuit breaker that fails fast after a small number of consecutive errors. The breaker state is stored per vendor rather than per user so that one slow provider does not stall every new signup. Record the breaker trip in the same job log so you can correlate provisioning delays with external incidents.
Reconcile state when webhooks arrive out of order
Design each provisioning handler to read the current state of the user record before applying changes. If the handler sees that a prerequisite step has not completed, re-enqueue the current job with a short delay instead of failing. This pattern tolerates webhooks that arrive before their predecessors without requiring the sender to guarantee order.
Run a periodic reconciliation job that scans for users whose signup event is older than a threshold and whose first-value flag is still false. The job re-fires any missing steps using the same idempotency keys already stored, so duplicate work is avoided. Surface the count of stuck users on an internal dashboard with links to the original webhook payload and the last error for each step.
