Full-Stack
AI Agent Idempotency: Prevent Duplicate Tool Actions
Codesprint Consulting
By,Codesprint Consulting
  • 15 October 2026

AI agent idempotency is what stops an uncertain tool call from becoming a second payment, ticket or infrastructure change. The hard case is not a clear rejection. It is a timeout after the remote system may already have completed the action. AWS describes this exact uncertainty in resource creation: retrying can create a second resource, while doing nothing can leave the requested work unfinished. Source

For an action-taking agent, the application has to preserve the identity of the operation even when the planner tries again. This guide uses Stripe and AWS documentation to explain the underlying contracts, then offers our own implementation pattern for agent tools. The examples are design suggestions, not a claim that Codesprint has processed client payments or tested a particular agent framework.

A timeout is not proof that nothing happened

Stripe's error-handling documentation says a network failure can leave you unsure whether the service received or executed a request. It also says a 500 response can be indeterminate: an operation may have produced side effects even though the caller received an error. Treating either result as permission to create a fresh operation can therefore be unsafe. Source

Imagine an agent opening a support ticket. The ticket service commits the record, but the response is lost. The agent sees a timeout and calls the creation tool again. If the second request has a new operation identity, the service may create a second ticket. This is our illustrative scenario, but the failure shape is the same one described in the AWS and Stripe retry guidance. Source

The wrong instruction is "try again until it works." A better application contract is "continue this same operation safely, or establish its outcome before starting another." That distinction belongs in deterministic tool code and persistent state. A model can help explain an uncertain result, but our recommendation is not to make its memory the only record of whether a side effect has already happened.

Separate a request attempt from a business operation

AWS prefers a unique caller-provided request identifier for idempotent APIs. Requests from the same caller with the same identifier can then be treated as duplicates. Its article explains why inferring intent solely from identical parameters is insufficient: two identical resource-creation requests can represent two genuinely desired resources. The caller needs a way to express whether it means a retry or a new action. Source

That creates two useful identities in an agent application. Our suggested operation ID identifies one authorized business action. An attempt ID identifies one execution try. A retry creates another attempt under the existing operation, not another business action. If the user really wants a second ticket, that is a new operation even if its title and description happen to match the first.

Do not use the model's tool-call ID as the sole business identifier. Our concern is that a new planning pass, resumed session or application retry may produce a new invocation while still referring to the same user request. Generate and persist the operation ID in application code, then pass the appropriate provider key through every execution path that implements that operation.

Also bind the operation to the correct tenant and account. AWS describes its idempotent session using the customer identifier together with the client request identifier. The EC2 documentation shows that provider scope can be narrower still: some operations are regional, while others are zonal. Reusing a token in another region or availability zone can represent another effect rather than a duplicate. Source Source

Know the provider's actual contract

Stripe's current documentation distinguishes API v1 and v2 behavior. For API v1, it saves the first status code and response body after endpoint execution begins, including 500 responses. A retry with the same key returns that saved response. API v2 is described differently: successful operations are not repeated, while failed or partially failed work can be retried to completion where possible. Do not assume one universal response-caching rule across every API version. Stripe idempotent requests

For Stripe API v1, keys can be removed after they are at least 24 hours old. Reusing a key after it has been pruned can create a new request. Parameters are compared with the original request, and changed parameters produce an error. The documentation also distinguishes validation failures and conflicts with an executing request, where an idempotent result is not saved because endpoint execution has not begun. Stripe idempotent requests

Our integration checklist therefore asks five concrete questions: what identifies the caller, which endpoints support keys, how long protection lasts, what happens during concurrency, and what changes are rejected. Record the answers for the exact endpoint and version. A field named idempotency_key is not evidence of a permanent, global duplicate-prevention guarantee.

Keep parameters stable across retries

When retrying the same operation, Stripe tells callers to preserve both the idempotency key and parameters within the retry window. Its error guide warns that a new key can start a second operation. If parameters need to change, first establish that the original operation did not succeed and is not still executing. An unknown original outcome needs reconciliation before a replacement begins. Source

Our suggested tool gateway stores a normalized request alongside the operation ID. On a retry, it compares the intended target account, resource, amount or other consequential fields with that stored request. A mismatch is a conflict to resolve, not a reason to silently mint another key. This is particularly important when a model rewrites a description or chooses a different resource during a later attempt.

Authorization is a separate gate. Idempotency can prevent a repeated action; it cannot make the first action permitted. Our recommendation is to bind approval to the reviewed target and consequential parameters, then recheck authorization before execution. A second genuinely requested purchase or message is not authorized merely because the first one used the same tool.

Persist state without pretending to have one giant transaction

AWS notes that recording an idempotency token and performing the related mutations must be atomic in a service implementing that contract. Otherwise the service can record the token without creating the resource, or create the resource without recording the token. Both cases undermine safe retries. That is a server-side design requirement, not something a prompt can enforce. Source

For an application calling an external provider, our proposed pattern is a durable operation record with states such as pending, executing, succeeded, failed and unknown. Store the provider key and returned resource identity. Serialize competing attempts for the same operation. Persist the intent before making the external call, and keep an unknown state when the response cannot establish the result.

A local database transaction does not automatically include the remote payment or ticket API. Our warning is to design explicitly for the gap between the provider action and the local success write. Provider-side idempotency, status retrieval and reconciliation need to work together. Calling the design "exactly once" without defining the boundary hides the very failure case the system has to handle.

Reconcile unknown outcomes instead of inventing certainty

Stripe recommends retrieving the relevant object, inspecting the request in the Dashboard and using webhook events to reconcile an indeterminate result. For API v1, its guidance limits unresolved-operation retries to the first 24 hours using the same key and parameters. If uncertainty remains after that window, it says not to retry with either the old key or a new one; establish the outcome first. Source

Our agent-facing tool response should make that state visible: "outcome unknown, reconciliation required" is different from "failed, nothing changed." Give the agent a read-only status route when available. If the evidence still cannot resolve the action, stop the affected mutation and route it to an operator. Report what is known rather than claiming completion or promising that no side effect occurred.

Reconciliation needs identifiers. Stripe recommends including a local identifier in resource metadata so webhook-delivered objects can be matched to local records, including during incident recovery. Its idempotency documentation also warns against putting sensitive information such as email addresses or personal identifiers into the key itself. Correlation should not require leaking customer data into every log line. Stripe error handling Stripe idempotent requests

Test the failures that create duplicates

This is our suggested test set. First, simulate a provider that commits the action and then drops the response. Repeat the agent's request and verify one external effect. Next, run two workers concurrently against the same operation. Restart the application after the provider succeeds but before local success is stored. Then change a consequential parameter under the same operation ID and confirm a visible conflict.

Add a delayed retry beyond the provider's retention window, an already-executing response and a webhook arriving before the original API response. Finally, request two legitimate identical actions and verify that your duplicate guard does not incorrectly collapse them. The goal is both safety and correct intent, not simply reducing every repeated-looking request to one.

These tests fit within the broader trajectory approach described in our AI agent safety testing guide. For an AI MVP, our software development services page is a starting point for discussing the application layer around tools. Build the retry contract before giving the agent a tool that creates obligations you cannot easily undo.

FAQ

In this guide, it means preserving one intended operation across repeated execution attempts so retries do not create additional side effects. The underlying API principle is safe repetition without effects beyond the original operation. AWS Builders' Library

Not when the original outcome is unknown. Stripe warns that a new key can start a second operation and recommends reconciling the first result before creating a replacement. Stripe error handling

No universal lifetime applies. Stripe API v1 keys may be removed after at least 24 hours, after which reuse can execute a new request. Check the actual provider contract. Stripe idempotent requests

No. Our design recommendation is to enforce both: authorization decides whether the action may happen, while idempotency limits repeated effects from the same operation. Neither substitutes for checking the target account and final state.

Tag:
  • AI agents
  • idempotency
  • API design
  • retries
  • tool calls

Have a project in mind? Let's talk.

Drop Us a Line

Connect with Codesprint Consulting

Ready to take the first step towards unlocking opportunities, realizing goals, and embracing innovation? We're here and eager to connect.

Your Success Starts Here!