Discuss online

XXBlueskyBlueskyRedditRedditFacebookFacebookEmail

Agent-Friendly API Design Specification: A Practical Guide

12 minute read

Summarize with:

Discuss online

XXBlueskyBlueskyRedditRedditFacebookFacebookEmail

Agent-Friendly API Design Specification: A Practical Guide

An agent-friendly API design specification tells an agent what an operation does, which inputs it accepts, what it returns, and what to do when the outcome is uncertain. Start with a valid machine-readable API contract, then make the details an agent needs to choose, execute, and recover from a call explicit—and test that the implementation behaves as described.

Key Takeaways

  • Describe decisions, not just endpoints. Use stable operation names and concise descriptions that explain when to use an operation, when not to use it, and whether it changes state.
  • Make inputs and outputs explicit. Publish types, required fields, allowed values, pagination, response shapes, and representative examples in the machine-readable contract.
  • Specify recovery behavior. Distinguish a validation error from a transient failure and an unknown write outcome; document retry conditions and any idempotency guarantees.
  • Separate acceptance from completion. For asynchronous work, define how to check status, recognize terminal states, and stop polling.
  • Test the contract against real behavior. Validate the published specification, inspect generated tool definitions, and run representative tasks and failure cases before relying on the interface.

What Is an Agent-Friendly API Design Specification?

An agent-friendly API design specification is a contract that describes an API in enough detail for an agent application to select an operation, construct a valid request, interpret the result, and respond to errors. It is a design approach, not the name of a single universally required standard.

A human developer can often resolve an ambiguous field by reading a guide or asking a colleague. An agent may see only a generated tool name, a short description, a schema, and the latest response. If a critical constraint is absent from that view, the application cannot assume the agent knows it.

OpenAPI 3.1.2 provides a common foundation: operations can describe identifiers, parameters, request bodies, responses, and security requirements. The specification does not guarantee that a generated tool interface preserves every detail, or that the running API honors the document. Check both separately.

A useful working contract has three layers:

  1. Machine-readable interface: operations, types, constraints, response schemas, and examples.
  2. Operational semantics: side effects, data freshness, pagination, retry rules, and asynchronous state transitions.
  3. Enforcement and evidence: server-side validation and authorization, plus tests showing that the implementation and the contract agree.

This guide focuses on the contract and its behavior. The separate API security checklist covers the implementation of permissions, credentials, and approval boundaries in more detail.

Design Operations an Agent Can Choose Correctly

Give each operation a stable, distinct identifier and describe the decision it represents. A name such as searchInvoices communicates more than getData; getInvoice and searchInvoices are easier to distinguish when their descriptions state the required identifier and intended use.

For each operation, include:

  • Purpose and scope: what the operation returns or changes, and which resources it applies to.
  • Use and non-use conditions: for example, search when the invoice ID is unknown; retrieve a specific invoice when it is known.
  • Side effects: whether the operation creates, modifies, sends, deletes, or triggers downstream work.
  • Prerequisites: required identifiers, account context, supported states, and any dependency on an earlier result.
  • Cost or limits where relevant: whether a call can initiate a billable action, how result limits work, and where to find current limits.

The agent-friendly HTTP API profile proposes explicit use conditions, side effects, and stable operation identifiers for tool-facing descriptions. It is an Internet-Draft, not a finalized standard; treat it as design guidance rather than a compliance requirement.

Keep descriptions concise enough to survive the tool conversion used by your client. If the runtime exposes only the first part of a description, put the differentiating information first and verify the actual generated result. Do not rely on descriptions to enforce permissions or prevent an unwanted action.

Specify Inputs, Outputs, and Pagination

Use explicit types and constraints so the caller does not have to infer them from prose. Declare which fields are required, the meaning and units of each value, valid enumerations, length or range limits, and whether an omitted value differs from null. The OpenAPI specification defines the operation and schema structures for documenting these details.

For outputs, define the fields needed to continue the task, including stable identifiers, status, and a timestamp or freshness indicator when relevant. A compact default response with optional detail can reduce unnecessary context, but never omit information the client needs to interpret the result correctly.

A small OpenAPI example

The following hypothetical contract describes a read-only invoice search. It is an example design, not a claim about an existing service. Its cursor is opaque: the caller passes the returned value back unchanged rather than constructing one.

openapi: 3.1.0
info:
  title: Example Invoice API
  version: 1.0.0
paths:
  /invoices/search:
    post:
      operationId: searchInvoices
      summary: Search invoices by status
      description: >-
        Read-only search. Use when the invoice ID is unknown.
        Returns at most 20 items in a stable order. Pass next_cursor
        unchanged to retrieve the next page; null means no next page.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [status]
              properties:
                status:
                  type: string
                  enum: [open, paid, overdue]
                cursor:
                  type: string
                  description: Opaque cursor from the preceding response.
      responses:
        '200':
          description: A page of matching invoices.
          content:
            application/json:
              schema:
                type: object
                required: [items, next_cursor]
                properties:
                  items:
                    type: array
                    maxItems: 20
                    items:
                      type: object
                      required: [id, status]
                      properties:
                        id:
                          type: string
                        status:
                          type: string
                          enum: [open, paid, overdue]
                  next_cursor:
                    type: [string, 'null']
        '400':
          description: Invalid search input; correct it before retrying.

Before using a contract like this, specify the actual sort key and tie-breaker, cursor expiry, and behavior when records change between pages. Implement those guarantees in the service and document whether traversal is a consistent snapshot. Also test that your validator and tool generator support the schema features you use; a valid OpenAPI document alone does not establish interoperability.

In production, add representative request and response examples, security declarations, and structured error schemas. For a search with no matches, return an empty items array and a null cursor rather than leaving the consumer to guess whether the response is incomplete.

Define Errors, Retries, and Unknown Outcomes

An agent needs an error it can classify, not just a message it can paraphrase. Document the HTTP status, a stable problem type or code, a safe explanation, and the next permitted action. RFC 9457 defines a standard problem-details format for HTTP APIs and permits problem-specific extension members.

For example, a service could return this problem body for an invalid enum value:

{
  "type": "https://example.com/problems/invalid-status",
  "title": "Invalid invoice status",
  "status": 400,
  "detail": "status must be open, paid, or overdue.",
  "code": "INVALID_STATUS",
  "retryable": false
}

Here code and retryable are example extensions, not standard RFC 9457 fields. Document their meaning and ensure clients do not treat a missing retryable field as permission to repeat a request. The example problem-type URL is illustrative, not a live documentation link.

Distinguish at least these cases:

  • Invalid input: correct the named field; do not repeat the same request unchanged.
  • Authorization failure: stop and route the issue to the appropriate access process rather than guessing credentials or permissions.
  • Rate limit or temporary unavailability: follow documented delay guidance and bounded retry rules. A delay alone does not establish that repeating a write is safe.
  • Timed-out write or lost response: the operation may or may not have taken effect. Reconcile its status or use a documented idempotency mechanism before repeating it.

For idempotency, specify the key's scope, retention period, behavior when the same key is reused with a different payload, and how the client can retrieve or reconcile a prior result. An idempotency key is useful only when the server actually enforces the documented behavior; naming a header is not enough.

Make Long-Running Actions Observable

A response that accepts work is not necessarily proof that the work completed. If an operation runs asynchronously, publish a status resource or another documented way to obtain the outcome. Explain how long status information remains available, how frequently to check it, and which states are terminal.

A practical contract for a hypothetical export might define:

  1. Submit: createExport returns an operation ID and an initial queued state.
  2. Check: getExportStatus returns queued, running, succeeded, failed, or cancelled.
  3. Finish: a succeeded result includes the location of the output; a failed result includes a structured problem.
  4. Stop: the client stops polling on a terminal state, a documented deadline, or its own call budget.
  5. Cancel, if supported: the contract explains whether cancellation prevents all effects or whether partial work may remain.

Document the actual status codes and polling guidance your API implements. HTTP 202 Accepted indicates that processing was accepted, not that it succeeded; the HTTP semantics reference illustrates a status-resource pattern for this distinction.

If status retrieval itself fails, retain the operation ID and return an unresolved state to the application. Do not create a second export merely because the first status check timed out.

Validate the Specification Against the Running API

A useful specification is an executable agreement between the team that owns the API and the application that calls it. Review the generated tool interface as well as the original document: a field that disappears during conversion is effectively unavailable to the model at decision time.

Run the following checks against a representative environment:

  1. Validate the document. Check OpenAPI syntax, unique operation IDs, resolved references, schemas, and example payloads with tooling compatible with the version you publish.
  2. Inspect the agent-facing view. Compare generated names, descriptions, input schemas, and response handling with the source contract. Record any truncation or unsupported schema features.
  3. Test operation selection. Give the agent tasks that distinguish similar operations, including one where it lacks a required identifier. Check that it asks for or retrieves the missing information rather than inventing it.
  4. Test boundaries. Send missing, malformed, out-of-range, and extra fields; verify that the service enforces its documented rules and returns the expected error shape.
  5. Exercise recovery. Simulate a rate limit, a temporary failure, and a lost response after a write. Verify retry limits, idempotency behavior, and the path for an unresolved outcome.
  6. Check pagination and asynchronous work. Test empty results, multiple pages, cursor expiry, terminal states, cancellation if offered, and the polling stop condition.
  7. Compare implementation and documentation. Fail the release check when a published contract promises behavior the service does not provide. Version changes and test existing callers before removing or changing fields.

Measure outcomes relevant to the task: correct operation selection, valid arguments, successful completion, unnecessary calls, and unsafe attempts. Keep test cases and expected results fixed when comparing revisions, then inspect failures rather than assuming a higher completion rate alone means the design is safe.

A Specification Review Checklist

Before releasing an agent-facing operation, ask its owner to answer these questions in the contract and demonstrate the answers in a test:

  • Selection: Is its name distinct, and does its description say when to use it and what it changes?
  • Inputs: Are required fields, types, constraints, units, defaults, and invalid-input behavior explicit?
  • Results: Are identifiers, fields, empty results, ordering, page size, and continuation behavior defined?
  • Recovery: Are stable errors, retry conditions, idempotency scope, and unknown outcomes documented?
  • Long-running work: Can the caller identify the operation, inspect its status, and recognize when to stop?
  • Change management: Is there an owner for the specification, a versioning policy, and a process for testing changes against existing clients?

For state-changing operations, add the execution policy that applies to the specific action: for example, whether a preview or human approval is required before a write. Keep enforcement in trusted application or service code. A description can inform the agent, but it is not an authorization control.

If you consume a third-party API rather than own it, apply the same checklist to the adapter or tool definition you control. Clearly distinguish guarantees made by the upstream service from behavior added by your adapter; do not promise retry safety, ordering, or side-effect controls that the upstream system does not provide.

Conclusion

An agent-friendly API is not defined by a schema alone. Its contract makes operation choice, inputs, outputs, side effects, and recovery behavior clear, while its implementation enforces those promises and exposes enough state to resolve uncertainty.

Start with one frequently used operation. Improve its description and schemas, run the selection and failure tests against the generated tool interface, and fix any mismatch between the contract and the service. Use the same review for the next operation only after the first behaves as documented.

FAQ

Is OpenAPI enough to make an API agent-friendly?

OpenAPI is a useful machine-readable foundation, but a valid document may still omit decision-critical details or differ from the running service. Check that descriptions, constraints, side effects, error behavior, and generated tool definitions are complete and accurate.

What should an API expose when a write times out?

Document how the client can determine whether the write occurred, such as an operation-status lookup or a supported idempotency mechanism. Until the result is reconciled, treat the outcome as unknown rather than assuming failure and repeating the action.

Should every operation have an example?

Representative examples are valuable for common requests, responses, empty results, and errors. They supplement rather than replace the formal schema and must be tested against the contract and implementation.

How should an API handle pagination for agents?

Return a predictable, bounded page and a documented continuation value, and explain how the client knows there are no more results. Define ordering, cursor lifetime, and behavior when the underlying data changes; impose a client-side page or call budget.

Can a tool adapter make an existing API more agent-friendly?

An adapter can present clearer operation descriptions, schemas, and response shapes where the upstream interface allows it. It cannot create an upstream guarantee merely by describing one: verify behaviors such as idempotency, permissions, and ordering with the underlying service.

All posts