An idempotency key lets a client safely retry a request without causing duplicate transactions.
There are situations when a transaction could be submitted two or more times. Reasons include network failures, timeouts, retry logic, or an impatient customer clicking the purchase button several times. These could cause a customer to send the same request at least twice. This means the customer might make the same purchase multiple times, for example. By the same reasoning, customers could have intentionally submitted the transaction several times. They might intend to purchase the same item more than once.
Flute needs a way to differentiate multiple transactions to protect the customer.
Idempotency means an operation gives the same result whether it runs once or many times. An idempotent request is safe to retry, because a retry does not create a new operation.
For instance, GET does not change any data, so a client can call it multiple times. PUT is also an idempotent call to make multiple times. While the initial call may change data, later calls with the same request body do not change it again. Even DELETE is an idempotent call. Again, while the initial call may delete a resource, subsequent calls will not; the resource has already been deleted. Superfluous PUT and DELETE may return an error, but no additional changes are made to the data or transactions.
In contrast, some POST calls are not safe to call multiple times. Multiple POST transactions, even if unintended, may invoke the same purchase several times.
Flute supports idempotency through an optional request header named idempotency-key. While optional, we recommend using this header key to protect customers.
The client generates this key and sends it with each mutating request. A mutating request is any request that potentially creates, updates, or cancels a resource or transaction.
This header is available on Flute v2 endpoints only. It is not available on Flute v1 endpoints.
Use an idempotency key on any endpoint that changes data, especially one that moves money. The table below lists every endpoint that accepts the idempotency-key header.
| Area | Endpoint |
|---|---|
| Payment Links | POST /v2/payment-links |
| Payment Links | POST /v2/payment-links/{paymentLinkId}/share |
| Payment Sessions | POST /v2/payment-sessions |
| Payment Sessions | POST /v2/payment-sessions/{paymentSessionId}/cancel |
| POS Transactions | POST /v2/pos/transactions |
| POS Transactions | POST /v2/pos/transactions/{posTransactionId}/cancel |
| POS Transactions | POST /v2/pos/transactions/reversal |
| Settlements | POST /v2/settlements/batches/close |
| Transactions | POST /v2/transactions |
| Transactions | POST /v2/transactions/{transactionId}/capture |
| Transactions | POST /v2/transactions/{transactionId}/reversal |
| Transactions | POST /v2/transactions/credit |
| Transactions | POST /v2/transactions/{transactionId}/tip-adjustment |
| Transactions | POST /v2/transactions/{transactionId}/ach-hold |
| Transactions | POST /v2/transactions/{transactionId}/ach-release |
Clients may provide a unique idempotency key for each new operation, not for each request attempt. A UUID or GUID works well, because each is unique and hard to guess. This format is not required, however. Any unique value is allowed.
Idempotency keys are scoped to the API client. Two different merchants or two different API keys for the same merchant can safely use the same key value without colliding.
Send the key in the idempotency-key request header. Reuse the same key only when retrying the same operation after a failure. Do not reuse a key for a new, unrelated operation.
If Flute detects:
- the same idempotency key
- the same request body
- within a limited time period, also called the retention window
Flute assumes it is the same operation. It allows one of the operations to succeed. All other attempts return an error.
For example, the customer might intend to buy the same item through several transactions. In that case, provide each different transaction with a different unique idempotency-key.
Store the idempotency key with the operation on the client side. This allows a retry after a crash to reuse the correct key.
If the idempotency key is not included, the call will not have idempotency protection.
Flute checks each key against any request it has already processed. Flute keeps this record only for the retention window.
| Situation | Result |
|---|---|
| Same key, same request body | Flute returns the original response again. No new operation runs. |
| Same key, request still processing | Flute returns 409 Conflict. Retry again after a short delay. |
Same key, different request body (422 Unprocessable Content) | Flute rejects the retry. Generate a new key for the different request. |
| New key, or no key at all | Flute treats the request as a new operation. |
Server error, such as 500 Internal Server Error | Flute does not store the key. A retry with the same key runs the operation again. |
The following example creates a transaction with an idempotency key.
POST /v2/transactions HTTP/1.1
Host: sandbox.api.flute.com
Authorization: Bearer <ACCESS_TOKEN>
Content-Type: application/json
idempotency-key: 6a1f7e2c-4b3d-4e5a-8f9c-1d2e3f4a5b6c
Content-Length: 160
{
"paymentProcessorId": "a2f2c2e7-0b23-4682-8f99-ab7707205461",
"baseAmount": 100.00,
"currencyCode": "USD",
"referenceId": "REF-ORDER-1042"
}If a network timeout occurs, for example, the client sends the exact same request again. The client reuses the same key and the same request body. Flute returns the original response, and no second transaction is created.
A retention window (also called TTL [time to live]) is the length of time Flute uses to remember an idempotency key and its result.
While the window stays open, a retry with the same key returns the stored result. Flute does not run the request again.
After the window closes, Flute treats the key as new. As a best practice, however, we recommend never intentionally reusing an idempotency key.
Flute uses two separate retention windows for each idempotency key.
| Setting | Default | Purpose |
|---|---|---|
| TtlMinutes | 1440 minutes (24 hours) | Sets the replay window. This window is how long a completed response is stored and replayed for the same key. |
| InFlightTtlMinutes | 5 minutes | Sets the in-flight lease window. This window is how long a key is held while the original request is still processing. |
The values above are defaults. An environment override can change them for a specific deployment.