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

# Delivery & Retries

> How StockInsights delivers webhooks, which failures are retried, and on what schedule.

## What counts as success

StockInsights sends each event as an HTTPS `POST` and waits for your response.

* **`2xx`** — the delivery is considered successful.
* Anything else, a timeout, or a connection error — the delivery **failed** and
  is handled per the [retry policy](#retries) below.

Respond `2xx` as soon as you have accepted the event. Acknowledge first, then do
any slow work asynchronously — a slow handler risks hitting the timeout and being
retried even though it succeeded.

<Note>
  Each request waits up to **10 seconds** for a response before it is treated as a
  timeout and retried.
</Note>

## Retries

When a delivery fails, StockInsights retries with exponential backoff and full
jitter — each retry is scheduled at a random point between zero and the backoff
for that attempt, which spreads retries out and avoids thundering herds.

A delivery is attempted up to **8 times** in total — the first attempt plus 7
retries — using this backoff schedule:

| After failed attempt | Wait before the next attempt (approx.) |
| -------------------- | -------------------------------------- |
| 1                    | up to 1 minute                         |
| 2                    | up to 5 minutes                        |
| 3                    | up to 30 minutes                       |
| 4                    | up to 2 hours                          |
| 5                    | up to 6 hours                          |
| 6                    | up to 12 hours                         |
| 7                    | up to 24 hours                         |

After the 8th attempt fails, the delivery is marked failed and the event is not
sent again. In the worst case a delivery is retried for roughly two days before
it is given up on.

### Which failures are retried

| Response                                   | Behavior                                                                                                                        |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `2xx`                                      | Success — no retry.                                                                                                             |
| `408`, `429`, or any `5xx`                 | Retried per the schedule above.                                                                                                 |
| Timeout or connection error                | Retried per the schedule above.                                                                                                 |
| `3xx`                                      | **Not retried.** Redirects are not followed.                                                                                    |
| Any other `4xx` (e.g. `400`, `401`, `404`) | **Not retried.** A `4xx` other than `408`/`429` signals the request will keep failing, so it is treated as a permanent failure. |

<Warning>
  Because a non-`408`/`429` `4xx` is treated as permanent, make sure a failed
  signature check or an unexpected payload does not cause your endpoint to return,
  say, `400` or `404` for a request it should have accepted — that event is
  dropped, not retried. If your endpoint is in a state where it *might* recover
  (mid-deploy, secret not yet rolled out), return a `5xx` so the delivery is
  retried. See [rotating the secret](/api-reference/webhooks/verifying-signatures#rotating-the-secret).
</Warning>

Deliveries are also failed permanently, without retrying, when the destination
URL cannot be delivered to safely: a non-HTTPS scheme, a hostname that does not
resolve, or a hostname that resolves to a private or loopback address.

## Ordering and duplicates

* **Delivery is at-least-once.** Retries and network conditions mean the same
  event can arrive more than once. Make your handler idempotent by de-duplicating
  on the event `id` (the `evt_...` value).
* **Order is not guaranteed.** Events can arrive out of order, especially when
  some were retried. Do not assume the order of receipt matches the order events
  occurred; use `occurred_at` if ordering matters to you.

## Best practices

<CardGroup cols={2}>
  <Card title="Acknowledge quickly" icon="bolt">
    Return `2xx` immediately and process asynchronously to avoid timeouts.
  </Card>

  <Card title="Be idempotent" icon="copy">
    De-duplicate on the event `id` so repeated deliveries are harmless.
  </Card>

  <Card title="Verify every request" icon="shield-check">
    Check the signature before acting on any payload.
  </Card>

  <Card title="Fail soft, not hard" icon="triangle-exclamation">
    Return `5xx` when you might recover, so the delivery is retried rather than dropped.
  </Card>
</CardGroup>
