Software Engineering
Webhook retries are normal; duplicate invoices are a design bug
How Mavri verifies signed store webhooks and uses a stable store-and-order key to make repeated delivery safe for invoice creation.
By Raktim Ranjit · Published · 5 min read
A store sends an order event. Mavri creates an invoice. Then the sender retries because its first request timed out before it received the response. If the second request creates another invoice, the integration has turned a routine network retry into duplicate work for the business.
Assume a sender can retry
A webhook sender cannot know with certainty whether a request failed before it reached the receiver, or whether the receiver completed the work and the response was lost. Retrying is a sensible way to recover from that uncertainty. Receivers should treat repeated delivery as part of the normal protocol, not as an edge case that will never happen.
The first safeguard is authenticity. Mavri's store webhook checks an HMAC-SHA256 signature over the request body using the connection's secret. That shows the payload came from a party that knows the secret and was not changed in transit. It does not prove that an event is new, valid for every business rule or safe to process twice.
Give the business event a stable identity
Mavri records the external order ID in the context of the store connection and uses that pair as the idempotency boundary. The first valid delivery can create the order and its invoice. A later delivery for the same store and external order is recognized as the same event instead of creating a second invoice.
The store connection matters. Two shops may both have an order numbered 1007; the ID is only unique inside its source. The idempotency key needs enough context to distinguish those events without treating a retry from one shop as new work.
Make the record and side effect agree
The idempotency check only helps if it is atomic with the work it protects. If the app records the key and crashes before creating the invoice, a retry must be able to finish the event. If it creates the invoice and crashes before recording the key, a retry must not create it again. That is why the database boundary matters: the order identity and the resulting business records must be committed together, or the system needs a recoverable state machine for the gap.
A good integration also keeps a clear response path. The sender needs to know which deliveries were accepted, which were rejected for a bad signature, and which need retrying. Logging the store connection and external order ID makes support work much easier than logging an unstructured blob of customer data.
What this does not mean
Idempotency does not make every order correct. A signed, new event can still contain an invalid address or a product that needs review. The owner should test mappings, taxes, discounts and partial refunds before automating a live workflow. A webhook also does not automatically email an invoice or collect payment; Mavri creates and tracks documents, while the business chooses how to send and collect.
A short checklist before connecting a store
Use a signing secret unique to the connection. Keep the event key stable across retries. Test a duplicate delivery deliberately and confirm only one invoice appears. Test a malformed signature and confirm the request is rejected. Finally, make sure the operator can trace an order from the store ID to the Mavri document without exposing unnecessary customer data in logs.
The useful question is not whether a webhook can fail. It is whether the billing record stays understandable when it does. That is the standard I use for Mavri's integrations.
The invoice each order creates keeps a snapshot of the details used to make it, and the whole server is one container on one origin. The full write-up of the product is in the Mavri case study; the Mavri website has the install guide and docs.
References
Author
Raktim Ranjit is a software engineer and the founder of NodeDR Infotech. He builds and maintains the software described here.