Skip to main content
The two v2 endpoints that move money require an Idempotency-Key header:
  • POST /v2/orders — buy a credit pack.
  • POST /v2/surveys/{id}/publish — pay for and publish a draft survey.
GET, PATCH, and DELETE calls are naturally idempotent and safe to retry on network error. POST /v2/surveys/{id}/stop is also safe to retry — stopping an already-stopped survey is a no-op. Other POST calls (POST /v2/surveys, POST /v2/media, POST /v2/webhook-endpoints) do not accept Idempotency-Key and are not deduplicated server-side. A retry after a lost response can create a duplicate draft survey, upload the same media twice, or register a second webhook endpoint. If you can’t tell whether the original attempt landed, list the resource (GET /v2/surveys?..., GET /v2/webhook-endpoints) or use your own request identifier (externalId on surveys) to reconcile before retrying.

The key

Send a unique string per attempt in the header:
  • Up to 255 characters. A UUID or a hash of your own request identifiers both work.
  • Scoped per team. Two different teams can reuse the same key without collision.
  • Retained for 24 hours after the first attempt.
  • Generate a fresh key per attempt. Reusing a key is only correct when the request body is byte-identical and you want the same server response.

Replay behaviour

Retrying with the same key and the same body replays the original response:
  • If the original succeeded (2xx), you get the exact same 2xx body back, with Idempotent-Replayed: true in the response headers.
  • If the original returned an error, only the first response is stored — non-2xx responses are not replayed. Retrying is a fresh attempt.
Retrying with the same key but a different body fails with 409 idempotency_mismatch. Either use a new key or send the identical body. Retrying while the original attempt is still running (concurrent duplicates) fails with 409 idempotency_in_flight and a Retry-After header — wait, then retry.

402 on publish never binds the key

A 402 from POST /v2/surveys/{id}/publish (insufficient_credits, card_declined, card_authentication_required) does not bind the Idempotency-Key. The survey stays as a draft, nothing was charged, and you can publish again — but you must use a new Idempotency-Key on the retry. The old one is unreserved.

Credit-pack order retry guarantee

Once you start a credit-pack checkout with POST /v2/orders, its identity and parameters are frozen for 24 hours on the same Idempotency-Key. If a network hiccup drops the response, retry with the same key and body to resume the same order and Stripe Checkout Session — you’ll get back the same checkoutUrl and orderId and won’t double-charge the buyer. Two things end that guarantee:
  • The 24-hour window expires.
  • You send a different body under the same key (which fails 409 idempotency_mismatch before charging anything).
For a genuinely different purchase, always generate a new Idempotency-Key.

Example