Idempotency Keys

Table of Contents

For the past 2 years, I’ve been working on the backend of a digital bank. One of the important concepts that I learned was about Idempotency Keys. In this article, I will discuss:

  • What is Idempotency?
  • Why is idempotency important in banking?
  • What are idempotency keys?
  • How are idempotency keys implemented?

What is idempotency?

An idempotent action is one that, if performed multiple times, produces the same output as if it were done once.

In terms of REST APIs, an idempotent endpoint is one that does not change the state of the resource when you call the endpoint.

GET endpoints are meant to be idempotent. If you call, say, GET /profile multiple times, it won’t cause any changes in the profile resource.

POST endpoints, on the other hand, are NOT idempotent. So if we had a POST /profile endpoint, you’d expect that every time this endpoint is called, a new profile resource is created on the backend.

Why is idempotency important for banking?

“Now, let’s say you’re building the backend for a bank, and you create an endpoint that handles transfers (let’s call it POST /transfer), deducting money from one account and crediting it to another.”

  • Suppose you had $100 in your bank account and used this endpoint to transfer $10.

  • If this endpoint was called once, you’d have $90, and your friend would have $10.

Now, imagine this: A mobile client calls this endpoint, and the connection breaks or times out.

At this point, you don’t know if the transaction was successful or not.

The simplest solution (although not the only one) would be to retry the endpoint. However, if the transaction was successful, the transfer amount will be deducted from the customer’s account TWICE, causing stress to the customer and harming the bank’s reputation.

What we need is a way to inform the backend that we’re retrying the transaction, rather than intentionally performing a new one. This is where idempotency keys come in handy.

What are idempotency keys?

An idempotency key is a unique set of characters, typically just a UUID, generated by the client (e.g., the mobile app) and sent with the API request in the header.

POST /transfer
Host: example.com
Content-Type: application/json
Content-Length: 42
X-Idempotency-Key: 8FB4A212-5B24-4BF3-AF90-C956C5FF006C

{
	"Amount": "10.00",
	"Currency": "USD"
}

The backend checks if it has successfully processed a transaction with the same idempotency key before. If it has, it could either return an error or return the previously created resource.

Idempotency keys can expire. For example, the Mambu Core Banking System expires the idempotency key after 6 hours. This means that let’s say the time is 12:00 PM, and we call the POST /transfer endpoint multiple times.

Number of times POST /transfer calledTimeIdempotency KeyResult
112:00PM0000A transfer is created. Customers account is debited.
21:00PM0000Idempotency key reused and hasn’t expired. New transfer is not created.
35:59PM0000Idempotency key reused and hasn’t expired. New transfer is not created.
46:00PM0000Idempotency key reused and has expired. New transfer is created.

How are idempotency keys implemented?

Front-end

  • The front-end (e.g., mobile app) typically follows a user journey for creating a transfer.
  • At the initial step of this journey, the front-end generates an idempotency key.
  • When the POST /transfer endpoint is called (near the end of the journey), the idempotency key is sent in the header.
  • If the request times out or the connection breaks, the front-end may display an error screen with a retry button.
  • The retry button calls the same POST /transfer with the same idempotency key.

Backend

  • If the backend successfully processes the transaction, it stores the idempotency key and the response in a cache as a key-value pair (with the key being the idempotency key and the response being the value). This key-value pair is stored with a TTL (e.g., 6 hours).

  • If the endpoint is called a second time, it first checks the cache to ensure the request hasn’t already been processed.

  • If it has, the cached response is sent back to the client.

Waqqas avatar
Waqqas
Principal Engineer