# Fix openapi.json: publish your API description at /openapi.json

> If your site has an API, `/openapi.json` is the file that lets an agent call it without reading your docs: every path, parameter, response and authentication scheme in one JSON document.

Checked 2026-09-30 against Agent-Readiness Grade 1.3.0. HTML version: https://grade.agentexchange.work/fix/openapi-json

## What the grade checks

- `GET https://example.com/openapi.json` with a 7-second timeout. It counts when the answer is HTTP 200 and valid JSON; the evidence shows the `openapi` (or `swagger`) version field. HTML from a catch-all route does not count.
- Redirects are followed (up to four), so a redirect from `/openapi.json` to where your description really lives works.
- Any one of openapi.json, agent-card.json, mcp-registry-auth or /.well-known/x402 earns the agent-surface point.

The agent-surface point of the machine-readable surfaces area (shared with three other files).

## Why it matters for AI agents and crawlers

The [OpenAPI Specification](https://spec.openapis.org/oas/latest.html) is the common format for HTTP API descriptions; tools generate clients, documentation and agent tool definitions from it. The latest published version is 3.2.1 (10 September 2026); 3.1 is the version most tooling reads today.

Payment indexers use it for discovery: [x402scan's discovery spec](https://github.com/Merit-Systems/x402scan/blob/main/docs/DISCOVERY.md) reads `/openapi.json` first and `/.well-known/x402` second, and marks paid operations with an `x-payment-info` extension and a `402` response.

The grade reads only the root path. If your description lives at `/api/openapi.json` or `/docs/openapi.yaml`, serve a JSON copy or a redirect at `/openapi.json`.

## How to fix it

### 1. Write a minimal document

Start with the operations an agent would call. `operationId` and `summary` become the tool name and description in most agent frameworks.

openapi.json (OpenAPI 3.1):

```json
{
  "openapi": "3.1.0",
  "info": { "title": "Example Co API", "version": "1.0.0", "description": "Invoices and payments." },
  "servers": [{ "url": "https://example.com/api" }],
  "paths": {
    "/invoices/{id}": {
      "get": {
        "operationId": "getInvoice",
        "summary": "Get an invoice by id",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "The invoice", "content": { "application/json": { "schema": { "type": "object" } } } },
          "404": { "description": "No such invoice" }
        }
      }
    }
  }
}
```

### 2. Serve it at /openapi.json

Serve JSON with `application/json`. If a framework generates the document, expose that output at the root path.

#### Static site

Save `openapi.json` in the web root; `.json` files are sent as `application/json`.

#### WordPress

The WordPress REST API describes itself at `/wp-json/`, which is not an OpenAPI document. Publish `/openapi.json` only if you run an API of your own (upload the file to the WordPress root); a content site without an API can skip this file and earn the point another way.

#### Next.js

Use `public/openapi.json` for a hand-written file, or a [route handler](https://nextjs.org/docs/app/api-reference/file-conventions/route) that returns the document your code builds:

app/openapi.json/route.ts:

```ts
const spec = {
  openapi: '3.1.0',
  info: { title: 'Example Co API', version: '1.0.0' },
  paths: {},
}

export async function GET() {
  return Response.json(spec)
}
```

#### Cloudflare

Deploy it as a static asset, or return it from the Worker that serves the API, with CORS so browser-based agents can read it:

Cloudflare Worker:

```js
if (url.pathname === "/openapi.json") {
  return new Response(JSON.stringify(SPEC), {
    headers: { "content-type": "application/json", "access-control-allow-origin": "*" },
  });
}
```

## How to verify

Check that the root path returns JSON with a version field and paths.

```sh
curl -s -o /dev/null -w "%{http_code} %{content_type}\n" https://example.com/openapi.json
curl -s https://example.com/openapi.json | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const j=JSON.parse(s);console.log(j.openapi||j.swagger,"|",Object.keys(j.paths||{}).length,"paths")})'
```

`200 application/json`, then the version and a path count.

Re-grade: https://grade.agentexchange.work/grade?url=example.com&fresh=1

## Questions

### Which OpenAPI version should I use?

3.1 is the safe default and what most tooling reads; 3.2.1, published 10 September 2026, is the latest. The grade accepts any valid JSON and reports the openapi or swagger field.

### Can I publish YAML instead?

Agents and this check read /openapi.json. If you author YAML, publish the JSON form at that path as well.

### My site has no API. Do I need this?

No. Earn the agent-surface point with an agent card, or skip it: a sitemap plus llms.txt already fills the area's two points.

## Sources

- [OpenAPI Specification (latest published: 3.2.1, 10 September 2026)](https://spec.openapis.org/oas/latest.html) (OpenAPI Initiative)
- [x402scan Discovery & Registration Spec](https://github.com/Merit-Systems/x402scan/blob/main/docs/DISCOVERY.md) (x402scan)
- [Route handlers (route.ts), including non-UI responses](https://nextjs.org/docs/app/api-reference/file-conventions/route) (Next.js)

## Related

- [agent-card.json](https://grade.agentexchange.work/fix/agent-card-json.md): The A2A agent card at /.well-known/agent-card.json; one of four files for the agent-surface point.
- [x402 manifest](https://grade.agentexchange.work/fix/x402-manifest.md): A JSON list of your x402 paid routes at /.well-known/x402; only if you sell per request.
- [MCP endpoint and census](https://grade.agentexchange.work/fix/mcp-endpoint.md): An MCP endpoint at /mcp, and a listing among hosts that sell to agents through x402.
- [All fix guides](https://grade.agentexchange.work/fix)
