<?xml version="1.0" encoding="utf-8"?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     category="info"
     docName="draft-gaikwad-agent-friendly-http-api-profile-00"
     ipr="trust200902"
     submissionType="IETF"
     consensus="false"
     version="3"
     tocInclude="true"
     sortRefs="true"
     symRefs="true">

  <front>
    <title abbrev="Agent-Friendly HTTP API Profile">Design Considerations and Profile for HTTP APIs Consumed by AI Agents</title>
    <seriesInfo name="Internet-Draft" value="draft-gaikwad-agent-friendly-http-api-profile-00"/>

    <author fullname="Madhava Gaikwad" initials="M." surname="Gaikwad">
      <organization>Independent</organization>
      <address>
        <postal>
          <city>Bengaluru</city>
          <country>India</country>
        </postal>
        <email>gaikwad.madhav@gmail.com</email>
      </address>
    </author>

    <date year="2026" month="June" day="9"/>

    <area>General</area>
    <keyword>AI agents</keyword>
    <keyword>API design</keyword>
    <keyword>tool calling</keyword>
    <keyword>Model Context Protocol</keyword>
    <keyword>OpenAPI</keyword>
    <keyword>HTTP</keyword>

    <abstract>
      <t>AI agents are a fast-growing kind of HTTP API client. A human developer
      reads an API's documentation once and then writes code that calls it. An
      AI agent works from the machine-readable description each time it plans a
      step. It works within a limited amount of text it can hold at once. It
      retries often. It picks operations by matching their descriptions to a
      goal. An API built mainly for human developers can lead an agent into
      failures that are easy to avoid, such as repeating a write, running out of
      room for the task, or getting stuck on an error it cannot recover
      from.</t>
      <t>This document lists the properties that make an HTTP API easy for AI
      agents to use, including APIs reached through tool-calling layers such as
      the Model Context Protocol. It gathers these properties into a profile of
      existing HTTP and API-description mechanisms, with a checklist at the end.
      It covers the API and its description. It does not define new ways to
      identify, authenticate, or authorize an agent. That work is happening
      elsewhere and is referenced here for context.</t>
    </abstract>
  </front>

  <middle>

    <section anchor="introduction" numbered="true">
      <name>Introduction</name>

      <t>Autonomous software agents built on large language models are starting
      to call HTTP APIs. Sometimes the agent forms the HTTP request itself. More
      often it goes through a tool-calling layer that presents each API operation
      as a callable "tool". The Model Context Protocol <xref target="MCP"/> is one
      common example of such a layer. Many of its servers are generated
      automatically from an existing API description such as an OpenAPI document
      <xref target="OPENAPI"/>.</t>

      <t>One property matters most. The description of the API, and the data the
      API returns, both become input to the agent's own reasoning. Several single
      points in this document are also ordinary good API practice: stable
      operation names, bounded pages, structured errors, idempotency. This
      document explains why each one matters more when the client is an agent. It
      adds the guidance that is specific to agents. It collects the result as a
      profile that a provider can apply and check against. This document does not
      assume that an agent is reliable or well-behaved. It treats the agent as a
      client whose behavior is shaped by the shape of the API.</t>

      <section anchor="two-layers" numbered="true">
        <name>Two Layers</name>
        <t>There are two layers to keep separate. The API layer is the HTTP API
        and its machine-readable description. The tool layer is the surface that a
        tool-calling protocol or generator builds from that description. This
        document is about the API layer. A good API layer is the most reusable
        way to get a good tool layer. The tool layer is increasingly handled by
        its own protocols.</t>
      </section>

      <section anchor="api-styles" numbered="true">
        <name>API Styles</name>
        <t>The advice here is written for resource-oriented HTTP APIs, where each
        operation acts on an identified resource. Most of it also applies to
        query-based APIs such as GraphQL and to remote-procedure-call styles such
        as gRPC. A few points change. A query-based API lets the client ask for
        exactly the fields it needs, which handles the over-fetching concern in
        <xref target="responses"/> on its own. In return it adds query-cost and
        query-depth concerns that an agent-facing deployment SHOULD limit. A
        reader using this document with another style should map each point to the
        matching idea in that style.</t>
      </section>

      <section anchor="scope" numbered="true">
        <name>Scope and Non-Goals</name>
        <t>This document is informational. It lists considerations, gives
        non-normative recommendations, and assembles a profile and a checklist. It
        does not define a protocol or a wire format. These topics are out of
        scope:</t>
        <ul spacing="normal">
          <li>How an agent proves its identity, authenticates, or is granted
          authority by a user. That work is active in the IETF, including OAuth
          2.0 Token Exchange <xref target="RFC8693"/>, on-behalf-of authorization
          for agents <xref target="I-D.oauth-ai-agents-obo"/>, and wider
          frameworks <xref target="I-D.klrc-aiagent-auth"/>. <xref
          target="identity"/> explains the connection.</li>
          <li>The internal design of tool-calling protocols, including how those
          protocols define tools, resources, and prompts.</li>
          <li>Agent-side topics such as planning, reasoning traces, and model
          evaluation.</li>
        </ul>
      </section>

      <section anchor="conventions" numbered="true">
        <name>Requirements Language</name>
        <t>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
        "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
        "OPTIONAL" in this document are to be interpreted as described in BCP 14
        <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when,
        they appear in all capitals, as shown here. This document is
        informational. These words give guidance to API authors. They are not
        protocol requirements.</t>
      </section>
    </section>

    <section anchor="terminology" numbered="true">
      <name>Terminology</name>
      <dl newline="false" spacing="normal">
        <dt>Agent:</dt>
        <dd>An autonomous software system, usually built on a large language
        model, that picks and calls API operations to reach a goal.</dd>
        <dt>Tool:</dt>
        <dd>An API operation as it is shown to an agent by a tool-calling layer.
        It has a name, a short description, and an input schema.</dd>
        <dt>API description:</dt>
        <dd>A machine-readable definition of an API, such as an OpenAPI document
        <xref target="OPENAPI"/>, that a tool layer can be built from.</dd>
        <dt>Context window:</dt>
        <dd>The limited amount of text the agent's model can hold and read at one
        time. Operation descriptions and response bodies use up this space.</dd>
        <dt>Affordance:</dt>
        <dd>A machine-readable part of a request or response that tells the agent
        what it can or should do, in a form it can act on without reading
        prose.</dd>
      </dl>
    </section>

    <section anchor="consumption-model" numbered="true">
      <name>How an Agent Uses an API</name>
      <t>The advice below follows from a few facts about how an agent uses an
      API. Stating them shows where the advice applies.</t>
      <ul spacing="normal">
        <li>The description is input. The agent is shown operation names,
        descriptions, and schemas while it plans and picks a tool. Their wording
        decides which operation it picks and how it fills in the arguments.</li>
        <li>Selection is by similarity. The agent picks an operation because its
        description reads close to the goal. It does not follow fixed logic. Two
        operations that read alike cause wrong-operation and wrong-argument
        mistakes.</li>
        <li>Space is limited. Descriptions and responses share one budget of text
        with the task itself. Large or low-value payloads crowd out the task and
        make later choices worse.</li>
        <li>Retries are common. An agent retries after a timeout or an unclear
        result. It may also send a request again as its plan changes.</li>
        <li>Writes are easy to trigger. An agent can call an operation that
        changes state while it is exploring. So an unintended or repeated write
        costs more than it would for a human-driven client.</li>
      </ul>
      <t>These facts apply most directly when operation descriptions, schemas,
      error messages, and response bodies reach a language model or a model-guided
      planner. When something in between filters, checks, or shortens the data
      before the model sees it, the space and selection effects shrink but do not
      disappear. The retry and side-effect effects usually remain.</t>
    </section>

    <section anchor="affordances" numbered="true">
      <name>Prefer Signals the Agent Can Act On</name>
      <t>This is the main idea of the document. An agent-facing API SHOULD give
      the agent machine-readable signals it can act on. It SHOULD avoid relying on
      prose that the agent has to read and interpret. A sentence such as "call
      this carefully" or "try again later" has to be understood by the model, and
      the model follows such sentences unevenly. The same intent stated as a field
      or a flag can be acted on directly. This swap shows up throughout the
      profile:</t>
      <ul spacing="normal">
        <li>an error carries a <tt>retryable</tt> flag the agent can read (<xref
        target="errors"/>);</li>
        <li>a response carries links to the operations that are valid next steps
        (<xref target="responses"/>);</li>
        <li>a write accepts a dry-run flag that previews its effect (<xref
        target="idempotency"/>);</li>
        <li>a schema lists allowed values as a fixed set (<xref
        target="description"/>);</li>
        <li>a high-risk operation carries metadata that says it needs confirmation
        (<xref target="idempotency"/>).</li>
      </ul>
    </section>

    <section anchor="description" numbered="true">
      <name>The API Description Is the Contract</name>
      <t>When an API is reached through a generated tool layer, its
      machine-readable description is the single source the tools are built from.
      Anything missing from the description is missing from the tool. So treat the
      description as a main deliverable. Do not treat it as documentation written
      after the code.</t>
      <ul spacing="normal">
        <li>Give every operation a stable, meaningful identifier, for example the
        OpenAPI <tt>operationId</tt>. Identifiers generated from the URL path
        produce tool names the agent picks badly.</li>
        <li>Give each operation a short summary that sets it apart from similar
        ones. Give it a longer description that says when to use it and when not
        to. Wording such as "Use this when ..." and "Do not use this when ..."
        works well. It heads off the wrong-operation mistake from <xref
        target="consumption-model"/>. <xref target="app-descr"/> shows this.</li>
        <li>Put the most important information first in the description. The agent
        may not read all of it.</li>
        <li>State side effects in the description. Do not leave them implied by
        the HTTP method.</li>
        <li>Type and describe every parameter and response field. List allowed
        values as an enumeration, which is a fixed set of permitted values. Set
        <tt>additionalProperties</tt> to false on objects, or use the equivalent,
        so the agent cannot add invented parameters. Include a real example.</li>
        <li>Put idempotency support, pagination limits, field-selection or
        verbosity controls, and deprecation status in the description itself. The
        agent acts on the description. Guidance kept only in human documentation
        does not reach it.</li>
        <li>Keep the description short and free of repeated prose. Its text uses
        up the agent's space budget.</li>
        <li>Test the description by running an agent against the API and reading
        where it picks the wrong operation or fills in a bad argument. These
        wording problems are hard to predict any other way.</li>
      </ul>
    </section>

    <section anchor="predictability" numbered="true">
      <name>Be Consistent and Predictable</name>
      <t>An agent learns patterns and reuses them. Anything inconsistent across
      the API makes it guess wrong. Keep naming, identifier formats, pagination,
      error shape, and authentication the same across the whole API. For example,
      a status value that is <tt>pending</tt> on one operation, <tt>IN_PROGRESS</tt>
      on another, and <tt>done</tt> on a third will cause errors. When an
      operation only works in certain states of a resource, document the states
      and the moves between them. Then the agent can work out which operations are
      valid without guessing. Keep enumerated values stable. The agent may have
      seen and remembered them in an earlier call.</t>
    </section>

    <section anchor="operations" numbered="true">
      <name>The Set of Operations You Expose</name>
      <t>A complete resource-oriented API does not become a good tool surface on
      its own. Turning every endpoint into its own tool gives the agent a surface
      that is large, low-level, and hard to work through.</t>
      <ul spacing="normal">
        <li>Offer composite operations that finish a whole unit of work in one
        call, where the steps behind them are common, safe, and well-bounded. This
        saves the agent from chaining several calls, where each call is a separate
        reasoning step. Keep resource meaning intact where you can. Keep
        authorization, audit, and partial-failure behavior visible.</li>
        <li>Curate the operations you expose. A small set of well-described
        operations serves the agent better than a large set it has to sort
        through.</li>
        <li>Some operations need extra care before an agent should reach them: the
        ones that take opaque binary input, change infrastructure, or cause
        effects that cannot be undone. Expose these to a general-purpose agent
        only with extra controls, such as a clear schema, a preview, narrow
        authorization, a confirmation step, or human approval. Do not expose
        deprecated operations at all.</li>
        <li>Offer batch operations that do several related actions in one call. A
        batch operation SHOULD report one result per item, so the agent can see
        which items succeeded and which failed.</li>
        <li>When a value can be found by a human-meaningful name, accept that name
        or offer a lookup operation. Do not force the agent to supply an opaque
        identifier it has no way to get.</li>
      </ul>
    </section>

    <section anchor="responses" numbered="true">
      <name>Keep Responses Small</name>
      <t>A response uses up the agent's space budget on the turn it arrives, and
      on every later turn it stays in view. Asking for too much costs more each
      turn. A response that is too large is also a denial-of-service and cost
      problem (see <xref target="security"/>).</t>
      <ul spacing="normal">
        <li>Return the fields that carry signal. Drop low-level details the agent
        will not use. When clients need different amounts of data, support field
        selection or a verbosity setting. <xref target="app-response"/> shows
        this.</li>
        <li>Support conditional requests on reads, for example an entity tag (an
        ETag value) with the matching conditional header. Then an agent that asks
        for the same data again can be told it has not changed. It does not have
        to receive and re-read an identical payload.</li>
        <li>Set a bounded default page size on collections. Document how the agent
        can narrow the results before it fetches them. A large or open-ended
        default makes the agent spend its space budget on low-value items, and it
        can make the agent's next tool choice worse.</li>
        <li>Use cursor-based pagination. A cursor is an opaque token the server
        returns that marks where the next page starts. Return a ready-to-use
        continuation, either that cursor or a complete <tt>next_page</tt> link, so
        the agent does not have to compute offsets or build URLs. It does both of
        those unreliably.</li>
        <li>Give collections a stable, documented order. Results that change order
        between calls confuse the agent.</li>
        <li>Make values self-describing when the type alone is unclear. For
        example, return a money amount together with its currency, not as a bare
        number. Return a human-readable label next to an identifier where you
        can.</li>
        <li>Include links to the operations that are valid next steps on a
        returned resource. Then the agent can see the available moves and does not
        have to guess them.</li>
      </ul>
    </section>

    <section anchor="errors" numbered="true">
      <name>Make Errors Recoverable</name>
      <t>An error response is not the end of the story. It is the input to the
      agent's next decision. An error that is unstructured or unclear leads the
      agent to retry something that cannot work, or to give up on something a
      retry would have fixed.</t>
      <t>Use a structured, machine-readable error format such as Problem Details
      for HTTP APIs <xref target="RFC9457"/>, which is a standard JSON shape for
      errors. Include a stable error code that is separate from the HTTP status
      code.</t>
      <t>Problem Details lets you add your own fields. For an agent-facing API,
      say plainly whether a failed call can be retried, using a field. Do not
      leave the agent to read this off the status code. Report every validation
      problem at once, with the field name for each one. A small, consistent set
      of fields across the API does the job: a boolean <tt>retryable</tt>, an
      optional retry-delay hint, and a list of field errors. With these the agent
      can decide to retry, to change the request, or to stop, without guessing.
      The field names here are only examples. This document does not register
      them. That would be the job of a later specification (see <xref
      target="iana"/>). Here is a retryable rate-limit error:</t>
      <figure anchor="ex-retryable">
        <name>Error response with an explicit retry indication</name>
        <sourcecode type="http-message"><![CDATA[
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json

{
  "type": "https://api.example.com/problems/rate-limited",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "Request rate exceeded for this credential.",
  "code": "RATE_LIMITED",
  "retryable": true,
  "retry_after": 30
}
]]></sourcecode>
      </figure>
      <t>A validation error sets <tt>retryable</tt> to false and lists the fields
      that were wrong. That tells the agent to change the request before it sends
      it again:</t>
      <figure anchor="ex-validation">
        <name>Validation error that should not be retried unchanged</name>
        <sourcecode type="http-message"><![CDATA[
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://api.example.com/problems/validation",
  "title": "Invalid request",
  "status": 422,
  "code": "VALIDATION_FAILED",
  "retryable": false,
  "field_errors": [
    { "field": "assignee_email", "code": "INVALID_FORMAT",
      "detail": "Not a valid email address." },
    { "field": "priority", "code": "NOT_IN_ENUM",
      "detail": "Must be one of: low, medium, high." }
  ]
}
]]></sourcecode>
      </figure>
    </section>

    <section anchor="idempotency" numbered="true">
      <name>Make Writes Safe to Repeat, Preview, and Undo</name>
      <t>Agents retry after timeouts, after unclear results, and as their plan
      changes. So safe writing for agents covers more than one idea. It covers
      repeating a write safely, previewing it, confirming it, cancelling it, and
      undoing it. As a rule, prefer writes that can be previewed, cancelled, and
      reversed over writes that take effect at once and cannot be undone.</t>
      <ul spacing="normal">
        <li>Support a client-supplied idempotency key on operations that have side
        effects. Idempotency means that sending the same request twice has the
        same effect as sending it once. With a key, a retry that carries the same
        key returns the first result and does not run the operation again. The
        Idempotency-Key HTTP header field <xref target="I-D.httpapi-idem"/> is one
        way to do this. Document the mechanism, the length of time a key is
        honored, and the scope in which a key must be unique. These decide whether
        a retry is safe.</li>
        <li>Tell a settled result apart from a temporary failure in what you store
        for a key. A settled result is safe to return again. A temporary server
        error is not, and returning it again as if it were final is a known trap.
        Reject a request that reuses a key with a clearly different body. Use a
        conflict status.</li>
        <li>For operations with large or irreversible effects, offer a preview, or
        dry-run, mode. It reports what the operation would do without doing it. In
        a resource-oriented API a request header (for example, <tt>X-Dry-Run:
        true</tt>) or a query parameter (for example, <tt>?dry_run=true</tt>)
        works well. The response lists the predicted changes. <xref
        target="app-dryrun"/> shows this.</li>
        <li>Mark high-risk operations with explicit metadata, for example a flag
        that the operation cannot be undone or that it needs confirmation. You can
        also require a separate confirmation step outside the request. The point
        is that the signal is machine-readable, not buried in warning text.</li>
      </ul>
    </section>

    <section anchor="ratelimit" numbered="true">
      <name>Signal When to Slow Down</name>
      <t>Agent traffic can come in bursts. It often lacks the natural pacing of a
      person at a keyboard. Signal clearly when the client should slow down, so a
      well-behaved agent can hold back on its own.</t>
      <ul spacing="normal">
        <li>When you reject or delay a request for rate limiting or a temporary
        outage, include a retry-delay hint such as the <tt>Retry-After</tt> header
        field. A client should follow it ahead of its own backoff timer.</li>
        <li>You MAY measure limits in units that fit agent traffic, not a flat
        request count. One agent task can turn into many calls and large
        responses. Counting by response size, or by a cost unit you define, can
        match real load better. Document the unit so the agent can track what it
        has left.</li>
      </ul>
    </section>

    <section anchor="async" numbered="true">
      <name>Long-Running Operations</name>
      <t>An operation that takes more than a few seconds does not fit a single
      blocking request. The connection may not stay open. An agent that waits for
      it cannot do anything else in the meantime.</t>
      <ul spacing="normal">
        <li>Acknowledge a long-running operation at once with the right HTTP
        status <xref target="RFC9110"/> (often 202 Accepted). Return an identifier
        for the operation and a link to a status resource the client can
        check.</li>
        <li>Have the status resource report a clear state, and on completion point
        to the finished resource. Include a retry-delay hint and say how often to
        poll. Allow the client to cancel an operation that has not finished.</li>
        <li>When the client can receive callbacks, you MAY send a notification
        when the operation finishes, in place of polling. Authenticate the
        notification, protect it against replay, and give it an identifier the
        receiver can use to drop duplicate deliveries.</li>
        <li>When an operation produces output piece by piece, you MAY stream the
        partial output, for example over server-sent events. Then the agent can
        start acting on early results before the operation finishes.</li>
      </ul>
    </section>

    <section anchor="versioning" numbered="true">
      <name>Changing the API Over Time</name>
      <t>The agent's behavior comes from the API description. So a change to the
      description is a change to the agent's tools. Manage change carefully.</t>
      <ul spacing="normal">
        <li>Prefer changes that keep working for existing clients, such as adding
        an optional parameter or a new operation. Save a new version for a change
        that would break an existing client, such as removing a field, changing a
        type, or making an optional parameter required.</li>
        <li>Do not change what an operation means while keeping its identifier the
        same. An agent that learned the operation once will use it again and
        expect the old behavior.</li>
        <li>Signal deprecation in machine-readable form, for example the
        <tt>Deprecation</tt> and <tt>Sunset</tt> header fields and a link to the
        replacement. Show it in the API description too. Release notes on their own
        do not reach the agent.</li>
        <li>Detect breaking changes automatically by comparing each new version of
        the API description against the last one.</li>
        <li>Choose one way to express versions and use it across the whole API.
        Mixing several versioning styles makes the API harder for the agent and
        for people to follow.</li>
      </ul>
    </section>

    <section anchor="discovery" numbered="true">
      <name>Making the API Easy to Discover</name>
      <t>An agent does best with one machine-readable, low-noise place to learn
      what the API can do. A complete API description <xref target="OPENAPI"/> is
      the main artifact for this. Some providers also publish a short, plain-text
      index of their documentation aimed at models. The llms.txt convention <xref
      target="LLMSTXT"/> is one community example, and it is not a standard.
      Generate any such file from the same source as the human documentation, so
      the two do not drift apart. Keep it focused on parameters, the meaning of
      errors, and authentication.</t>
    </section>

    <section anchor="observability" numbered="true">
      <name>Observability</name>
      <t>After an agent runs, an operator often needs to work out what it did. To
      let the agent's steps line up with the API's own logs, accept and pass along
      a standard correlation identifier. The W3C Trace Context <tt>traceparent</tt>
      header field <xref target="W3C-TRACE-CONTEXT"/> is one such identifier, and
      a generic correlation header is another. Record it in the API's logs next to
      the identity that acted. This helps with debugging, with working out cost,
      and with the audit trail in <xref target="security"/>.</t>
    </section>

    <section anchor="identity" numbered="true">
      <name>How This Relates to Identity and Authorization</name>
      <t>This document does not say how an agent authenticates to an API, or how a
      user grants it authority. A single static credential that covers the whole
      API is a poor fit for an agent. If it leaks, it reaches everything. The
      direction the field is taking is to issue narrow, short-lived credentials
      that can be revoked, and to record delegation openly. The user the agent
      acts for is recorded separately from the agent. That keeps actions
      traceable.</t>
      <t>Authors building for agents should follow the relevant work, including
      OAuth 2.0 Token Exchange <xref target="RFC8693"/>, protected resource
      metadata <xref target="RFC9728"/>, resource indicators <xref
      target="RFC8707"/>, dynamic client registration <xref target="RFC7591"/>,
      on-behalf-of authorization for agents <xref target="I-D.oauth-ai-agents-obo"/>,
      and the wider analysis in <xref target="I-D.klrc-aiagent-auth"/>.</t>
    </section>

    <section anchor="security" numbered="true">
      <name>Security Considerations</name>
      <t>Building an API for agents changes its security picture. The changes
      connect to the points above.</t>

      <t>Injected instructions in text. The agent treats the text it receives as
      input to its reasoning. So both operation descriptions and response bodies
      can carry instructions that an attacker planted. This is called indirect
      prompt injection. Keep trusted control fields apart from untrusted content.
      Mark natural-language content that came from users or third parties as data,
      not as instruction. One way is to put it in its own named field and keep a
      note of where it came from. Do not put externally supplied text into
      operation descriptions or API documentation unless it is clearly marked as
      untrusted. Enforce access decisions at the API. Do not rely on instructions
      in the agent's prompt, because injected text can override them.</t>

      <t>Running out of room and money. An API can return a payload far larger
      than expected, and so can a downstream system that has been compromised. A
      huge payload can fill the agent's space budget and make the task fail. Where
      use is billed, it can also run up a large, unexpected cost. People often
      treat the limits on response size and the cursor-based pagination in <xref
      target="responses"/> as performance tuning. For an agent they are also a
      security control. Enforce them on the server. Do not count on the client to
      ask for less.</t>

      <t>Unsafe retries. The idempotency, preview, and undo guidance in <xref
      target="idempotency"/> is also a security matter. Agents retry more than
      clients driven by a person, and an unsafe retry can repeat an effect that
      changes state. Apply least privilege to operations that have real side
      effects. Keep an audit log that records who acted and under what delegation.
      Consider requiring a confirmation step for high-risk actions.</t>

      <t>Wider reach when access is broad. The discovery artifacts in <xref
      target="discovery"/> make the API easier for any automated client to read,
      including a hostile one. They do not replace authorization or rate limiting.
      Authorization is out of scope here. Still, broad access together with easy
      discovery widens how much a tricked or compromised agent can reach. Scope
      access narrowly to the task. That limits the damage an injected instruction
      can do.</t>
    </section>

    <section anchor="checklist" numbered="true">
      <name>Agent-Friendly Profile Summary</name>
      <t>An HTTP API meant for agents can be called agent-friendly, in the sense
      of this document, when:</t>
      <ul spacing="normal">
        <li>operation identifiers are stable and reveal intent;</li>
        <li>descriptions say when to use and when not to use each operation, and
        state side effects;</li>
        <li>input objects are strictly typed, use fixed value sets, and reject
        unknown properties;</li>
        <li>responses are small by default, with field selection or verbosity
        controls;</li>
        <li>pagination uses cursors and returns a ready-to-use continuation;</li>
        <li>reads support conditional requests, so unchanged data need not be
        sent again;</li>
        <li>collections have a stable, documented order;</li>
        <li>errors are structured, carry stable codes, and say whether a retry is
        safe;</li>
        <li>state-changing operations support idempotency, with a documented
        window and scope;</li>
        <li>high-risk writes can be previewed, cancelled, or confirmed, and are
        marked as such;</li>
        <li>rate limits, retry delays, and polling advice are machine-readable;</li>
        <li>deprecation and replacement operations are machine-readable and shown
        in the description;</li>
        <li>a correlation identifier is accepted, passed along, and logged;</li>
        <li>untrusted returned content is kept apart from trusted control fields
        and marked as data.</li>
      </ul>
    </section>

    <section anchor="iana" numbered="true">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions. It does not define any protocol
      element, header field, media type, or registry entry. The field names used
      in the error examples in <xref target="errors"/> are only examples. If such
      Problem Details fields are ever standardized, registering them would be the
      job of that later specification.</t>
    </section>

  </middle>

  <back>
    <references>
      <name>References</name>

      <references>
        <name>Normative References</name>

        <reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author initials="S." surname="Bradner" fullname="S. Bradner"/>
            <date year="1997" month="March"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
        </reference>

        <reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author initials="B." surname="Leiba" fullname="B. Leiba"/>
            <date year="2017" month="May"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
        </reference>

        <reference anchor="RFC9110" target="https://www.rfc-editor.org/info/rfc9110">
          <front>
            <title>HTTP Semantics</title>
            <author initials="R." surname="Fielding" role="editor"/>
            <author initials="M." surname="Nottingham" role="editor"/>
            <author initials="J." surname="Reschke" role="editor"/>
            <date year="2022" month="June"/>
          </front>
          <seriesInfo name="STD" value="97"/>
          <seriesInfo name="RFC" value="9110"/>
        </reference>

        <reference anchor="RFC9457" target="https://www.rfc-editor.org/info/rfc9457">
          <front>
            <title>Problem Details for HTTP APIs</title>
            <author initials="M." surname="Nottingham"/>
            <author initials="E." surname="Wilde"/>
            <author initials="S." surname="Dalal"/>
            <date year="2023" month="July"/>
          </front>
          <seriesInfo name="RFC" value="9457"/>
        </reference>
      </references>

      <references>
        <name>Informative References</name>

        <reference anchor="RFC7591" target="https://www.rfc-editor.org/info/rfc7591">
          <front>
            <title>OAuth 2.0 Dynamic Client Registration Protocol</title>
            <author initials="J." surname="Richer" role="editor"/>
            <date year="2015" month="July"/>
          </front>
          <seriesInfo name="RFC" value="7591"/>
        </reference>

        <reference anchor="RFC8693" target="https://www.rfc-editor.org/info/rfc8693">
          <front>
            <title>OAuth 2.0 Token Exchange</title>
            <author initials="M." surname="Jones"/>
            <author initials="A." surname="Nadalin"/>
            <author initials="B." surname="Campbell" role="editor"/>
            <author initials="J." surname="Bradley"/>
            <author initials="C." surname="Mortimore"/>
            <date year="2020" month="January"/>
          </front>
          <seriesInfo name="RFC" value="8693"/>
        </reference>

        <reference anchor="RFC8707" target="https://www.rfc-editor.org/info/rfc8707">
          <front>
            <title>Resource Indicators for OAuth 2.0</title>
            <author initials="B." surname="Campbell"/>
            <author initials="J." surname="Bradley"/>
            <author initials="H." surname="Tschofenig"/>
            <date year="2020" month="February"/>
          </front>
          <seriesInfo name="RFC" value="8707"/>
        </reference>

        <reference anchor="RFC9728" target="https://www.rfc-editor.org/info/rfc9728">
          <front>
            <title>OAuth 2.0 Protected Resource Metadata</title>
            <author initials="M." surname="Jones"/>
            <author initials="P." surname="Hunt"/>
            <author initials="A." surname="Parecki"/>
            <date year="2025" month="April"/>
          </front>
          <seriesInfo name="RFC" value="9728"/>
        </reference>

        <reference anchor="I-D.oauth-ai-agents-obo" target="https://datatracker.ietf.org/doc/draft-oauth-ai-agents-on-behalf-of-user/">
          <front>
            <title>OAuth 2.0 Extension: On-Behalf-Of User Authorization for AI Agents</title>
            <author>
              <organization>IETF</organization>
            </author>
            <date year="2025"/>
          </front>
          <refcontent>Work in Progress, Internet-Draft, draft-oauth-ai-agents-on-behalf-of-user</refcontent>
        </reference>

        <reference anchor="I-D.klrc-aiagent-auth" target="https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/">
          <front>
            <title>AI Agent Authentication and Authorization</title>
            <author initials="P." surname="Kasselman"/>
            <author initials="J." surname="Lombardo"/>
            <author initials="Y." surname="Rosomakho"/>
            <author initials="B." surname="Campbell"/>
            <date year="2026"/>
          </front>
          <refcontent>Work in Progress, Internet-Draft, draft-klrc-aiagent-auth</refcontent>
        </reference>

        <reference anchor="I-D.httpapi-idem" target="https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/">
          <front>
            <title>The Idempotency-Key HTTP Header Field</title>
            <author>
              <organization>IETF HTTP API Working Group</organization>
            </author>
            <date year="2025"/>
          </front>
          <refcontent>Work in Progress, Internet-Draft, draft-ietf-httpapi-idempotency-key-header</refcontent>
        </reference>

        <reference anchor="MCP" target="https://modelcontextprotocol.io/specification">
          <front>
            <title>Model Context Protocol Specification</title>
            <author>
              <organization>Model Context Protocol</organization>
            </author>
            <date year="2025"/>
          </front>
        </reference>

        <reference anchor="OPENAPI" target="https://spec.openapis.org/oas/latest.html">
          <front>
            <title>OpenAPI Specification</title>
            <author>
              <organization>OpenAPI Initiative</organization>
            </author>
            <date year="2024"/>
          </front>
        </reference>

        <reference anchor="LLMSTXT" target="https://llmstxt.org/">
          <front>
            <title>The /llms.txt file</title>
            <author initials="J." surname="Howard"/>
            <date year="2024"/>
          </front>
        </reference>

        <reference anchor="W3C-TRACE-CONTEXT" target="https://www.w3.org/TR/trace-context/">
          <front>
            <title>Trace Context</title>
            <author>
              <organization>W3C</organization>
            </author>
            <date year="2021"/>
          </front>
          <refcontent>W3C Recommendation</refcontent>
        </reference>
      </references>
    </references>

    <section anchor="app-mcp" numbered="true">
      <name>Example: MCP Tools for a Project Management System</name>
      <t>This appendix applies the profile to a Model Context Protocol <xref
      target="MCP"/> server for a project management system. The system has
      projects, tasks, and assignees. Each tool uses a strict JSON Schema, so the
      model can find and call it without inventing parameters. Every input schema
      sets <tt>additionalProperties</tt> to false, uses a fixed value set for
      closed choices, marks the required fields, and bounds the size of any
      collection.</t>

      <t>An agent is often told a project by name, not by identifier. A lookup
      tool turns the name into an identifier the other tools need. This follows
      the rule in <xref target="operations"/> not to demand an identifier the
      agent cannot get:</t>
      <sourcecode type="json"><![CDATA[
{
  "name": "find_projects",
  "description": "Find projects by name.",
  "inputSchema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["name_contains"],
    "properties": {
      "name_contains": { "type": "string" },
      "limit": {
        "type": "integer",
        "minimum": 1, "maximum": 20, "default": 10
      }
    }
  }
}
]]></sourcecode>

      <t>A creation tool keeps a strict input schema. Its description, kept short
      in the example, should also say that the operation notifies the assignee,
      point the agent to <tt>find_projects</tt> when it has only a name, and
      explain that reusing the <tt>idempotency_key</tt> returns the original task
      instead of making a duplicate:</t>
      <sourcecode type="json"><![CDATA[
{
  "name": "create_task",
  "description": "Create a task in a project.",
  "inputSchema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["project_id", "title"],
    "properties": {
      "project_id": { "type": "string" },
      "title": { "type": "string", "maxLength": 200 },
      "assignee_email": { "type": "string", "format": "email" },
      "priority": {
        "type": "string",
        "enum": ["low", "medium", "high"],
        "default": "medium"
      },
      "idempotency_key": { "type": "string" }
    }
  }
}
]]></sourcecode>

      <t>A search tool bounds its result size and returns an opaque cursor, so the
      agent never computes an offset. The <tt>status</tt> filter lets it narrow
      the results before it fetches them:</t>
      <sourcecode type="json"><![CDATA[
{
  "name": "find_tasks",
  "description": "Search tasks in a project (bounded page).",
  "inputSchema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["project_id"],
    "properties": {
      "project_id": { "type": "string" },
      "status": {
        "type": "string",
        "enum": ["open", "in_progress", "done"]
      },
      "limit": {
        "type": "integer",
        "minimum": 1, "maximum": 50, "default": 20
      },
      "cursor": { "type": "string" }
    }
  }
}
]]></sourcecode>

      <t>A result is bounded. It carries human-readable labels and a next action,
      and it gives back a cursor for the following page:</t>
      <sourcecode type="json"><![CDATA[
{
  "tasks": [
    {
      "task_id": "t_8a1",
      "title": "Draft launch checklist",
      "status": "in_progress",
      "assignee": { "id": "u_42", "name": "Priya Rao" },
      "next_actions": { "complete": "update_task_status" }
    }
  ],
  "next_cursor": "eyJvIjoyMH0"
}
]]></sourcecode>

      <t>To read the next page, the agent calls the same tool again and passes the
      cursor straight back. It does no arithmetic:</t>
      <sourcecode type="json"><![CDATA[
{
  "name": "find_tasks",
  "arguments": {
    "project_id": "p1",
    "status": "open",
    "cursor": "eyJvIjoyMH0"
  }
}
]]></sourcecode>

      <t>A batch tool creates several tasks in one call. It bounds the array size,
      and its result reports one outcome per item, so the agent can see which
      tasks were created and which were rejected:</t>
      <sourcecode type="json"><![CDATA[
{
  "name": "create_tasks_batch",
  "description": "Create several tasks in one project.",
  "inputSchema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["project_id", "tasks"],
    "properties": {
      "project_id": { "type": "string" },
      "idempotency_key": { "type": "string" },
      "tasks": {
        "type": "array",
        "maxItems": 50,
        "items": {
          "type": "object",
          "additionalProperties": false,
          "required": ["title"],
          "properties": {
            "title": { "type": "string", "maxLength": 200 },
            "priority": {
              "type": "string",
              "enum": ["low", "medium", "high"]
            }
          }
        }
      }
    }
  }
}
]]></sourcecode>

      <t>The batch result shows the per-item outcomes. One task was created and
      one was rejected, and the agent can tell exactly which is which:</t>
      <sourcecode type="json"><![CDATA[
{
  "results": [
    { "index": 0, "status": "created", "task_id": "t_901" },
    { "index": 1, "status": "rejected", "code": "TITLE_TOO_LONG" }
  ],
  "created_count": 1,
  "rejected_count": 1
}
]]></sourcecode>

      <t>A state-changing tool uses a fixed value set for the closed list of
      states and accepts an idempotency key. Its description should note the side
      effect of notifying the assignee:</t>
      <sourcecode type="json"><![CDATA[
{
  "name": "update_task_status",
  "description": "Move a task to a new workflow state.",
  "inputSchema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["task_id", "status"],
    "properties": {
      "task_id": { "type": "string" },
      "status": {
        "type": "string",
        "enum": ["open", "in_progress", "done", "cancelled"]
      },
      "idempotency_key": { "type": "string" }
    }
  }
}
]]></sourcecode>

      <t>Each choice maps back to the profile. The intent-revealing names and the
      use-when guidance reduce wrong-operation mistakes (<xref
      target="description"/>). The fixed value sets and
      <tt>additionalProperties: false</tt> stop the agent from inventing values
      and parameters. The bounded <tt>limit</tt> and the opaque cursor follow
      <xref target="responses"/>. The idempotency key and the stated side effects
      follow <xref target="idempotency"/>. The batch result follows the
      partial-failure rule in <xref target="operations"/>.</t>
    </section>

    <section anchor="app-descr" numbered="true">
      <name>Example: A Weak and a Strong Operation Description</name>
      <t>An operation described only well enough for someone who already knows the
      system leaves the agent to guess its scope and its side effects:</t>
      <sourcecode type="yaml"><![CDATA[
# Too little for an agent
operationId: updateUser
summary: Updates a user
]]></sourcecode>
      <t>The same operation, described for an agent, names its exact purpose, says
      when not to use it, and states the side effect:</t>
      <sourcecode type="yaml"><![CDATA[
# Enough for an agent
operationId: updateUserEmailAddress
summary: Change a user's email address
description: >
  Use this only to change an existing user's email address. Do not
  use it for other profile fields. Use updateUserProfile for those.
  Side effect: sends a verification email. The new address stays
  unverified until the user confirms it.
]]></sourcecode>
    </section>

    <section anchor="app-response" numbered="true">
      <name>Example: A Small Response with Field Selection</name>
      <t>The request selects three fields and a bounded page, so the response
      stays small. The response gives back a ready-to-use link for the next page,
      so the agent does no offset arithmetic:</t>
      <sourcecode type="http-message"><![CDATA[
GET /projects/p1/tasks?status=open&fields=id,title,status&limit=20
Accept: application/json

HTTP/1.1 200 OK
Content-Type: application/json

{
  "items": [
    {
      "id": "t_8a1",
      "title": "Draft launch checklist",
      "status": "open"
    }
  ],
  "next_page": "/projects/p1/tasks?status=open&cursor=eyJvIjoyMH0",
  "next_cursor": "eyJvIjoyMH0"
}
]]></sourcecode>
    </section>

    <section anchor="app-dryrun" numbered="true">
      <name>Example: Previewing a Destructive Write</name>
      <t>A dry-run request reports what an operation would do, without doing it.
      The agent can check the predicted effects and decide whether to go
      ahead:</t>
      <sourcecode type="http-message"><![CDATA[
POST /projects/p1/archive
X-Dry-Run: true
Content-Type: application/json

{ "reason": "Project completed" }

HTTP/1.1 200 OK
Content-Type: application/json

{
  "dry_run": true,
  "would_change": {
    "project_status": { "from": "active", "to": "archived" },
    "tasks_closed": 14,
    "members_notified": 6
  },
  "reversible": true
}
]]></sourcecode>
      <t>Sending the same request without the <tt>X-Dry-Run</tt> header performs
      the operation. The predicted-effects shape is the same, so the agent can use
      it to decide whether to go ahead or to ask for confirmation first.</t>
    </section>

    <section anchor="acknowledgments" numbered="false">
      <name>Acknowledgments</name>
      <t>This document gathers considerations discussed across the API design and
      AI agent engineering communities. The author thanks the authors of the
      referenced specifications and drafts, whose work covers the identity and
      authorization topics this document leaves to them, and the reviewers whose
      feedback shaped the profile structure, the worked examples, and the
      treatment of context exhaustion and observability.</t>
    </section>

  </back>
</rfc>
