Delivery & de-duplication
InsurGrid's delivery is at-least-once, not exactly-once. That single fact shapes how you should write every webhook handler: it must be safe to run twice on the same event.
New term — at-least-once delivery: a guarantee that an event will be delivered one time or more — never zero times — but occasionally more than once. The tradeoff for "we'll keep trying until it lands" is that your handler has to tolerate the same event arriving twice.
Why duplicates happen
A duplicate isn't a bug on InsurGrid's side — it's the normal cost of guaranteeing nothing gets silently dropped. A common case: your endpoint finishes processing and is about to respond, but the response is slow to arrive or gets lost in transit. From InsurGrid's side, that looks identical to a failure, so the event is retried — even though you already handled it.
Retries, backoff, and replay
If your endpoint doesn't return a 2xx status, InsurGrid retries the delivery automatically, spacing attempts out over time rather than firing them all at once. Every attempt — successful or not — is recorded in a delivery log you can review in the API section, and a failed delivery can be replayed on demand once your endpoint is fixed.
In plain terms: you don't need to build your own retry or delivery-tracking system. InsurGrid keeps trying on your behalf and gives you visibility into what was sent and when. What you're responsible for is making sure a repeated delivery doesn't cause a repeated effect on your side.
Make your handler idempotent
New term — idempotent: an operation that produces the same end result no matter how many times it runs. Processing the same webhook once or three times should leave your system in the same state either way.
The standard pattern: derive a stable de-dupe key for the event, record every key you've successfully processed, and skip anything you've already seen. Which key to use depends on the event (see the next section).
// server (sketch)
const crypto = require("crypto");
function dedupeKey(event) {
// form-submission carries its own id; policy-data uses a content hash
return event.data.submission_id
?? crypto.createHash("sha256").update(JSON.stringify(event.data)).digest("hex");
}
function handle(event) {
const key = dedupeKey(event);
if (alreadyProcessed(key)) return; // skip duplicate
markProcessed(key);
// …save the data, trigger downstream work…
}alreadyProcessed / already_processed can be as simple as a unique constraint on the id column in your database — an insert that violates the constraint is your duplicate check.
What id to de-dupe on
Form submissions (form-submission)
The submission_id in the payload body — it identifies the underlying submission no matter how many times it's delivered. Use this directly.
Policy data (client-submission)
The payload has no dedicated id field. De-dupe on a hash of the payload contents (data): compute a SHA-256 of the JSON and treat a repeat hash as a duplicate.
If your endpoint receives a delivery-id header from your webhook setup, you can use that instead — confirm the exact header for your account during onboarding. When in doubt, the content hash above always works.
Ordering is not guaranteed
Don't assume events arrive in the order they happened. If you subscribe to multiple topics for related data (for example, a submission event and a per-file upload event), one may arrive before the other. Design your handler to tolerate out-of-order arrival — for example, upsert by id rather than assuming "this is the first thing I've seen for this record."
Related
Idempotency keys — the equivalent safety net for the writes you send to InsurGrid.
