Why AI Coding Agents Still Write stripe.charges.create, and How to Stop It
Why AI coding agents still reach for Stripe's legacy Charges API, when current models get it right on their own, how Stripe steers AI tools toward Payment Intents and Checkout Sessions, and how to keep your agent on current Stripe APIs.
Key takeaways
- -Stripe's Charges API is legacy. Stripe recommends Payment Intents or Checkout Sessions for new integrations, and its API reference says creating a charge is no longer recommended.
- -The Charges API is not SCA ready, and Stripe lists features it does not support, including businesses in India.
- -Stripe addresses AI tools directly: its llms.txt and its agent best-practices skill tell models never to recommend the Charges API.
- -Current frontier models often choose Payment Intents unprompted. The remaining risk sits with older or smaller models, codebases that already use Charges, and deprecations too recent for training data.
- -The same pattern applies beyond Charges: Stripe lists the Sources API as deprecated, the Tokens API as outdated, and the Card Element as legacy.
- -Swytchcode has agents pick operations from its catalog, read the real schema before each call, and run only operations you have enabled, so you decide which Stripe operations are available at all.
AI coding agents still write stripe.charges.create because years of tutorials, forum answers, and open-source code used Stripe's Charges API before Payment Intents existed, and models learned from all of it. Stripe now labels the Charges API legacy and recommends Payment Intents or Checkout Sessions for new integrations. Current frontier models often get this right without help, but the old pattern returns with older or smaller models, in codebases that already use Charges, and whenever an agent works from memory instead of current docs. The fix is to name the API you want, give the agent Stripe's current docs or tools, and only allow the operations you intend to use.
This article covers what changed in Stripe's API, why the old call keeps appearing, when current models avoid it, how Stripe itself steers AI tools, and how to keep agents on current Stripe APIs. Checked against Stripe's documentation in October 2026.
What is wrong with stripe.charges.create?
The Charges API creates a payment from a card token in one server call. It predates Strong Customer Authentication (SCA), the European requirement that many online card payments be authenticated by the customer's bank. Stripe's documentation describes the differences:
- Legacy status. Stripe's Charges API page labels it legacy and points new integrations to Payment Intents. The API reference says creating a charge is no longer recommended and that confirming a PaymentIntent creates the Charge object instead.
- No SCA support. Stripe lists the Charges API as not SCA ready. Payment Intents handles 3D Secure and other authentication steps.
- Missing features. The Charges API page lists features it does not support, including businesses in India. Stripe's migration guide says new features are only available with Payment Intents.
- Still running. The same migration guide says Stripe is not deprecating Charges, so existing integrations keep working. The problem is new code that starts on an API Stripe no longer recommends.
// Legacy: Charges API with a card token
const charge = await stripe.charges.create({
amount: 1099,
currency: "usd",
source: "tok_visa",
});
// Current: create a PaymentIntent on the server,
// then confirm it on the client with Stripe.js and the Payment Element
const paymentIntent = await stripe.paymentIntents.create({
amount: 1099,
currency: "usd",
automatic_payment_methods: { enabled: true },
});
// Send paymentIntent.client_secret to the clientFor many apps, a Checkout Session is simpler still. Stripe hosts the payment page and handles authentication, and the server only creates the session and handles the webhook that confirms payment.
Why agents keep writing it
- Volume of old examples. The Charges flow was the standard Stripe integration for years, so it is heavily represented in public code and tutorials.
- It is shorter. One server call looks simpler than a PaymentIntent plus client-side confirmation and a webhook, and a short, complete-looking answer is easy for a model to produce.
- Existing code wins. In a repository that already uses Charges, an agent extends the local pattern. That is usually the right instinct, and here it spreads the legacy API into new features.
- Tokens and Sources come along. Charges code usually brings card tokens and the Sources API with it, which Stripe now lists as outdated and deprecated.
- Nothing fails in test. A Charges call with a test token succeeds, so the gap shows up later, when a customer's bank asks for authentication the integration cannot handle.
Do current models still do it?
Less than they used to. A small 2026 pilot published by Synscribe asked coding agents for a Stripe one-time payment, and every trial chose paymentIntents.create unprompted, with no Charges code. The sample was tiny, a single trial per setup, but it matches what many developers now see: older, widely discussed deprecations are largely absorbed by current frontier models.
The risk has moved. It sits with older and smaller models, local models, agents working inside an existing Charges codebase, and changes recent enough that training data has not caught up. The same pilot found agents producing obsolete configuration for Tailwind CSS v4, a newer change, until a short directive was added to their context.
How Stripe steers AI tools
Stripe treats this as its own problem and publishes guidance written for models:
- llms.txt with instructions. The llms.txt file on docs.stripe.com includes a section of instructions for language model agents, including to never recommend the Charges API and to check the npm registry for the latest SDK version.
- Agent skills. Stripe's best-practices skill in its stripe/ai repository tells agents never to recommend the Charges API, and lists the Sources API as deprecated, the Tokens API as outdated, and the Card Element as legacy.
- MCP server. Stripe's MCP server at mcp.stripe.com includes tools to search API methods, read parameter details, and search documentation. Stripe recommends restricted API keys for agent use.
- Plain-text docs. Adding .md to the end of a docs.stripe.com page URL returns the page as Markdown, which agents can read directly.
How to keep an agent on current Stripe APIs
| Step | What it helps with | Limit |
|---|---|---|
| Name the API in the prompt or rules | Removes the guess between Charges, Payment Intents, and Checkout | Only applies where the rule is loaded |
| Ban legacy calls by name | Stops charges.create, Sources, and Tokens in new code | Rules guide the model without enforcing anything |
| Give the agent current docs | Stripe's llms.txt, .md pages, or MCP server replace memory with current reference | The agent has to actually read them |
| Check the installed SDK version | Matches generated code to the stripe package in package.json | Does not catch legacy patterns that still compile |
| Search the codebase for legacy calls | Finds charges.create and tok_ examples an agent would copy | Manual, and needs repeating |
| Test with 3D Secure cards | Proves the flow handles authentication | Only covers the paths you test |
## Stripe
- New payments: Checkout Sessions, or Payment Intents with the Payment Element.
- Never use stripe.charges.create, the Sources API, the Tokens API, or the Card Element.
- Read package.json for the installed stripe version before writing code.
- Test with a card that requires 3D Secure before calling a payment flow done.Put this in your agent's rules file, such as AGENTS.md, CLAUDE.md, or a Cursor rule.
Where Swytchcode fits
Swytchcode is an execution layer between agents and the APIs they call. When an agent calls Stripe through Swytchcode, it does not write the request from memory. It discovers the operation that matches the job, reads that operation's real schema with swy info, and runs it with swy exec. Every call goes through one pipeline: resolve the tool, validate inputs, evaluate policies, resolve credentials, execute, and normalize the response. Credentials never pass through the model's context.
The project's tooling.json works as an allowlist. Only operations you add with swy add can run, so you decide which Stripe operations are available to the agent at all.
npm install -g swytchcode
swy init --editor=cursor --mode=sandbox
swy doctor
# Find the operation by intent, then enable only what you need
swy discover "accept a one-time card payment" --json
swy get <project>
swy add <canonical_id>
swy info <canonical_id>
swy exec <canonical_id>In sandbox mode, calls go to Stripe's test environment. What happens when the agent reaches for the wrong operation or fields:
- Unknown or not-enabled operations are rejected. An invented canonical ID, or a real one that has not been added to the project, exits with code 2 and no request is sent.
- Invalid inputs fail before the request. A missing required field, a wrong type, or a field the operation does not define exits with code 1 before Stripe sees the request.
- Provider errors come back structured. Each error includes the status code, an error category, and whether it is retryable. A 200 response with an error in the body counts as a failure.
- Changed operations are flagged. swy sync warns when an installed operation's method has changed upstream.
- Payments and refunds can be held. dry_run=true previews the request without sending it. For production, allow and deny rules are available from the Pro plan, and approval rules that route a payment or refund call to Slack or Telegram are available on Business and Enterprise.
Where this does not help
Swytchcode governs the calls an agent executes. It does not rewrite stripe.charges.create in your application code. Legacy calls already in your codebase need the search and rules above, and a migration that follows Stripe's guide. The client-side part of a Payment Intents flow, confirming the payment with Stripe.js, also stays in your application.
Frequently asked questions
Is Stripe's Charges API deprecated?
Stripe labels it legacy and says creating a charge is no longer recommended. Its migration guide says Stripe is not deprecating Charges, so existing integrations keep working, but new features only come to Payment Intents.
Why does AI generate stripe.charges.create?
The Charges flow appears in years of tutorials and open-source code, it is shorter than the Payment Intents flow, and agents copy patterns already present in a codebase.
What should I use instead of stripe.charges.create?
Checkout Sessions for a Stripe-hosted payment page, or Payment Intents with the Payment Element for a custom flow. Both handle authentication such as 3D Secure.
Do current AI models still write Charges API code?
Current frontier models often choose Payment Intents unprompted. Older and smaller models, and agents working in an existing Charges codebase, are more likely to produce it.
How do I stop my agent from using legacy Stripe APIs?
Name the API to use, ban the legacy calls in your rules file, give the agent Stripe's current docs or MCP server, and only enable the operations you intend to use.
Swytchcode resources
- Why AI agents call the wrong API: https://www.swytchcode.com/content/why-your-ai-agent-calls-the-wrong-api-and-how-to-fix-it
- Why Cursor writes outdated API calls: https://www.swytchcode.com/content/cursor-outdated-api-calls
- Replit Agent Stripe integration problems: https://www.swytchcode.com/content/replit-agent-stripe-integration-problems
- Swytchcode CLI commands: https://docs.swytchcode.com/reference/commands/
- Supported APIs: https://www.swytchcode.com/apis
More content
Claude Tool Use Picks the Wrong Tool: Selection Errors vs Argument Errors
Why Claude calls the wrong tool when many similar tools are loaded, how selection errors differ from argument errors, what Anthropic's tool search and strict tool use do, and how to structure tools so selection stays reliable.
Invalid Tool Arguments in the OpenAI Agents SDK: Why the Model Sends the Wrong Parameters
What "Invalid JSON input for tool" means in the OpenAI Agents SDK, what strict mode guarantees and what it leaves out, why schema-valid arguments can still carry wrong values, and how to catch both kinds of error before a call runs.
Lovable and Bolt Apps Break on Third-Party APIs: Wrong Endpoints, Missing Secrets, Deprecated Calls
Why apps built with Lovable and Bolt break when they call third-party APIs, from wrong endpoints and secret name mismatches to keys exposed in the browser and deprecated SDK calls, and how to give the builder the facts it needs to get the integration right.
