> ## Documentation Index
> Fetch the complete documentation index at: https://docs.immoteur.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook delivery, retries, and idempotency

> Handle webhook headers, delivery outcomes, retries, and duplicate events reliably

A `2xx` response acknowledges a webhook delivery. A non-`2xx` response or transport failure does not acknowledge it.

<Warning>
  If your endpoint restricts source IPs, allowlist the following Immoteur egress
  IPs before testing:{" "}

  `51.38.208.80` and `54.37.96.165`{/* immoteur-parameter:end */}.
</Warning>

## Receive a webhook safely

Keep the HTTP handler small. Do not run slow business logic, enrichment, or downstream API calls before acknowledging the request.

```mermaid theme={null}
sequenceDiagram
  participant I as Immoteur
  participant R as Your HTTP receiver
  participant Q as Your queue
  participant W as Your worker
  I->>R: POST payload and delivery headers
  R->>Q: Persist or enqueue the event
  R-->>I: 204 No Content
  Q->>W: Process queued event
```

1. Read `X-Immoteur-Event-Id` and persist or durably enqueue the event before acknowledging it.
2. Return `204 No Content` or another `2xx` response as soon as that work is safe.
3. Process the event in your own worker or queue after the HTTP response. The queue and worker are your integration architecture, not an Immoteur service.

## Timeouts

Immoteur currently applies a {/* immoteur-parameter:start {{immoteur.webhook.delivery.connection_timeout}} */}5 seconds{/* immoteur-parameter:end */} connection timeout and a {/* immoteur-parameter:start {{immoteur.webhook.delivery.request_timeout}} */}5 seconds{/* immoteur-parameter:end */} request timeout. A receiver that waits for slow work can time out before it acknowledges the delivery. Persist or enqueue first, then return a successful response quickly.

## Read the delivery headers

| Header                   | Scope             | Stability                                               | Receiver action                                                        |
| ------------------------ | ----------------- | ------------------------------------------------------- | ---------------------------------------------------------------------- |
| `User-Agent`             | Every delivery    | `ImmoteurWebhook/1` identifies Immoteur webhook traffic | Use it only as delivery context; it is not an authentication mechanism |
| `X-Immoteur-Service-Id`  | Every delivery    | Identifies the configured Immoteur service              | Store it with the event when one receiver handles multiple services    |
| `X-Immoteur-Event-Id`    | Event correlation | Stable across retries of a classified notification      | Use it as the classified-notification idempotency key                  |
| `X-Immoteur-Delivery-Id` | HTTP attempt      | Changes for every delivery attempt                      | Log it for diagnostics; do not use it as an idempotency key            |
| `X-Immoteur-Timestamp`   | HTTP attempt      | Unix timestamp in seconds                               | Record it when delivery timing is useful to your integration           |

Do not assume that export chunks have the same event-ID stability as classified notifications.

## Retries and circuit breaking

| Delivery type           | Current retry contract                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Classified notification | One initial delivery plus up to {/* immoteur-parameter:start {{immoteur.webhook.classified.retry_count}} */}3{/* immoteur-parameter:end */} retries. Default retry delays are {/* immoteur-parameter:start {{immoteur.webhook.classified.retry_delays}} */}30, 60, and 120{/* immoteur-parameter:end */} seconds. A receiver `Retry-After` or `RateLimit-Reset` can adjust the delay, with a maximum of {/* immoteur-parameter:start {{immoteur.webhook.classified.retry_delay_cap}} */}5 minutes{/* immoteur-parameter:end */}. |
| Classified export       | The current default is {/* immoteur-parameter:start {{immoteur.webhook.export.attempt_count}} */}3{/* immoteur-parameter:end */} total attempts. An unsuccessful attempt requeues after {/* immoteur-parameter:start {{immoteur.webhook.export.retry_delay}} */}30 seconds{/* immoteur-parameter:end */}.                                                                                                                                                                                                                        |

A non-`2xx` response, including a `4xx`, is not a successful acknowledgement. An export can have multiple delivery attempts and chunks; do not assume a fixed chunk size or strict order.

```mermaid theme={null}
flowchart TD
  A[Classified update or export chunk] --> B[HTTP POST]
  B -->|2xx| C[Delivery acknowledged]
  B -->|Non-2xx or transport failure| D[Apply delivery retry policy]
  D -->|Retry succeeds| C
  D -->|Retries exhausted| E[Delivery fails]
  D -->|Circuit breaker threshold reached| F[Circuit opens temporarily]
  F --> G[Skip deliveries while open]
```

Immoteur applies the appropriate retry policy after an unacknowledged delivery. When {/* immoteur-parameter:start {{immoteur.webhook.circuit.in_flight_retry_limit}} */}1,000{/* immoteur-parameter:end */} retry events are in flight for one service, its circuit opens for {/* immoteur-parameter:start {{immoteur.webhook.circuit.cooldown}} */}10 minutes{/* immoteur-parameter:end */}. Deliveries for that service are skipped during the cooldown and are not automatically replayed.

Configure the endpoint and filters in [Configure Immoteur webhooks](./webhook). Use [Mirror classified data](./mirror-classified-data) for the full export, live notification, and delta-export integration flow. Use [API reliability](./api-reliability) for API rate-limit headers.
