Idempotency

Send a write again after a network failure without risking a duplicate, with the Idempotency-Key header.

API documentationGuides · Idempotency

The problem

A write request goes out, the connection drops, no response comes back. Did the write happen? Without precautions, sending it again may create a second contact, redeem a promo code twice or publish two replies.

The Idempotency-Key header solves this: a request sent again with the same idempotency key runs only once, and every attempt receives the same response.

Use an idempotency key

Pick a unique value per business operation, for instance a UUID or the operation's identifier in your tool, and keep it for every new attempt of that same operation:

curl https://api.mabellenote.com/v1/promo-codes/redeem \
  -H "Authorization: Bearer $MBN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: till-1-ticket-2026-10-25-0042" \
  -d '{ "code": "BIS-7KQ4", "establishmentId": "kh2n8p5r3t6v9b1c4d7f0g2j5k8m1n3p", "amountCents": 4850 }'
  • The key has 1 to 255 characters. Outside these bounds, the API answers 400 invalid_request.
  • It is specific to the API key: two API keys can use the same value without interfering.
  • It is kept for 24 hours after its first use. Beyond that, the same value starts afresh.

What the API answers

SituationResponse
First request with this keyRuns normally.
Same key, same request, already completedThe original response, replayed, with Idempotent-Replayed: true.
Same key, same request, still running409 idempotency_in_progress: try again a little later.
Same key, different request (other method, path or body)422 idempotency_key_reused.

A request refused with an error, for instance 400, 404 or 409 promo_code_already_redeemed, does not keep its key: once fixed, sending it again with the same key runs it again. Only successful responses are replayed.

Operations concerned

OperationIdempotency-Key
POST /v1/contactsOptional
PATCH /v1/contacts/{contactId}Optional
POST /v1/visitsOptional
POST /v1/promo-codes/redeemOptional
POST /v1/reviews/{reviewId}/reply-draftsOptional
PUT /v1/reviews/{reviewId}/reply-draftOptional
POST /v1/reviews/{reviewId}/replyRequired

Reads do not need it: they change nothing. POST /v1/webhook-endpoints does not accept it, because its response contains the signing secret, which must not be stored.

Special cases

  • Visits: on top of the header, a visit is unique per externalId within a location. Sending the same ticket number twice only creates one visit, even without Idempotency-Key: the first response is 201, the following ones 200.
  • AI-written drafts: the generation does not happen in the same transaction as the key record. The guarantee is therefore “at most one generation per completed idempotency key”: a request interrupted mid-computation may, rarely, use a generation without a response.
  • Publishing on Google: the key is required, so that a replayed call never publishes twice. Use the review identifier, or the reply's identifier in your tool.

Good practices

  • Generate the key before the first attempt, and keep it with the operation until you get a response.
  • Retry with the same key on a network error, a timeout, 409 idempotency_in_progress, 429 rate_limited or 503 service_unavailable, respecting Retry-After.
  • Never reuse a key for another operation: you would get 422 idempotency_key_reused, or worse, the response of another operation if the bodies are identical.

Updated on October 25, 2026