Skip to content
Developer Docs

Delivery & Retries ​

Rules your receiver endpoint must follow and how Zertiban behaves when something fails.

This section defines how Zertiban delivers webhooks and the requirements the receiver endpoint must meet to guarantee a stable, consistent and resilient integration.

Zertiban implements an at-least-once delivery model: the same event may be sent more than once in retry scenarios or network failures. For this reason, deduplication on the receiver side is mandatory.

Receiver endpoint rules ​

The endpoint that receives webhooks must satisfy the following functional and technical requirements:

  1. Signature verification. Every webhook must be validated using the HMAC-SHA256 mechanism described in Signature Verification. No event with an invalid signature should be processed.
  2. Response time. The endpoint must respond 2xx in under 5 seconds. If processing takes longer, respond 200 OK immediately and continue processing asynchronously in the background.
  3. Event deduplication. Use eventUuid as the idempotency key. Store processed events and discard them if received again to avoid duplicated side effects.
  4. No redirects. The endpoint must not return 3xx responses. Zertiban does not follow redirects under any circumstances: any redirect can cause event loss.
  5. Tolerance to out-of-order delivery. Events may arrive in a different order from the one in which they happened. Use the body timestamp to detect stale events, as described in Ordering and out-of-order delivery.

Asynchronous processing recommended

If your business logic involves expensive operations or depends on external systems (ERP, DB, emails, queues…), apply the following pattern:

  1. Verify the signature.
  2. Persist the event in internal storage.
  3. Respond 200 OK immediately.
  4. Process the event asynchronously via a queue or worker.

This approach improves system resilience and avoids blocking the reception of new events.

Retry policy ​

Zertiban automatically retries webhook delivery when it does not receive a satisfactory response. The policy is defined as follows:

ParameterValue
Total attempts3
Interval between retries0.5 seconds (fixed)
HTTP connection timeout5 seconds
HTTP response timeout5 seconds

Batch reprocessing ​

If all 3 attempts fail, undelivered events are retried via an internal batch process roughly every 1 minute. This mechanism ensures high delivery availability even in case of temporary failures on the receiver endpoint.

Ordering and out-of-order delivery ​

Zertiban does not guarantee that events arrive in the order they happened. Retries and batch reprocessing shift a failed delivery further into the future, so a retried event can land behind an event that was emitted later — a status change retried through the batch process may reach you a minute after the status change that followed it.

The body timestamp is what tells you the real order: it records when the underlying action occurred and it is identical across all retry attempts of the same event. Neither the zb-timestamp header nor the arrival order do, since both reflect delivery time. See The two timestamps for the full comparison.

The comparison decides whether to apply the state, not whether to keep the event. Persist every event you receive: a late arrival is still a fact about your resource, and several event types share the same resource.uuid without carrying a state transition — OPERATION_OPENED and OPERATION_COMPLETED are both about the same operation, and discarding the former because the latter arrived first loses the open, which was never competing with it.

  1. Deduplicate by eventUuid first. If you have already seen it, you are looking at a retry: stop here.
  2. Persist the event, whatever its timestamp says.
  3. Store, per resource uuid, the timestamp of the newest event whose state you have applied.
  4. If the incoming timestamp is newer, apply the state and update the stored value.
  5. If it is older, the state it carries is stale: leave your state untouched, don't roll it back.
  6. If both are equal, they are two different events of the same resource at the same instant — eventUuid has already ruled out a duplicate. Neither is stale, so process both; if their states conflict, resolve it with a rule of your own, because the payload does not order them.

Respond 2xx in every case. An event whose state you skipped has still been received correctly, and any other response will make Zertiban retry it.

Deduplication and ordering are two different guards

eventUuid stops the same event being applied twice, timestamp stops an older event overwriting newer state. A consumer needs both: timestamp is not an idempotency key, since two different events of the same resource can share it.

Troubleshooting: I don't receive notifications ​

If webhooks are not arriving correctly, verify the following:

  1. Endpoint registration. Confirm with Zertiban that the URL is correctly registered, active and associated with the correct businessUuid.
  2. External reachability. The endpoint must be publicly reachable via HTTPS. For external testing you can use tools like reqbin.com or any HTTP client outside your internal network.
  3. Endpoint response. The endpoint must respond only with 2xx codes, in under 5 seconds. 3xx, 4xx or 5xx responses can trigger retries or event loss.
  4. Local development. For local environments, expose the endpoint via HTTPS tunnels, for example with ngrok (ngrok http 3000). The generated public URL can be used as a temporary endpoint in sandbox.
  5. Signature validation. During the initial integration phase, accept all webhooks without blocking, log zb-signature and zb-timestamp, and validate the signature calculation afterwards. This makes it easier to spot discrepancies in your algorithm implementation.

Debug shortcut

Always log eventUuid, eventType, zb-timestamp and the signature verification result. This approach simplifies incident diagnosis and significantly reduces integration time.

Final recommendation ​

A well-designed webhook consumer should prioritize:

  • Idempotency.
  • Low response latency.
  • Asynchronous processing.
  • Tolerance to retries.

This guarantees a robust, scalable integration that is consistent with Zertiban's delivery model.