Errors & Retry Semantics
What each response status means and when to retry.
The Custom E-Com webhook endpoint returns a small, well-defined set of HTTP statuses. Use them as the source of truth for whether to retry — do not parse response bodies for retry decisions.
Status codes
200 OK — accepted
Two meaningful body shapes:
| Body | Meaning |
|---|---|
{"ok":true, ...} | Event was verified, validated, and recorded synchronously. If you have an active flow on this event, flow triggering runs asynchronously afterward (the response carries no flow id). |
{"deduplicated":true} | Same (webhookId, x-vyg-event-id) was already accepted within the last 24h. |
Retry? No — your delivery is done.
400 Bad Request — your request is malformed
Returned for any of:
- Missing
webhookIdin the URL path. - Missing
x-vyg-signatureheader or empty body. - Body is not valid JSON.
- Body is missing the
typeorevent_idfield.
Retry? No. Fix the request before re-sending. If you do re-send after
fixing, use a new x-vyg-event-id so the original (broken) attempt
isn't deduplicated against the fix.
401 Unauthorized — signature problem
Returned when:
- The brand has no webhook secret configured (rotate it from Settings → Providers → Custom E-Com).
- The
x-vyg-signaturedoes not match the HMAC-SHA256 of the body keyed with the brand's secret.
Retry? No. Verify your signing implementation against the
HMAC Verification examples — the same
fixture body and secret must produce the documented hex string. The most
common cause is signing one representation of the body and sending
another (re-stringification, whitespace, trailing newlines from echo).
404 Not Found — integration or event type not found
Returned when the webhookId from the URL path matches no enabled
integration, or when no enabled event definition matches the type you
sent ({"ok":false,"reason":"unknown_event_type", ...}). This usually
means:
- Wrong id in the URL.
- The integration was disabled on the LiveRecover side.
- The
typeslug is not registered (or its definition is disabled). Send only topics you have registered; an unregistered topic is never recorded and never triggers a flow.
Retry? No. Confirm the id with your account contact, or register / re-enable the event definition, then re-send.
500 Internal Server Error — transient
Anything thrown out of the handler that isn't one of the above.
Retry? Yes — with backoff.
Recommended retry policy
For 5xx only:
- Jittered exponential backoff.
- Suggested schedule: 1s → 5s → 30s → 5m → 30m → 1h → ... up to 24h.
- Always retry with the same
x-vyg-event-id. Idempotency is persisted on(webhookId, x-vyg-event-id), so reusing the id is safe at any horizon — a successful original delivery short-circuits with200 {"deduplicated":true}and the event is recorded only once. - Stop retrying once you receive any
2xx(the event is accepted or was already accepted) or a non-5xxerror.
Do not retry 4xx. Doing so consumes your sending budget without
changing the outcome and can mask real bugs (e.g., a clock-skewed signing
machine that produces 401 for every request).
Idempotency window and retries
LiveRecover deduplicates accepted events on (webhookId, x-vyg-event-id)
indefinitely — once an event with a given id is accepted, replaying it
with the same id always short-circuits to 200 {"deduplicated":true} and
the event is never recorded twice, no matter how much time has passed.
What this means for integrators:
- Reuse the same
x-vyg-event-idfor every retry of the same delivery — at any horizon, hours or weeks later. The replay is deduped. - For a brand-new event, mint a new
x-vyg-event-id. Reusing an old id will short-circuit to200 {"deduplicated":true}and the event will not be recorded a second time. - The 24h ceiling on retry budget above is just a sending-cost guideline, not an idempotency window — exceeding it does not risk double-processing.