Summary

  • For an API v1 POST, Stripe stores the first response after endpoint execution starts, including a 500. A retry with the same idempotency key returns that stored response while the key remains retained.
  • Stripe says the operation behind a 500 may have had side effects. It may later reconcile a failed operation and send a webhook for an object created, while the cached response stays unchanged. API v2 uses different retry behavior.

The red receipt can outlast the incident

The customer wants one answer: did the order go through? The support agent sees 500 and the retry sees 500 again. On Stripe API v1, this second response can be a copy of the first, not a second attempt at the underlying operation. Stripe saves the status and body once endpoint execution begins; the header Idempotent-Replayed: true identifies a replay. A dashboard that counts the repeated response as another fresh failure counts receipts instead of customer outcomes.

The uncomfortable part is what can happen behind that receipt. Stripe calls a 500 indeterminate because side effects may already exist. During incident recovery it attempts reconciliation and can send a webhook for an object that the API caller never saw in a successful response. The cached 500 does not turn green when reconciliation advances; Stripe also says reconciliation may fail to resolve the operation. The operator therefore needs a separate way to see the object state.

Stripe's operational guidance is specific: retrieve the relevant object, inspect the request in the Dashboard and reconcile webhook events. A local operation identifier in resource metadata lets the team match a later webhook to the request that first produced the red response. That identity belongs in the order ledger before the first call, so support can answer the customer with the same record finance uses. A new idempotency key while the first result is unknown can start a separate operation.

This rule is versioned. Stripe's API v2 documentation describes retries that avoid repeating successful work and attempt to complete failed or partial work, with an updated response when possible. An integration migrating between v1 and v2 cannot use one generic “retry the 500” playbook. The first question is which API contract handled this request; the next is what the durable object and verified webhook say happened.

Sources