
The API Contract Your AI Users Already Believe Exists
An AI generated a polished demo for an API endpoint that did not exist, then its creators shipped compatibility routes instead of arguing with the model. The lesson is bigger than one text-erasure API: generated code is now a form of product telemetry.
Five days ago, an EraseText user reported what looked like a CORS failure in AI-generated code. It was not CORS. It was something more interesting: the model had confidently invented an API.
The generated demo looked plausible. It used a POST request, multipart FormData, an image file, a bearer token, and expected JSON containing an output_url that could be assigned directly to an image element. The endpoint was https://erasetext.com/api/mcp/erase.
None of that exact contract existed.
The real API was hosted elsewhere. Its documented route was /v1/erase, its file field was image_file, authentication used a different header convention, and successful calls returned raw image bytes rather than JSON. The model had also attached MCP to an HTTP route even though EraseText's MCP support is JSON-RPC for agent runtimes, not a browser-facing REST path.
Then the EraseText team did something that sounds wrong until you think about it for a minute: they shipped the hallucinated endpoint.
Not the whole hallucination. That distinction matters. But they made the common wrong guess work.
I think this is one of the clearest examples yet of how AI changes API design. Not because models are suddenly authoritative, and not because every invented interface deserves support. The important part is that generated code is becoming a new kind of demand signal. It tells you what developers, through the statistical instincts of their tools, expect your product to look like.
That signal is messy. It is also valuable.
A hallucination can be a product requirement
For years, API teams have treated documentation as the source of truth and support tickets as a downstream problem. A developer reads the docs, writes a request, gets something wrong, and eventually corrects their implementation. That workflow assumes the developer is looking at your docs before writing the integration.
That assumption is weakening quickly.
A growing number of developers start with an assistant prompt: "Build me a page that removes text from an uploaded image." The assistant produces a page, a fetch call, error handling, and enough UI to look finished. By the time the developer sees your actual documentation, there is already code on their screen. More importantly, there is already a mental model of your API.
The assistant did not retrieve the exact EraseText contract. It filled in blanks using the shape of many other image APIs. POST is normal. multipart/form-data is normal. A field called image is normal. A URL in a JSON response is normal. Calling a route under the product's marketing domain is normal.
That is not random nonsense. It is a prior.
The model guessed wrong about the specific implementation, but its guess was structurally reasonable. And when enough models, prompts, tutorials, and developer habits converge on the same reasonable guess, refusing to acknowledge it can become an expensive kind of correctness.
A repeated wrong guess is no longer just bad generated code. It is evidence of the interface people expect you to have.
There is a temptation to hear that and conclude that APIs should bend to every model hallucination. Absolutely not. That would turn product design into a compatibility landfill. But URL aliases, header aliases, and response wrappers are different from silently pretending your system has features it does not have.
EraseText drew that line well.
It accepted the invented route variants: /api/mcp/erase, /api/erase, /mcp/erase, and /erase. It accepted Authorization: Bearer et_... by translating the token into the expected API-key header. It allowed image alongside the documented image_file field. On the compatibility routes, it transformed the resulting image bytes into JSON with an output_url data URL because that was what the generated browser demo expected.
But it did not accept the invented crop parameters, x, y, w, and h.
That is the correct boundary. A route alias changes where a valid request arrives. A header alias changes how valid credentials are expressed. A response envelope can adapt one representation to another when the underlying operation is unchanged.
Invented crop parameters are different. Accepting them without implementing cropping would mean silently ignoring a caller's instruction. Returning a successful response would be worse than a clear failure because the application would appear to work while doing the wrong thing.
Compatibility is good. Fiction is not.
The CORS diagnosis was a distraction
The other detail worth sitting with is the error report. The browser produced an opaque network error. The assistant called it CORS. That diagnosis is familiar because it is often plausible and sometimes correct.
But the preflight request passed. The subsequent POST failed because it went to a static marketing site at an endpoint that did not exist and never reached an API service. From the browser's perspective, the difference was not especially helpful. From an engineering perspective, it was everything.
"CORS" has become the new "DNS" in debugging conversations: a compact word people reach for when a browser request disappears into a fog of inaccessible details. AI assistants are particularly likely to stop there because CORS is a common pattern and browser failures are deliberately opaque.
The practical lesson is not that CORS is unimportant. The practical lesson is that a CORS-shaped client error is not a root cause.
When an assistant says CORS, inspect the actual request path and the actual host first. Check whether that host owns the route. Check whether the request reached the expected service. Check the status and logs where the request terminates. Only then start changing headers, adding a proxy, or loosening an origin policy.
The generated demo recommended a Node proxy to work around the supposed CORS issue. That is a classic example of an AI producing a technically familiar fix for a problem it has not actually established exists. A proxy might have hidden the bad routing by moving the request server-side, but it would have increased complexity while preserving the underlying false assumption about the API contract.
This is why generated code needs the same review instinct we apply to copied snippets: do not evaluate it by how complete it looks. Evaluate the assumptions it smuggles in.
APIs now have two audiences
The traditional audience for an API is a human developer. That developer needs clear docs, examples, authentication guidance, reliable errors, and a stable contract.
The second audience is now an AI system that may be writing code on the developer's behalf.
Those audiences overlap, but they do not consume information the same way.
Humans can navigate a documentation site, compare examples, notice caveats, and ask questions. Models work from what they can retrieve, what they were trained on, what nearby context says, and what appears statistically likely. If the contract is hard to discover or hard to state concisely, the model fills the gap with convention.
That is why the response from EraseText was not limited to aliases. The team also published a machine-readable OpenAPI document, an llms.txt file with plain-language guidance for browser demos, a hosted playground intended to be copied, and an MCP handshake instruction directing webpage-generation use cases toward the HTTP form-data endpoint rather than the JSON-RPC endpoint.
That combination is sensible because it attacks the problem at multiple points.
OpenAPI gives tools a formal description of endpoints, fields, and responses. A plain-language file can explain the distinction that schema alone does not fully capture, such as why a browser demo should call one interface while an agent runtime uses another. A playground offers a working reference implementation. An MCP handshake message reaches an agent at a moment when it is actively choosing how to integrate.
None of these guarantees that a model will stop guessing. Models will still guess. Developers will still paste incomplete prompts into chat windows. Documentation will still go stale. But the goal is not perfection. The goal is to make the canonical path easier to discover than the plausible fake one.
That is a different operational standard from "our docs exist."
Treat generated code like observed traffic
The strongest idea in this story is not the compatibility route itself. It is the framing: failing generated code is traffic data.
That does not mean every screenshot should become a roadmap item. It means failures should be classified before they are dismissed.
If one person manually mistypes a route, correct the typo. If assistants repeatedly generate the same route pattern, inspect why. Is the route close to your documented naming? Is the API on a subdomain while the product is on the apex domain? Does your authentication scheme diverge from a common convention without a compelling reason? Is your successful response technically efficient but awkward for the most common browser use case?
Those questions are product questions, not merely support questions.
A URL alias is often cheap. A routing rule at the edge can make a familiar path reach the same upstream service. A header normalization layer can accept a conventional bearer format without weakening the real credential validation. A compatibility response can be restricted to routes intended for browser demos while preserving the raw-byte response that works better in pipelines.
The cost is not always zero, though. Every alias becomes an interface you need to own. It needs tests, observability, rate-limit behavior, authentication behavior, error behavior, and a decision about how long it remains supported. The answer is not to create dozens of undocumented variants blindly.
Instead, make compatibility intentional.
A good compatibility layer has a few properties:
- It maps to an existing operation with identical meaning.
- It does not silently discard caller intent.
- It preserves authentication and authorization requirements.
- It is observable, so you can measure whether the route is solving a real recurring problem.
- It has one canonical contract in the documentation, even when alternate entry points remain supported.
That final point prevents a common failure mode. Once compatibility aliases exist, teams can accidentally make their contract more confusing by documenting every historical route equally. The canonical route should remain obvious. The aliases are there to make mistakes survivable, not to make the API ambiguous.
Do not let AI average away your product decisions
There is a counterargument here: perhaps models should be forced to learn the actual API. Perhaps accepting their guesses rewards bad behavior and encourages sloppy integrations.
I understand the instinct. APIs should not be designed by autocomplete.
But there is a difference between preserving meaningful product decisions and preserving incidental friction. If a service has a real reason to return image bytes, keep returning image bytes on the canonical endpoint. If an operation does not support cropping, return a clear error for crop arguments. If MCP is JSON-RPC in your system, do not pretend that an HTTP path with mcp in it is actually MCP.
Those are semantic decisions. They define what the product does.
Whether the same operation can also be reached via /api/erase rather than /v1/erase is often not semantic. Whether a bearer token can be normalized into the same validated API key is often not semantic. Whether a browser-demo-oriented compatibility path can return a JSON data URL rather than force generated frontend code to decode raw bytes is often not semantic.
Engineers should be ruthless about that difference.
A lot of bad platform design comes from treating every rough edge as sacred because it is technically documented. Documentation does not automatically make a choice good. Sometimes it only records the order in which a system happened to be built.
The AI angle makes this more visible because models are excellent at averaging conventions. That average can be wrong for your product. It can also expose where your product is needlessly surprising.
The job is not to obey the average. The job is to know when the average is telling you something useful.
The new API ergonomics question
For API owners, I would add one question to integration review: what would a competent assistant guess if it had only our homepage, product name, and a one-sentence description?
Would it guess the right host? The right endpoint family? A reasonable authentication format? The right request shape? The right response shape for a browser app?
Then ask a harder question: which wrong guesses are harmless enough to support, and which would corrupt the meaning of the API?
That exercise will surface issues that normal documentation reviews miss. It forces you to distinguish between the contract your team knows and the contract your ecosystem infers.
The EraseText response is a useful pattern because it is neither surrender nor denial. They did not claim the model was correct. They did not patch a prompt, because they correctly recognized that another model could make the same inference tomorrow. They adjusted the system at the point they controlled: their own domain, routing, header handling, and response adaptation.
That is the adult response to AI-generated code. Verify it. Correct it. Instrument the recurring failures. Then decide whether the model's mistake is truly invalid, or whether it has uncovered an unclaimed route between your product and the way people now try to use it.
The APIs that hold up in this environment will still have precise contracts. They will just understand that precision and forgiveness are not opposites.