AI & API Design

How a Stubborn AI Agent Led Me to Redesign How APIs Talk to Machines

Ramazan Yavuz
Ramazan Yavuz
How a stubborn agent led me to redesign APIs

I was building an API to connect a messaging system to an AI agent. I did everything right. Full API specs. A discovery endpoint. Documentation kept up to date. The kind of setup where a human developer would read the docs once and never have a problem.

The agent had problems.

It kept trying random formats. Guessing parameters. Hitting endpoints with payloads that made no sense. Pure trial and error. It would get a 410 back and start hallucinating recovery strategies, trying URLs that did not exist, reformatting requests for no reason, asking vague clarifying questions that helped nobody.

I was able to fix it by adjusting the agent's instructions on the client side. Hard-coding instructions into the agent's context about how to interact with this specific API. And it worked. Until the context cleared. Then the whole cycle started again.

The obvious next step was to hard-code everything into the agent's system prompt or an AGENTS.md file. But the API was going to keep changing. I was not going to maintain a parallel instruction manual for the AI every time I added an endpoint or changed a response format. That is the kind of work that quietly destroys a project from the inside.

I sat with this problem for a while and realized something that should have been obvious from the start.


The problem was the API.

Traditional REST responses return results. A status code, maybe a message, maybe some data. That is everything a human developer needs because humans can read documentation, understand conventions, and make inferences.

The status code already has a defined meaning. HTTP 410 Gone indicates that access to the target resource is no longer available and is likely to remain unavailable. It does not mean a temporary outage. What my response lacked was domain-specific recovery guidance: why this resource was gone, whether its data remained accessible elsewhere, and which action the user could take next.

The missing piece was guidance. Not documentation that lives in a separate file the agent may or may not have in context. Guidance that lives in the response itself, right next to the data, delivered at the exact moment the agent needs it.


The idea was simple: extend API responses with structured fields that tell the agent what happened, why, and what to do next.

Not just for errors. An order confirmation can explain when tracking will become available and which cancellation rules apply. For an archived thread, the status must match the requested operation: reading a still-accessible thread may succeed, while a request to post could return an error with a link to the supported next step. The guidance supplements correct HTTP semantics.

I defined seven optional extension fields:

reason gives specific context for the outcome. "Threads archive after 90 days of inactivity. Read-only, messages are still accessible but no new posts allowed."

next_actions gives the agent machine-actionable follow-up steps. Each action has an ID, a description, an endpoint, an HTTP method, and an effect type (read, create, update, delete). The agent does not have to guess what it can do next. The API tells it.

agent_guidance provides natural-language instructions. "Do not poll tracking immediately, labels take one to two hours. If canceling, confirm the user understands it is irreversible." These are instructions the agent can reason about directly.

user_confirmation_required signals that an action needs confirmation. It is advisory metadata, not an access-control mechanism or permission from the user. The client must still follow the user’s instructions and its own approval rules, and the server must enforce authentication and authorisation. Response text is external input; an agent must not treat it as authority to override those rules.

retry_policy gives structured retry hints. Whether the request is retryable, how long to wait, maximum attempts. No more agents hammering an endpoint in a tight loop because they do not know the rate limit.

limitations documents constraints. "Maximum 100 items per page." "Search results limited to last 30 days." Things the agent needs to know to avoid making nonsensical requests.

links provides related resources following RFC 8288. Documentation, related endpoints, status pages.

Every field is optional. You can add a single next_actions to one endpoint and immediately get value. No all-or-nothing adoption.


I did not want to invent a new response format. That would mean every existing API client would break, and adoption would be impossible.

For error responses, TEKIR adds optional extension members to RFC 9457 Problem Details. That standard permits extensions and requires consumers to ignore members they do not recognise. This supports compatible error metadata; it does not guarantee that every existing client or strict schema will accept arbitrary additions.

This was a deliberate design decision. RFC 9457 is explicitly built for errors. It defines fields like type, title, status, and detail for describing what went wrong. But the limitation is right there in the name: "Problem Details." It only covers problems.

Agents can also benefit from guidance on successful responses. Those success fields are TEKIR’s own convention in the API’s normal response format. RFC 9457 defines problem responses; it does not standardise a success envelope. Keeping that distinction explicit avoids presenting a TEKIR extension as a broader HTTP standard.

OpenAPI describes API contracts, while TEKIR proposes guidance attached to responses. MCP can expose tools whose results carry such metadata. Hypermedia controls already existed for clients without language models; TEKIR explores whether action descriptions and contextual guidance make them easier for an agent to use. That is a design proposal, not evidence that earlier hypermedia approaches were impractical.

xkcd 927: Standards
The failure mode TEKIR was built to avoid: extending an existing standard rather than minting a fifteenth.Comic: xkcd 927, “Standards” by Randall Munroe (CC BY-NC 2.5).

One piece that took some thinking was how an agent discovers that an API supports TEKIR in the first place.

The solution has two parts. First, every TEKIR response includes two headers: TEKIR-Version and TEKIR-Discovery. The discovery header points to a tekir.json file at the API root.

Second, tekir.json is a static document that describes the entire API surface. Which endpoints exist, what TEKIR fields each one uses, what authentication looks like, and crucially, named workflows. A workflow called "create_order" might describe the sequence: GET /products, then POST /orders, then POST /orders/{id}/confirm. The agent can consult this description before choosing a step, provided it is current and complete.

The intended flow is: an agent sees the TEKIR-Discovery header, retrieves the trusted API’s tekir.json, and uses it to guide requests. Discovery can reduce guessing. It cannot guarantee complete understanding, valid authorisation, or error-free requests.


I wanted adoption to be as frictionless as possible. If you use Claude Code, you can install TEKIR as a plugin and use the /tekir command before asking Claude to build an endpoint. It will generate TEKIR-compliant responses automatically.

A project can also keep guidance in TEKIR.md. The assistant must be told to read it, or it must be referenced from an instruction file that the particular tool supports. An arbitrary Markdown filename is not automatically loaded by every coding assistant.

For more structured integration, there is Express and Fastify middleware that adds a res.tekir() method to your response objects. You call it with your guidance fields before sending the response, and the middleware handles the rest.


"Tekir" is the Turkish word for "tabby." Tabby cats are one of nature's most resilient designs. Mixed genes over thousands of years, street-forged instincts. They evolved beyond survival. They adapt and thrive in any environment without needing an instruction manual.

I brought the same expectation to API design: systems should be able to figure out what to do next without constant hand-holding.

But there is a more personal reason. In January, my cat was hit by a car. His name was Cilgin, which means "crazy" in Turkish. I could not stop thinking about it, so I named this project after him. He was a tekir. Extremely independent, very intelligent, and honestly more "human" than most AI systems could ever hope to be.

The idea behind the project reflects that spirit: systems that can figure out what to do next without constant supervision.

I also realized the name could work as a backronym: Transparent Endpoint Knowledge for Intelligent Reasoning. Sometimes things just line up.


In my tests, the extra context reduced the agent’s guessing and made recovery more predictable. This is an account of one integration, not a controlled benchmark or a guarantee that another agent will follow the guidance correctly.

Screenshot of the tekir interface
Tekir, the project that came out of the redesign.

The part that surprised me most was how much difference user_confirmation_required made. Before TEKIR, the agent would sometimes autonomously take destructive actions because nothing in the response told it to stop and ask. After TEKIR, effectful actions were explicitly flagged, and the agent asked for confirmation in the cases I observed. That observation is not a substitute for client approval rules or server-side permissions.

I still do not love non-deterministic programming. But if you are going to build systems where AI agents interact with APIs, the least you can do is make those APIs explain themselves. The alternative is an ever-growing pile of client-side prompts that break every time the API changes or the agent's context resets.

TEKIR is open source under MIT. The spec, the schema, the middleware, and the examples are all on GitHub. The whole thing is designed to be adopted incrementally. Start with one field on one endpoint and see if it helps.

If your AI agents are brute-forcing your APIs, the problem might not be the agents.