Developer documentation / 03

Webhooks without repeated commitments.

Plan verified event ingestion, durable receipt, duplicate protection, ordered state transitions, and a clear replay process.

Verify the provider before accepting the event

Follow the upstream service’s exact signature and timestamp rules. Do not invent a common header and assume it works across every marketplace. Some protocols require the raw body, so verification may need to happen before parsing or reserialization. Keep secrets out of client-visible code and logs.

Record receipt before processing effects

A proposed durable inbox stores a verified event before acknowledging successful receipt. A separate worker can apply the business effect. If storage fails, the system should not claim a durable receipt it has not achieved. Track received, processing, completed, and review-needed states separately.

Scope identity to the provider and account

A stable event ID should be interpreted within the scope defined by the provider. Guard uniqueness atomically rather than using an unprotected check-then-write sequence. Record the related command or resource ID without confusing it with delivery identity.

Illustrative event envelope
{
  "example": true,
  "schema_version": "1.0",
  "event_id": "evt_demo_001",
  "source": "example-provider",
  "event_type": "command.acknowledged",
  "occurred_at": "2026-09-15T12:00:01Z",
  "command_id": "cmd_demo_001",
  "provider_reference": "provider_demo_001",
  "state": "acknowledged"
}
Illustrative local fixture · no live request or transaction

Protect transitions from late or duplicate messages

An acknowledged event arriving after completion should not move a record backwards. Use provider-supported versions, sequences, or authoritative resource lookups where needed. Do not rely only on arrival order. Keep a replay and reconciliation route for events that cannot be applied with confidence.

Separate event delivery from business effects

A worker can crash after an external action but before recording success locally. Give the action its own identity and recovery path. A transactional outbox can record intended work alongside a local state change, while the destination’s documented idempotency mechanism can protect repeated delivery where supported.

Test and monitor the failure paths

Include duplicate delivery, invalid signatures, storage failure, unknown schema versions, concurrency, and a crash around an external action. Monitor queue age and unresolved effects, not just request counts. Read the webhook idempotency field note for a full walkthrough. Stripe’s official webhook guide is a concrete reference for its own delivery and verification semantics.