Idempotency
In the context of an API, idempotency is a design principle whereby repeating the same request multiple times does not alter the state of the system after the first time. This property is particularly important when creating objects, because in case of timeouts or network errors it allows you to replay the request without running the risk of creating duplicates.
Why idempotency matters
When working with APIs, network failures and timeouts can occur at any point in the request-response cycle. Without idempotency, retrying a failed operation could lead to:
- Duplicate resource creation
- Inconsistent data states
- Unnecessary processing or billing
Idempotent operations ensure your system remains consistent even when retries are necessary.
GET, PUT and DELETE requests
In Mambo, all GET, PUT and DELETE requests are intrinsically idempotent and are therefore safe to retry.
POST requests
POST requests are not idempotent by default. However, they can be made idempotent by providing a unique identifier in the Idempotency-Key header. When Mambo receives a POST request with an Idempotency-Key header, clients are guaranteed that for the next 24 hours retrying the same request with the same key will replay the original response without creating additional objects. After 24h since the initial request the idempotency keys are expired from the system.
Note that replayed responses to POST requests will include both successful attempts as well as attempts that generate an exception. For example, if you send a POST request to create an Activity and the request results in a 404 UserNotFound response, sending the same request (with the same idempotency key), will always result in a 404 UserNotFound response. This is true even if the missing user is created after the initial request.
Sending idempotency keys
Idempotency keys should be random and unique. Any non-empty string is a valid idempotency key, however we recommend using a string in the UUID v4 format, since they are guaranteed to be unique and can be easily generated in most programming languages using standard libraries (e.g. the UUID class in Java, the System.Guid class in C# and the uuid module in Python). Sending an empty string in the Idempotency-Key header will result in an InvalidIdempotencyKeyException.
Response headers
The response to any POST request will include an Idempotency-Status header. The table below outlines the possible values of this header together with their meaning.
| Value | Status Code | Description | Action |
|---|---|---|---|
| Ok | Any | The request was processed and the response was saved. | N/A |
| Replayed | Any | An existing request for the same idempotency key was found. The response is being replayed. | N/A |
| NotRequested | Any | The Idempotency-Key header was not present. | N/A |
| InvalidKey | 400 | The idempotency key supplied is not a valid. | Repeat the request using non-empty string in the Idempotency-Key header. |
| InProgress | 409 | The original request for this idempotency key is still being processed. | Retry the request later. |
| KeyAlreadyUsed | 409 | The idempotency key was previously used with a different request. | Retry the request with a different key or ensure the request is the same as the original request. |
Idempotency flow
Implementing idempotent requests typically follows this pattern:
- Generate a unique UUID v4 for your operation
- Include it in the
Idempotency-Keyheader - Make your POST request
- If the request fails due to network issues, retry with the same key
- Check the
Idempotency-Statusheader to determine the result
Idempotency using Mambo SDKs
Best practices
When implementing idempotency in your applications:
- Use consistent keys: Use the same idempotency key for retries of the same logical operation
- Store keys: Consider storing idempotency keys alongside your records to track which operations have been performed
- Handle failures: Check the
Idempotency-Statusheader to understand why a request might have failed - Set appropriate timeouts: Configure your HTTP client with reasonable timeouts to trigger retries when necessary