ServiceNow Idempotency Explained
What Idempotency Means in Practice
Idempotency is a property of an operation: performing the operation once or multiple times produces the same result. An idempotent operation is safe to retry; if the call fails and you retry, the outcome is the same as if you had only called it once. Idempotency is essential for reliable systems because networks are unreliable: API calls fail, timeouts occur, connections drop. Without idempotency, a retry of a failed call might execute the operation twice, causing errors like duplicate records or double charges.
Example: a payment processing call is supposed to charge a customer 100. The call succeeds, but the network returns an error before the client receives the success response. The client retries the call. Without idempotency, the customer is charged twice (200 total). With idempotency, the retry recognizes that the charge already succeeded and returns the same success response without executing again. Same result: one charge for 100, whether the call succeeded immediately or required a retry.
How Idempotency Is Implemented
Idempotency is typically implemented using unique identifiers called idempotency keys. When a client makes an API call, it includes an idempotency key (a unique identifier for this operation, often a UUID or random string). The server records: “idempotency key X resulted in outcome Y.” If the client retries with the same idempotency key, the server looks up the key, finds the previous result, and returns it without executing the operation again.
The idempotency key must be stable across retries: it should not change if the client retries. It should be unique per operation: different operations should have different keys so the server can distinguish them. The server should retain idempotency key results for a defined window, set by your team, to handle retries. After the window expires, the server can forget the key and new calls with that key are treated as new operations.
Idempotent and Non-Idempotent Operations
Some operations are naturally idempotent. Reading data (GET requests) is idempotent: fetching a record multiple times returns the same data each time. Setting a field to a specific value is idempotent: setting a field to “approved” multiple times results in the field being “approved” once. These operations produce the same result regardless of how many times they are executed.
Other operations are non-idempotent. Creating a new record is not naturally idempotent: if you create the same record twice, you get two records (duplicates). Incrementing a counter is not idempotent: incrementing twice gives a different result than incrementing once. These operations must use idempotency keys or must include logic to detect duplicates and avoid double execution.
ServiceNow APIs provide idempotency support for some operations but not all. Before building an integration, check the API documentation to see which endpoints support idempotency and whether your integration needs it. If you are making potentially risky calls (charges, record creation, data modifications) over unreliable networks, idempotency or duplicate detection is essential.
Design Patterns for Handling Idempotency
When integrating with external APIs, consider whether the API is idempotent. If it is not and your integration makes calls that could have bad consequences if executed twice, implement client-side duplicate detection. Keep a local log of which operations you have already executed and what their results were. When a call fails, check the log before retrying: if you have already executed this operation, return the cached result rather than making the call again.
Use idempotency keys when available. Include a unique identifier with each API call (generate a UUID, or use a combination of operation type and record ID). If the call fails, retry with the same key. The server recognizes the key and returns the previous result without re-executing the operation. This is cleaner than client-side tracking and works better with distributed systems where multiple instances of your code might be running.
Consider retry strategy. Not all failed calls should be retried immediately. Errors like “invalid input” will fail again if you retry with the same data; retry makes no sense. Errors like “service temporarily unavailable” might succeed on retry. Errors like “request timeout” might have partially succeeded (the operation might have gone through but the response was not received); retry with idempotency is essential. Build retry logic that considers the error type and decides whether retry makes sense.
Testing Idempotency
Test idempotent operations by calling them multiple times and verifying the result is the same each time. For a record creation operation, call it twice with the same idempotency key and verify that one record is created (not two). For a field update, update the field multiple times and verify it has the expected value (not some intermediate state). For an external API call, intentionally simulate failure scenarios (network timeout, service error) and verify that retrying with the same idempotency key produces the same result as the initial success.
Verify that the server properly handles idempotency keys. Check: are keys stored and recognized correctly? Does the server return the same result when the same key is used? Does the server properly expire old keys (no longer tracking results after the retention window)? Can different clients share the same key correctly? Test edge cases and error scenarios, not just the happy path.
Choosing where idempotency has to live in an integration
Idempotency is often discussed as a property of an API, but in an integration the real question is where duplicate execution could happen and who is able to stop it. A ServiceNow flow that calls an external system is exposed at three points: the outbound call itself, the retry logic wrapped around it, and the receiving side that creates or changes data. Protection at one point does not cover the others. A receiving system that ignores repeated keys is useless if the sending flow regenerates a new key on every attempt, and a sender that dedupes locally will still double-post when two instances of the same flow run in parallel.
| Failure point | What duplicates look like | Control that catches it |
|---|---|---|
| Timeout after the far side committed | Second record created with the same content | A stable key stored on the receiving side |
| Flow retried by an error handler | Several outbound calls for one business event | A retry that reuses the key from the first attempt |
| Two flow runs fired by one trigger | Parallel calls that both pass validation | A claim or lock record written before the call |
| Replay of a queued message | An update applied after a newer one | A version or sequence check on the target |
Building the key so that retries reuse it
The key must describe the business event, not the attempt. A common mistake is to generate a random identifier inside the retry loop, which gives every attempt a fresh key and removes the protection entirely. Derive the key from stable facts instead, such as the source system name, the source record identifier and the event type. If one source record can legitimately generate more than one event, add the version or timestamp of that event to the key, and make sure the value is captured once when the event is first received.
- When the event first enters the integration, write it to a log table with a status of pending and the derived key.
- Check that log for the key before making the outbound call. If a completed entry exists, return its stored result.
- Make the call with the key in a header the receiving system understands, or in the body if the receiving API has no header convention.
- On success, update the log entry to completed and store the response reference. On failure, leave the entry pending and record the error class.
- Allow a separate, scheduled reconciler to look at entries stuck in pending and decide, by querying the receiving system, whether the call actually landed.
Deciding which errors deserve a retry
Not every failure should be retried, and the decision should be made per error class rather than by a single global policy. Validation rejections will fail identically on every attempt, so retrying only wastes capacity and fills logs. Server unavailability and rate limiting are good candidates for a delayed retry. Timeouts are the dangerous case, because the far side may have completed the work. For those, the only safe retry is one that carries the original key and that the receiver is known to honor. Where the receiver does not honor keys, route timeouts to the reconciler instead of the retry queue.
Checks that show the protection actually works
A common finding in review is that a team has written a key but never proved the receiver uses it. Test with deliberate faults. Force a timeout after the call is sent, retry it, and count the records created in the target. Run the same event through two flow instances at once and check whether one of them is refused or both succeed. Replay an event that is several days old, after the key may have been purged on the receiver, and see what happens. Each test has an expected outcome written down before it is run.
When idempotency is not worth building
Some integrations do not need this machinery. A nightly read-only extract that overwrites a snapshot is naturally safe to run twice, and adding a key log would only slow it down. Likewise, operations that set a field to a fixed value are idempotent by nature. Save the effort for calls that create records, move money or fire notifications to people, and for anything where a duplicate would be noticed by a customer or an auditor before a technician.
Frequently asked questions about duplicate-safe integrations
Can we rely on the other system’s retry handling instead of ours?
Only if its documentation and your tests confirm that it honors a key for the operation you call. Some external APIs deduplicate for a limited window only, so a delayed retry can fall outside it. Treat the other system’s behavior as an assumption to test, not a guarantee to design around.
Where should the key log be stored?
In a table the integration owns, with a unique index on the key so the platform itself refuses a second pending row. Keeping it in a flow variable or in memory loses the protection the moment a flow restarts.
How long should completed keys be kept?
Keep them at least as long as the longest period in which a retry can realistically occur, including delayed retries and reconciliation runs. Set the period with the owner of the receiving system, and document it next to the integration design.
Does idempotency matter for inbound calls into ServiceNow?
Yes. An inbound call that creates a record can be sent again by the caller after a timeout. Apply the same key check on the ServiceNow side before the insert, so that a repeated request returns the original record rather than creating a copy.
Next step
For the surrounding design choices, the integrations library covers how outbound calls are structured, and the guide to API contract versioning explains how receiver changes can quietly break the key behavior you rely on.
Frequently asked questions
If an API call fails and I retry without an idempotency key, how do I know if it executed once or twice?
You cannot know for certain without investigation. The operation might have succeeded and the network failed to return the response (you retry and duplicate is created). It might have failed before executing (you retry and it executes once). It might have been processing when you retried (you wait and it executes once). Use idempotency keys to avoid this ambiguity entirely.
How should we choose the idempotency key for an operation?
Use a value that uniquely identifies the operation. A UUID generated for this call works. A combination of operation type and record ID works if each record operation is unique. A timestamp plus operation type works if operations are not simultaneous. The key must be consistent across retries (do not regenerate it randomly) and unique per operation.
Do all APIs support idempotency, or only some?
Not all APIs support idempotency. Check the documentation for the API you are calling. If the API supports idempotency (usually by accepting an idempotency-key header or similar parameter), use it. If the API does not support idempotency, implement your own duplicate detection logic, or be very careful about retrying non-idempotent operations.
Related guides
Related Oblytech services
Where this topic turns into delivery work, these are the Oblytech services that cover it.
Planning, running or fixing a ServiceNow programme? Tell us where you are and we will suggest a sensible next step.
Contact us