Signed webhook delivery
Verify the exact message, acknowledge durable receipt and separate transport retries from business processing.
On this page
Before you start
- A provider signing profile
- A durable intake store
- Event identity and replay policy
Verify, persist, acknowledge, then process
The sender response window ends after durable intake. Business processing follows its own recoverable lifecycle.
Scroll across the diagram to see all four systems.
Signed delivery
Transmit the exact body with the provider’s signature metadata.
Verify message
Check the covered bytes, configured algorithm and replay controls.
Persist envelope
Store the verified event with its source and connection identity.
Confirm durable receipt
Acknowledge that the event survives a receiver restart.
Return success
Complete delivery acknowledgement without waiting for every destination.
Process permitted action
Validate business rules, deduplicate the action and record completion.
Verify the provider’s exact signing contract
A signature is meaningful only when the receiver and sender agree on the covered components, canonicalization, algorithm and key. Preserve the raw request bytes until verification completes. Parsing JSON and serializing it again can change whitespace or key order and invalidate a correct signature.
GitHub documents HMAC SHA 256 over the payload and a constant time comparison for its X-Hub-Signature-256 header. Other providers use different profiles. RFC 9421 defines a general HTTP message signature framework, but its presence does not mean every webhook vendor implements it.
import hashlib
import hmac
def valid_signature(raw_body: bytes, secret: bytes, supplied: str) -> bool:
expected = "sha256=" + hmac.new(
secret, raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected.encode("ascii"), supplied.encode("utf-8"))Separate message authenticity from replay prevention
A valid signature proves the covered content under the configured key; it does not establish that the delivery is new. If the signing profile covers a timestamp or nonce, enforce its documented freshness and replay rules. Do not invent a timestamp header for a provider that does not send one.
Preserve the source, connection and event identity as a deduplication boundary. CloudEvents defines uniqueness in the combination of source and id. For a vendor payload, use that vendor’s documented identity properties rather than assuming any field named eventId is globally unique.
| Check | Protects against | Important boundary |
|---|---|---|
| Exact signature verification | Altered or unauthenticated content | Apply the actual provider profile. |
| Timestamp or nonce policy | Replay within the signing protocol | Only when covered and supported by that protocol. |
| Event deduplication | Repeated event receipt | Namespace identity by source and connection. |
| Business action identity | Repeated downstream effects | An event can contain more than one permitted action. |
| Current permission evaluation | Acting after authority changes | Valid transport does not authorize the business action. |
Acknowledge durable intake, not completed work
Keep the receiver small: authenticate the delivery, validate the envelope and durably store it. Return a success response only after the intake contract has been satisfied. If persistence fails, a success response can cause the sender to stop retrying while the event is lost.
The worker can then enrich data, apply policy and call destinations outside the sender response window. Track receipt, processing and business completion separately so operators can see an event that was acknowledged but remains unresolved.
Plan for duplicates and changed order
Delivery order is not automatically business order. A qualification event may arrive after a later consent withdrawal. Prefer a source revision or authoritative reread when the state transition requires current context; do not overwrite a newer state solely because an older event arrived later.
A replay should retain the original source event identity while creating a separate processing attempt. Correcting the mapping must not make an already completed destination action look new.
Give failed work a route back to completion
Record why intake or processing failed and assign an owner. A dead letter queue needs an inspection, correction and replay procedure, not just a retention setting. Protect payload access and apply retention appropriate to the included customer data.
Test a tampered body, missing signature, old signed timestamp where supported, duplicate delivery, receiver crash after persistence and destination failure after acknowledgement. Rotate signing keys under the provider’s supported overlap behavior.
Inspect webhook intake policy
Evaluate verified signature metadata, freshness and durable intake. Signature verification itself remains the provider specific cryptographic step described above.
/api/v1/webhooks/crmExpected outcome
Delivery accepted
The verified envelope is durably received. Business processing has a separate completion state.
{
"receipt_id": "receipt_240",
"state": "received"
}Content-Type: application/jsonDecision path
- Verify exact message
- Evaluate replay controls
- Persist the event envelope
- Acknowledge receipt
Input schema 4 fields
| Field | Type | Meaning |
|---|---|---|
signature_validRequired | boolean | Result of exact provider signature verification. |
freshness_validRequired | boolean | Result of the configured replay policy where supported. |
already_receivedRequired | boolean | Whether this source event is already durably recorded. |
inbox_availableRequired | boolean | Whether durable intake can commit. |
Standards and documentation
Explore the protocol specifications and platform guidance for this topic.
Bring the contract into your operating environment.
Discuss an implementation