A timeout doesn’t mean the request failed. Without idempotency, your retry might send the same message twice.
Network calls fail in three ways: the request never arrived, the request arrived but the response was lost, or the request arrived and was processed. From the client side, the last two look identical — a timeout.
This matters more for messaging than for most APIs. A duplicated read is invisible. A duplicated SMS costs money and irritates a customer.
The fix is an idempotency key: a unique value your client generates per logical operation and sends with the request. If the server has seen the key before, it returns the original result rather than performing the work again. Generate it from something stable in your own domain — an order ID, a notification row’s primary key — not a random UUID created at call time, or a retry will generate a fresh one and defeat the purpose.
Pair this with exponential backoff and a cap on attempts.