# Retries and idempotency

Sometimes a request fails, or you never get its response: a timeout on your side, a dropped
connection, or a 502, 503 or 504 from the network. This page says when you can simply send the
request again.

The API has no idempotency key header. Each endpoint's page has a "Retry safety" section that says
whether that endpoint is safe to send again after a lost response, and how.

## When to retry

| Response                      | Retry?                                                                                                   |
| ----------------------------- | -------------------------------------------------------------------------------------------------------- |
| 400, 401, 403, 404            | No. The same request gets the same answer. Fix the cause first; see [Errors](https://developers.lazyinvoice.co.il/embedded/errors/).         |
| 409 `business_exists`         | No. On a retried create it means the first request succeeded; see [Create a business](https://developers.lazyinvoice.co.il/embedded/api/businesses/create/). |
| 409 `business_not_ready`      | Yes, after you repeat [Create a business](https://developers.lazyinvoice.co.il/embedded/api/businesses/create/) with the same `external_id`. |
| 409 `billing_not_ready`       | No. Contact Lazy.                                                                                        |
| 429 `rate_limited`            | Yes, after the wait that [Rate limits](https://developers.lazyinvoice.co.il/embedded/rate-limits/) describes.                                |
| 500 `internal_error`          | Yes, following the endpoint's "Retry safety" section. For a purchase, a 500 means nothing was bought.   |
| 502, 503, 504, or no response | Yes, following the endpoint's "Retry safety" section. The request may or may not have taken effect.     |

Wait before each retry, and wait longer each time: for example 1, 2, 4 and 8 seconds, then give up
and alert. Add a random delay of up to a second so many failed requests do not retry at the same
moment.

## Purchases: top-ups and scan-period extensions

[Top up documents](https://developers.lazyinvoice.co.il/embedded/api/usage/top-up/) and
[Extend the scan period](https://developers.lazyinvoice.co.il/embedded/api/usage/scan-period/) are not safe to retry after a lost
response: a purchase that succeeded and is sent again is bought, and billed, twice. A
`500 internal_error` means nothing was bought, so retry it freely. When you lost the response
instead, check whether the purchase happened before you retry:

1. Before the purchase, read the business's usage with
   [Get usage](https://developers.lazyinvoice.co.il/embedded/api/usage/get/). Keep its `reset_at` and the limit you are raising:
   `max_docs` for a top-up, `max_email_scan_days` for a scan-period extension.
2. Send the purchase.
3. If you lose the response, read the usage again.
4. If the limit rose, the purchase happened. Do not retry.
5. If the limit did not rise and `reset_at` is unchanged, the purchase did not happen. Send it
   again.
6. For a top-up only: if `reset_at` changed, a new monthly period started and set `max_docs` back
   to the business's monthly allowance, so the two readings cannot be compared. Decide from the new
   usage whether the business still needs a top-up.

Do not send two purchases for the same business at the same time. The recipe cannot tell them apart.
