> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowlix.eu/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotency

> Retry every create operation without duplicating a Payment, Refund, or Payout

Every public create operation requires an `Idempotency-Key` header:

| Operation               | What one key identifies         |
| ----------------------- | ------------------------------- |
| `POST /v1/payments`     | One Direct Payment attempt      |
| `POST /v1/payments/hpp` | One Hosted Payment Page attempt |
| `POST /v1/refunds`      | One Refund                      |
| `POST /v1/payouts`      | One Payout submission           |

Generate and persist the key before sending the request. A UUID or a stable key
derived from the business operation both work, provided you never reuse it for
a different request.

```bash theme={null}
curl -X POST https://api.flowlix.eu/v1/payments/hpp \
  -H "Authorization: Bearer $FLOWLIX_API_KEY" \
  -H "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Content-Type: application/json" \
  -d '{ ... }'
```

## Retry outcomes

| Situation                                           | Result                                                        | Your next action                                                                         |
| --------------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| First request with a key                            | The operation is processed normally.                          | Store the key with the operation.                                                        |
| Same key and equivalent effective request identity  | The original result is returned; no second object is created. | Accept the replayed result.                                                              |
| Same key and a different effective request identity | `409 idempotency_key_reused`.                                 | Retry the original operation, or use a new key for a genuinely new business operation.   |
| Original request is still processing                | `409 idempotency_key_in_use`.                                 | Wait briefly, then retry with the same key and an equivalent effective request identity. |
| Missing key                                         | `400 parameter_missing`.                                      | Add the header before sending the request again.                                         |

Keys are scoped to the API key and operation. The same text used for
`POST /v1/payments` and `POST /v1/refunds` identifies two independent
operations, but reusing values across operations makes incident analysis much
harder. Prefer a new key for every new business operation or Payment attempt.
No fixed idempotency retention period is promised by this API contract.

## Operation-specific effective request identity

Identity is derived from parsed fields, not raw JSON bytes. JSON property order
and whitespace do not change the identity. The fields and normalization rules
are different for each operation:

| Operation           | Fields in the effective identity                                                                                                                                                                                     |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Direct Payment      | `amount`, currency text as submitted, `billing_details`, `customer_ip_address`, `description`, `merchant_customer_id`, `merchant_reference`, and `return_url`. Card fields under `payment_method_data` are excluded. |
| Hosted Payment Page | `amount`, normalized currency, `billing_details`, `customer_ip_address`, `description`, `merchant_customer_id`, `merchant_reference`, and `return_url`. Currency is trimmed and uppercased for identity.             |
| Refund              | `payment_id`, `amount`, `merchant_reference`, and `reason`. An omitted merchant reference has its own normalized empty value.                                                                                        |
| Payout              | `amount`, normalized currency, canonical initiator IP, uppercased recipient country, recipient names, destination card number, and `merchant_reference`.                                                             |

A Payout replay returns the saved creation response, not a refreshed status.
It can remain `PROCESSING` after the operation completes. Use
`GET /v1/payouts/{payout_id}` for the current state; do not use a new key to
resolve an uncertain existing Payout.

For Direct Payment, changing only card fields while retaining the key replays
the original Payment; it does not submit the changed card. A new card attempt is
a new Payment attempt and requires a new key.

For Payout, the destination card is part of the identity. A retry of one Payout
therefore needs the same destination data, while a distinct Payout gets a new
key.

## Retry recipe

1. Create and persist the key before the first request.
2. Persist the non-sensitive fields that define the operation's effective
   identity.
3. On a timeout or retryable HTTP error, retry with the original key and an
   equivalent effective request identity, using exponential backoff.
4. On `idempotency_key_in_use`, wait briefly and retry the same operation.
5. On `idempotency_key_reused`, compare the effective identity fields. Retry
   the original operation or deliberately start a new business operation with
   a new key.

## Card-data retry recipe

Direct Payment and Payout requests contain card data. Persist the key and
non-sensitive operation data, but never persist PAN, CVC, or a complete
card-data request body merely to support retries.

* Keep card data only inside your approved PCI handling boundary for as long as
  the active request attempt needs it.
* While the original body remains available there, a lost response can be
  retried with the same key and equivalent effective request identity.
* For Direct Payment, card fields are deliberately excluded from idempotency.
  Reusing the key with unchanged non-card fields returns the original Payment,
  even if different card fields are supplied.
* A new card attempt is a new Payment attempt and requires a new key.
* For Payout, retain destination data only within the approved PCI boundary
  while retrying the original operation.

<Warning>
  `merchant_reference` does not prevent duplicates. Two create requests with
  the same `merchant_reference` and different idempotency keys can create two
  objects.
</Warning>

After a terminal `FAILED` or `EXPIRED` Payment, a customer retry is a new
Payment attempt and therefore needs a new key. Retrying a lost HTTP response is
the same operation and therefore keeps the original key.
