As a developer, you are always wanting things to go smoothly, but in the world of REST APIs, that isn’t always the case. Client failures, networking errors, or just poorly written code can all spell trouble for an API call (and your end user experience). Luckily, idempotency is here to help! It can be a little confusing at first, but once you have a handle on it, you’ll see that it is an important aspect of designing and using mission critical systems.
Idempotence is the property of certain operations in mathematics and computer science whereby they can be applied multiple times without changing the result beyond the initial application. — Wikipedia
In the context of a developer working with REST APIs, your application is sending requests and getting back responses with some other service on the internet. When that API is idempotent guarantee that when you send an identical request you will always get an identical response. What do I mean by identical? The headers, body, authorization, everything needs to be exactly the same.
Not all REST APIs use idempotency, most of the time it is only necessary for requests that influence the state of something, or you really don’t want to accidentally run twice. For many APIs, this is limited to POST or PUT requests, whereas most “state neutral” requests do not add the complexity to their interfaces. In most cases, with Square’s APIs you will see this in the context of Transactions where you want to keep the state of bank accounts in order, and prevent accidental money movement.
Idempotency with Payment APIs
For our idempotent APIs, there is a dedicated body parameter called
idempotency_key to help you keep track of your requests. This is a string that you include in the request body. When we see the request you make with that specific key, we store the request and response with that key so that we can see if you make the same request again. When you send a duplicate request with the same
idempotency_key we will look up the response that you received earlier, and send that back as well. If you send a request with the same
idempotency_key but a different body, you will get an error. If you send the same request (including the
idempotency_key) then we will simply look up the previously sent response and send it back to you. If you are familiar with the
Etag header it is another method to help prevent accidental state changes but with a different mechanism.
It is important to note that getting back a “successful” response in the context of idempotency doesn’t mean that you are getting a
200 OK. Even if you get back a
500 or something else indicating that your request didn’t work, you will still need to update your
idempotency_key as the one you just used will be tied to that “unsuccessful” request.
When you do get a replayed request, everything will be the same, same headers, status code, etc. One clue you can use to see if you are getting a replayed request is to look at the timestamp in the response, if you are getting a response with a timestamp a few hours, or days ago, you know that this is probably not the first time you are getting this particular response.
You might be thinking that there are lots of good use cases where you want to be making lots of similar requests; how does making the same API call twice differ than making a single request that gets repeated from network stutter?You’ll just need to make sure that for each transaction you want to make, you have a unique
idempotency_key for the request.
“Where do I find the idempotency key?”
Where are you supposed to get that unique value for each request? Well, it isn’t something you can look up in your developer dashboard like your access token. You’ll need to decide the best way to set the
idempotency_key with your application architecture. Common examples are using the current timestamp or a UUID for the
idempotency_key and while it is true that these are generally unique, they have some drawbacks. Consider an e-commerce site with a single page front end that sends the card data (in this case a card nonce) to a backend asynchronously to make a charge. What happens if your user clicks that charge button twice, sending two requests to your backend? If you are dynamically creating the idempotency key each time your backend code runs, then you would accidentally be charging your customer twice (in reality you can’t charge the same nonce twice, but that is besides the point).
A better strategy for setting your
idempotency_key might be to use something like a session id, a hash of the username & date, a transaction id that you are using in another system, or even a uuid that is assigned when a user starts a session instead of at the time of the request. That way you might guarantee that every unique idempotency key that you make is a for a unique purchase but you’ll also have the information to catch problems before the request when you might accidentally making duplicate charges. There isn’t a value that works for everyone, but spend some time to think about how applications will be making idempotent requests before you architect your system.
In many cases, working with payments for the first time can be your first introduction to idempotency, but that doesn’t mean it should be a bad one. Idempotency is a powerful tool in helping your apps and services be more resilient against the unexpected and uncontrollable. If you want to learn more, you might want to check out the Square documentation or this video about cows (the premier work on the subject).