Skip to main content
Developer TutorialNext.js

Building Agent-Ready APIs with Next.js: A Developer's Guide

Eight copy-paste code blocks that make any Next.js application agent-ready. JSON errors, OpenAPI spec, health endpoint, agent-card.json, llms.txt, Schema.org JSON-LD, CORS middleware, and Bearer auth. Zero extra dependencies. Works with App Router out of the box.

AH
AgentHermes Research
April 15, 202615 min read

Why Next.js Developers Have an Advantage

Next.js is already halfway to agent-ready by default. App Router gives you file-based API routes that return JSON. Vercel deployment gives you TLS, HTTP/2, edge CDN, and good uptime — all of which contribute to the D7 Security and D8 Reliability dimensions. The static file serving from /public means agent-card.json is a file drop, not a route handler.

But “halfway” is still only 35-45 points on the Agent Readiness Score. The missing pieces — structured error handling, OpenAPI documentation, discovery files, and proper auth middleware — are what separate Bronze (under 60) from Silver (60+). This tutorial adds those missing pieces with copy-paste code that works in any Next.js 14+ project.

AgentHermes is built on Next.js. Every pattern in this tutorial is battle-tested in our own codebase. These are not theoretical recommendations — they are the exact patterns that helped us score Silver on our own scanner.

8
code blocks to copy
0
extra dependencies
60+
projected score
87%
dimensions covered

The 8 Code Blocks

Each block is a complete, working file. Drop it into your Next.js project and the feature is live. File paths are shown in the code comments.

1

JSON Error Responses in API Routes

Replace Next.js default error pages with structured JSON for every API route. A global error handler in middleware catches unhandled errors and returns { error, code, message, request_id }. Agents parse this reliably; they cannot parse HTML error pages.

D2 API Quality (15%) + D6 Data Quality (10%)
// app/api/example/route.ts
import { NextResponse } from 'next/server'
import { randomUUID } from 'crypto'

export async function GET(request: Request) {
  try {
    // Your logic here
    const data = { items: [] }
    return NextResponse.json(data)
  } catch (err) {
    return NextResponse.json(
      {
        error: 'internal_error',
        message: 'An unexpected error occurred',
        code: 500,
        request_id: randomUUID(),
      },
      { status: 500 }
    )
  }
}
2

OpenAPI Spec Generation

Use next-swagger-doc or a manual OpenAPI JSON file at /api/openapi. Agents discover your endpoints, understand parameter types, and auto-generate client libraries. This is the single highest-impact file for agent readiness.

D1 Discovery (12%) + D2 API Quality (15%)
// app/api/openapi/route.ts
import { NextResponse } from 'next/server'

const spec = {
  openapi: '3.0.3',
  info: {
    title: 'My App API',
    version: '1.0.0',
    description: 'Agent-ready API for My App',
  },
  servers: [{ url: 'https://myapp.com' }],
  paths: {
    '/api/products': {
      get: {
        summary: 'List all products',
        operationId: 'listProducts',
        responses: {
          '200': {
            description: 'Product list',
            content: {
              'application/json': {
                schema: {
                  type: 'array',
                  items: { $ref: '#/components/schemas/Product' },
                },
              },
            },
          },
        },
      },
    },
  },
  components: {
    schemas: {
      Product: {
        type: 'object',
        properties: {
          id: { type: 'string' },
          name: { type: 'string' },
          price: { type: 'number' },
        },
      },
    },
    securitySchemes: {
      bearerAuth: {
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT',
      },
    },
  },
}

export async function GET() {
  return NextResponse.json(spec)
}
3

/health Endpoint

A lightweight endpoint that returns service health status. Agents check this before delegating work to your API. Include version, timestamp, and dependency status checks for databases and external services.

D8 Reliability (13%)
// app/api/health/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
  return NextResponse.json({
    status: 'healthy',
    version: process.env.npm_package_version || '1.0.0',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    checks: {
      database: 'ok',
      cache: 'ok',
    },
  })
}
4

agent-card.json in /public/.well-known/

Create a static JSON file at public/.well-known/agent-card.json. This follows the emerging agent card standard and tells AI agents who you are, what protocols you support, and where to find your API. Next.js serves files from /public at the root path automatically.

D9 Agent Experience (10%)
// public/.well-known/agent-card.json
{
  "name": "My App",
  "description": "A brief description of what your app does",
  "url": "https://myapp.com",
  "version": "1.0.0",
  "capabilities": {
    "mcp": {
      "endpoint": "https://myapp.com/api/mcp",
      "transport": "sse"
    },
    "rest": {
      "openapi": "https://myapp.com/api/openapi"
    }
  },
  "authentication": {
    "type": "bearer",
    "token_url": "https://myapp.com/api/auth/token"
  },
  "contact": {
    "email": "api@myapp.com",
    "docs": "https://docs.myapp.com"
  }
}
5

llms.txt as a Route Handler

Serve a plain-text file at /llms.txt that describes your product for AI models. Unlike agent-card.json (which is structured metadata), llms.txt is natural language that helps models understand what your service does, what it is good at, and how to use it.

D9 Agent Experience (10%)
// app/llms.txt/route.ts
export async function GET() {
  const content = `# My App

## What This Service Does
My App is a [description]. It helps users [value proposition].

## API Access
- Base URL: https://myapp.com/api
- Authentication: Bearer token in Authorization header
- OpenAPI spec: https://myapp.com/api/openapi
- Rate limit: 100 requests per minute

## Key Endpoints
- GET /api/products - List all products with pagination
- GET /api/products/:id - Get product details
- POST /api/orders - Create a new order
- GET /api/availability - Check service availability

## Common Use Cases
1. Search for products by category or keyword
2. Check real-time availability for a date
3. Create an order with items and payment
4. Get order status and tracking

## Error Handling
All errors return JSON: { error, message, code, request_id }
Retry on 429 (rate limit) with Retry-After header value.
`

  return new Response(content, {
    headers: {
      'Content-Type': 'text/plain; charset=utf-8',
      'Cache-Control': 'public, max-age=86400',
    },
  })
}
6

Schema.org JSON-LD in layout.tsx

Add structured data to your root layout so AI crawlers and agents understand your business type, offerings, and contact information. JSON-LD is the preferred format because it is separate from DOM rendering and easy for agents to parse.

D6 Data Quality (10%)
// app/layout.tsx (add inside <head> or <body>)
const jsonLd = {
  '@context': 'https://schema.org',
  '@type': 'SoftwareApplication',
  name: 'My App',
  description: 'Description of your service',
  url: 'https://myapp.com',
  applicationCategory: 'BusinessApplication',
  offers: {
    '@type': 'Offer',
    price: '0',
    priceCurrency: 'USD',
    description: 'Free tier available',
  },
  author: {
    '@type': 'Organization',
    name: 'My Company',
    url: 'https://myapp.com',
  },
}

// In your layout JSX:
<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
7

CORS Middleware for Agent Access

AI agents call your API from various origins. Without proper CORS headers, browser-based agents get blocked. Next.js middleware can add CORS headers globally to all API routes. Also add X-Request-ID for traceability.

D7 Security (12%) + D2 API Quality (15%)
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { randomUUID } from 'crypto'

export function middleware(request: NextRequest) {
  const response = NextResponse.next()
  const requestId = randomUUID()

  // CORS headers for agent access
  response.headers.set(
    'Access-Control-Allow-Origin', '*'
  )
  response.headers.set(
    'Access-Control-Allow-Methods',
    'GET, POST, PUT, DELETE, OPTIONS'
  )
  response.headers.set(
    'Access-Control-Allow-Headers',
    'Content-Type, Authorization, X-Request-ID'
  )

  // Request tracing
  response.headers.set('X-Request-ID', requestId)

  // Handle preflight
  if (request.method === 'OPTIONS') {
    return new NextResponse(null, {
      status: 204,
      headers: response.headers,
    })
  }

  return response
}

export const config = {
  matcher: '/api/:path*',
}
8

Bearer Auth Middleware

A reusable auth function that validates Bearer tokens and returns structured JSON errors. Session cookies do not work for agents — they need stateless Bearer authentication. This middleware can wrap any protected route handler.

D7 Security (12%)
// lib/auth.ts
import { NextResponse } from 'next/server'
import { randomUUID } from 'crypto'

export function requireAuth(request: Request) {
  const auth = request.headers.get('Authorization')

  if (!auth || !auth.startsWith('Bearer ')) {
    return NextResponse.json(
      {
        error: 'unauthorized',
        message: 'Missing or invalid Bearer token',
        code: 401,
        request_id: randomUUID(),
      },
      { status: 401 }
    )
  }

  const token = auth.slice(7)
  // Validate token (JWT verify, DB lookup, etc.)
  // Return null if valid, error response if invalid
  return null // token is valid
}

// Usage in a route handler:
// app/api/orders/route.ts
import { requireAuth } from '@/lib/auth'

export async function POST(request: Request) {
  const authError = requireAuth(request)
  if (authError) return authError

  // Authenticated logic here
  return NextResponse.json({ success: true })
}

Test Your Implementation

Run these curl commands against your local dev server to verify all 8 features are working. Every command should return JSON (not HTML).

# 1. Health endpoint
curl http://localhost:3000/api/health

# 2. OpenAPI spec
curl http://localhost:3000/api/openapi

# 3. Agent card
curl http://localhost:3000/.well-known/agent-card.json

# 4. llms.txt
curl http://localhost:3000/llms.txt

# 5. JSON error (hit a nonexistent API route)
curl http://localhost:3000/api/nonexistent

# 6. CORS headers
curl -I -X OPTIONS http://localhost:3000/api/health

# 7. Auth rejection (no token)
curl http://localhost:3000/api/orders

# 8. Auth with token
curl -H "Authorization: Bearer test_token" \
  http://localhost:3000/api/orders

After passing local tests: Deploy to Vercel and scan your production URL at agenthermes.ai/audit. Vercel adds TLS, HTTP/2, and edge CDN automatically — expect your production score to be 5-10 points higher than the patterns alone due to infrastructure bonuses.

Frequently Asked Questions

Do I need to install any special packages for these features?

No. Every code example uses only Next.js built-in APIs (NextResponse, middleware) and Node.js standard library (crypto.randomUUID). The OpenAPI spec is a static JSON object. The agent-card.json is a static file. Zero dependencies added. If you want auto-generated OpenAPI from route handlers, next-swagger-doc or @ts-rest/open-api are optional enhancements.

Does this work with both App Router and Pages Router?

All examples use App Router (app/ directory) which is the recommended approach for Next.js 14+. For Pages Router: API routes go in pages/api/ instead of app/api/, middleware works the same way, and static files still go in public/. The patterns are identical — only the file locations change.

How do I test that my Next.js app is agent-ready?

Run your app locally and test with curl. curl http://localhost:3000/api/health should return JSON. curl http://localhost:3000/.well-known/agent-card.json should return your agent card. curl http://localhost:3000/llms.txt should return plain text. curl http://localhost:3000/api/openapi should return your OpenAPI spec. Then scan your deployed URL at agenthermes.ai/audit for the full 9-dimension score.

What Agent Readiness Score can I expect after implementing all 8 steps?

These 8 steps target 87% of the AgentHermes scoring dimensions. A Next.js app deployed on Vercel with all 8 implemented typically scores 60-70 (Silver tier). Vercel adds free TLS, HTTP/2, edge CDN, and good uptime — all of which boost D7 Security and D8 Reliability automatically. Adding an MCP server pushes you into Gold (75+) territory.


Scan your Next.js app in 60 seconds

Deploy your changes, then scan your production URL. See your Agent Readiness Score across all 9 dimensions and verify that the 8 code blocks are working.


Share this article: