Idempotency

The page describes how to safely retry POST requests to the Mipise Payment Services API without creating duplicate resources.

🌐 Introduction

Every POST endpoint of the MPS API accepts an optional Idempotency-Key HTTP header. Include it
on any request that mutates state — creation, transfer, mandate, and so on — so that a network
timeout or an ambiguous 5xx response never turns into a duplicate resource.

📘

A retry with the same Idempotency-Key and the same body within 7 days replays the exact
response of the original call
— same status code, same body — even if the original call had
returned an error. One key identifies one business intent.


🔑 Header Format

The key is a client-generated string with these constraints:

  • Length: 8 to 255 characters.
  • Charset: [A-Za-z0-9_-:.].
  • Recommended: a UUID v4 generated on your side.

A malformed key returns 400 with the bad_request error code. Generate your key before
sending the request and persist it locally until you receive a definitive response (2xx or 4xx).


♻️ Lifecycle of a Key

The server treats a key as one of five states:

  • First call — the request is processed normally and the response (status + body) is stored
    server-side for 7 days.
  • Replay (same key + same body) — MPS returns the original response as-is, plus two response
    headers: Idempotency-Status: replay and Idempotency-Replayed-At: <ISO 8601>.
  • Replay with a different body422 validation_failed. Idempotency keys must uniquely
    identify a single business intent; generate a new key.
  • Concurrent replay409 conflict. A previous call with the same key is still being
    processed. Retry after a short back-off.
  • After 7 days — the key is purged and can be reused for a new intent.
👍

5xx responses do not consume the key. You can retry the exact same request until success.


🎯 Scope

Idempotency records are scoped by (agent, HTTP method, path). Two different agents may safely use
the same string on the same endpoint. The same agent may reuse a key on two different endpoints —
they are treated as independent intents.

📘

UUIDs in the URL are normalized before scoping. POST /platforms/A/pay_outs and POST /platforms/B/pay_outs share the same scope for a given agent.


📦 Example Flow

The client sends a first POST with an idempotency key:

curl -X POST https://sandbox.mipisepaymentservices.com/api/platforms/018f.../pay_outs \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9b6e1f3a-2a14-4c12-9a3a-ce3f7a3a1b21" \
  -d '{"pay_out": {"amount": 10000, "currency": "EUR"}}'

MPS creates the pay-out and returns 201 Created with a body like:

{
  "uuid": "e2a1-...",
  "object": "financial_request",
  "amount": 10000,
  "currency": "EUR"
}

The client's network drops before the response arrives. It replays the exact same request with the
same Idempotency-Key. MPS recognizes the replay and returns the same 201 Created and the same
JSON body
, plus two extra response headers: Idempotency-Status: replay and
Idempotency-Replayed-At set to the current UTC timestamp.

Same UUID — no duplicate pay-out was created.

💡

On 409 conflict, retry after 1 – 5 seconds — the first call is still running server-side.


🚫 Reused Key with a Different Body

Sample error response:

{
  "success": false,
  "error": {
    "code": "validation_failed",
    "message": "Idempotency-Key reused with different payload.",
    "details": {}
  },
  "meta": { "timestamp": "2026-07-02T08:12:33Z" }
}

Fix: generate a new Idempotency-Key for the corrected request. Reusing the same key across
different intents is treated as a client bug.


✅ Client-Side Recommendations

  • Persist the key — generate it before sending, keep it locally until you receive a 2xx or
    4xx.
  • Retry safely on 5xx or timeout — reuse the same key with an exponential back-off (e.g. up to
    60 s).
  • Retry on 409 — back off 1 – 5 s, then reuse the same key.
  • One key = one intent — never reuse a key across two independent operations.
  • Regenerate on 422 — if the server rejects your body (validation, mismatch), generate a fresh
    key for the corrected request.
👍

Idempotency is optional. Requests without an Idempotency-Key header are processed with the
historical (non-idempotent) semantics — you get the current behaviour, unchanged.