Idempotent Requests


0. Introduction - Idempotency Idea


Idempotency is a key principle in web API design. It ensures that performing the same operation multiple times results in the same outcome as if the operation was performed only once. This is especially important in scenarios where a request fails (e.g., due to a network error or timeout), and the client retries the request. By enabling idempotency, APIs provide a reliable way to handle such retries without risking unintended side effects like duplicate operations.


1. Idempotency in Mobilitybox API


Basics

The Mobilitybox idempotency works by saving the resulting status code and body of the first request made for any given Idempotency-Key, regardless of whether it succeeds or fails. Subsequent requests with the same key return the same result, including 500 errors.


GET
Requests

The Mobilitybox API guarantees the idempotency of GET requests. This means it is always safe to retry these operations, as they do not cause side effects or change the server's state.


PUT
and
DELETE
Requests

According to HTTP semantics, the PUT and DELETE verbs are idempotent, and the PUT verb in particular signifies that a target resource should be created or replaced entirely with the contents of a request’s payload. So PUT and DELETE requests are idempotent inside the Mobilitybox API as well.


POST
Requests

For POST requests, idempotency can be achieved by including an Idempotency-Key in the request header. This header ensures that the API tracks the operation and prevents it from being executed more than once, even if the client retries the request.

Example Scenario: If a network error prevents a response from being received while creating an Order, the client can safely retry the operation by including the same Idempotency-Key, ensuring that no duplicate order object is created.


Validity of the Idempotency-Key

The Idempotency-Key is valid for at least 24 hours. During this time, retrying the same request with the same key guarantees the operation will not be repeated. After this time has elapsed, there is no certainty that the key and the response are still stored in the cache. We generate a new request if a key is reused after the original is pruned.

The idempotency layer compares incoming parameters to those of the original request and errors if they’re the same to prevent accidental misuse.

We save results only after the execution of an endpoint begins. If incoming parameters fail validation, or the request conflicts with another request that’s executing concurrently, we don’t save the idempotent result because no API endpoint initiates the execution.


Status Codes and Error Messages

Using idempotency of the Mobilitybox can cause new status codes or error messages:

Accepted: If an request timeouts on clientside, the Mobilitybox still execute the operation. When the client sending the same request again and the operation is still being processed it will return 202 - Accepted.

Conflict: When used the same Idempotency-Key for two different requests (for example having a different request body or using a different Api-Key) the API will return a Conflict Error.

Internal Server Error: If an internal error occurs (like dropping internal connection caused by restarting of an internal component) the result would be an Internal Server Error.

HTTP Status Code References:

Code Tag Description/Message
200 OK Everything worked as expected.
201 Created Everything worked as expected and a resource was created or changed.
202 Accepted The request is already being processed. Please retry again in a few seconds.
400 Bad Request The request was unacceptable, often due to missing a required parameter.
401 Unauthorized No valid API key provided.
403 Forbidden The API key doesn’t have permissions to perform the request.
404 Not Found The resource was not found.
409 Conflict The request conflicts with another request (perhaps due to using the same idempotent key).
422 Unprocessible Entity Validation Error
500 Internal Server Error Internal Server Error


2. How to make your request Idempotent


Sending Idempotency Keys

Idempotency keys are sent in the Idempotency-Key header. Use them for all POST requests to the Mobilitybox Ticketing API.

All POST requests accept idempotency keys. Don’t send idempotency keys in GET, PUT and DELETE requests because it has no effect. These requests are idempotent by definition.

When you don't send an Idempotency-Key the result will not be cached and it has to be assumed that the same operation will be performed twice.


Requirements for Idempotency Keys

A client generates an Idempotency-Key, which is a unique key that the server uses to recognize subsequent retries of the same request.

How you create unique keys is up to you, but we require and suggest the following things:

  • Ensure that the key is unique enough to identify a specific operation within your account
  • Keys must not repeat during their validity window to avoid accidental request duplication.
  • The Idempotency-Key must be at least 36 and no more than 255 characters long.


Best Practices for Generating Idempotency Keys

We recommend using a well-established algorithm, such as UUID v4, to generate sufficiently random and unique tokens. This minimizes the risk of collisions and ensures reliable operation tracking.

Example:

Idempotency-Key: "10adfcd5-f490-490f-a384-4f4a17831f42"


Best Practices for Handling long running Requests

Certain requests to the Mobilitybox API may take longer to process, particularly during periods of high server load. This can result in client timeouts even though the operation is still being executed on the server.

To address such scenarios, the provided Idempotence option allows clients to retrieve the potential response of a previously submitted request. This ensures that clients can obtain results even if the initial request was interrupted due to a timeout.

Additionally, for requests that are still being processed, the server will respond with a 202 Accepted status code. This indicates that the operation is in progress and not yet completed.

We recommend that clients implement an exponential backoff algorithm when retrying requests after encountering errors or delays. This approach helps manage retries efficiently while avoiding excessive load on the server.


Identifying Replayed Responses

When the server replays a previously executed response due to a request retry, the response will include the following header:

Idempotent-Replayed: true

This header allows clients to distinguish between a fresh response and a replayed one, aiding in debugging and monitoring. This header will also be set if the return is an error like 4XX or 5XX.


3. Example Implementation

The following pseudo code example showing how to use the Idempotency for the order api.

FUNCTION CreateOrderInMobilitybox
  SET product_id = "mobilitybox-product-uuid"
  SET api_url = "https://api.mobilitybox.com/v7/ticketing/order.json"

  # Generiere eine eindeutige ID für die Idempotenz
  SET idempotency_key = GENERATE_UUID_V4()

  # Erstelle den Header mit dem Idempotency-Key
  SET headers = {
    "Content-Type": "application/json",
    "Idempotency-Key": idempotency_key
  }

  # Beispiel-Payload
  SET payload = {
    "product_ids": [product_id],
    "optional_order_reference": "User ID: XXX"
  }

  order_response = MakeOrderRequestInMobilitybox(url, headers, payload)

  SaveOrderIntoDatabase(order_response)
END FUNCTION

FUNCTION MakeOrderRequestInMobilitybox(url, headers, payload, attempt = 0)
  WAIT 2^attempt seconds

  SET timeout = 3 seconds

  TRY
    response = HttpPost(url, payload, timeout)
    IF response.statuscode IS 202
      MakeOrderRequestInMobilitybox(url, headers, payload, attempt + 1)
    ELSE
      return response
    END IF
  CATCH Timeout
    MakeOrderRequestInMobilitybox(url, headers, payload, attempt + 1)
  END TRY

END FUNCTION