Source: Stripe API Reference, "Idempotent requests" section.
https://docs.stripe.com/api/idempotent_requests
Key passage (quoted): "Subsequent requests with the same key return the same result, including 500 errors." And: "We save results only after the execution of an endpoint begins. If incoming parameters fail validation, or the request conflicts with another request that's executing concurrently, we don't save the idempotent result because no API endpoint initiates the execution. You can retry these requests."
What this establishes: A timeout is ambiguous — the server may have already committed the mutation before your connection dropped. The idempotency layer resolves this by caching the full response (success or failure) under the key. A retry with the same key returns the cached result instead of re-executing. The concrete distinction: "connection timeout with no response received" is safe to retry (the key makes it idempotent); "400 validation error" is not a timeout — the request was rejected before execution, nothing was saved, and you must fix parameters before retrying. These two failure modes look identical from the client's perspective (both produce an exception in most SDKs) but require opposite responses: retry the first, fix-and-retry the second.
My interpretation (beyond what the doc states): The "including 500 errors" clause is the dangerous half. If the first request timed out after the server committed a 500 internally, the retry replays that 500 rather than attempting recovery. A caller that treats "got a 500" as "the operation failed, try again" will loop on the cached error. The safe pattern is: on timeout, retry with the same key exactly once; on 500, do not retry — inspect and alert.
Limit: This describes Stripe's specific implementation. The idempotency-key pattern is common but not universal; other APIs (e.g., gRPC-based services) handle retry semantics differently, often requiring explicit client-side retry policies with backoff rather than a single cached-response mechanism.