A timeout tells you that a response did not arrive in time. It does not tell you whether the server finished the operation. That distinction matters whenever a request creates something, sends something or changes a record. Retrying blindly can turn one uncertain result into two very definite mistakes.
AWS explains the problem well in its guide to idempotent APIs. A caller-provided request identifier lets the service recognise another attempt at the same operation. The identifier needs to belong to the original intent; generating a fresh one for every retry defeats the point.
I would make that behaviour part of the API contract, including what happens if the same key arrives with different parameters or while the original request is still running. Recording the key and performing the change also need a reliable relationship. A separate “have we seen this?” check followed by a write is not enough if two workers can both pass the check.
The interface should reflect uncertainty as well. “We could not confirm the result” is more honest than “nothing happened” when the connection dropped. Give the user a way to check the existing operation before starting another one. A slightly less confident message can save a lot of cleanup.