nts client errors and reduces hallucination in AI-assisted workflows.
// verified-endpoints.ts
export const VERIFIED_ENDPOINTS = {
venues: '/venues/',
culturalEvents: '/cultural-events/',
eventDetails: '/cultural-events/{eventId}/',
locations: '/locations/',
newsFeed: '/news-feed/',
dailyHighlights: '/daily-highlights/',
search: '/search/?query={term}'
} as const;
export const BASE_URL = 'https://api.example.com/public/v2.1';
Step 2: Generate OpenAPI 3.1 Specification
Use a schema-first or code-first generator to produce a valid OpenAPI 3.1 document. Include only verified endpoints. Explicitly mark required fields, response schemas, and pagination structures. Avoid adding authentication flows that do not exist in production.
# openapi.yaml (excerpt)
openapi: 3.1.0
info:
title: Public Discovery API
version: 0.1.0
description: >
Verified endpoint families for venue and event discovery.
Attribution requires fetching detail endpoints for canonical source URLs.
paths:
/cultural-events/:
get:
operationId: listEvents
parameters:
- name: location
in: query
schema: { type: string }
- name: page_size
in: query
schema: { type: integer, maximum: 50 }
responses:
'200':
description: Paginated event summaries
content:
application/json:
schema:
type: object
properties:
results:
type: array
items: { $ref: '#/components/schemas/EventSummary' }
next_page: { type: string, nullable: true }
components:
schemas:
EventSummary:
type: object
properties:
id: { type: string }
title: { type: string }
venue_name: { type: string }
# Note: canonical_url is intentionally omitted here
Step 3: Create LLM-Optimized Context File
Large language models require concise, structured context that fits within token limits. The llms.txt standard provides a machine-readable index that explains what the API covers, where to find specifications, and how to handle attribution.
# llms.txt
# Public Discovery API - LLM Context Index
# Version: 0.1.0
## Overview
This API provides verified venue, event, and local news data.
Base URL: https://api.example.com/public/v2.1
## Supported Endpoints
- GET /venues/
- GET /cultural-events/
- GET /cultural-events/{eventId}/
- GET /locations/
- GET /news-feed/
- GET /daily-highlights/
- GET /search/?query=...
## Discovery Resources
- OpenAPI Spec: https://api.example.com/openapi.json
- API Catalog Linkset: https://api.example.com/.well-known/api-catalog
- Agent Skills Index: https://api.example.com/.well-known/agent-skills/index.json
## Attribution Requirement
List endpoints do not include canonical source URLs.
Always fetch the detail endpoint and use `canonical_url` for public rendering.
Failure to include direct source links violates usage policy.
## Unsupported Features
- OAuth/OIDC authentication
- MCP/WebMCP tooling
- Browser-only CORS requests
- Date range filters (pending verification)
Step 4: Publish RFC 9730-Compliant Linkset
API registries and discovery crawlers expect a standardized linkset format. Serve this at /.well-known/api-catalog with the application/linkset+json media type. Keep it compact and machine-readable.
{
"linkset": {
"self": {
"href": "https://api.example.com/.well-known/api-catalog"
},
"link": [
{ "rel": "describedby", "href": "https://api.example.com/openapi.json" },
{ "rel": "license", "href": "https://api.example.com/attribution-policy" },
{ "rel": "alternate", "href": "https://api.example.com/llms.txt" },
{ "rel": "service-desc", "href": "https://api.example.com/api-catalog.json" },
{ "rel": "agent-skills", "href": "https://api.example.com/.well-known/agent-skills/index.json" }
]
}
}
Step 5: Build Documentation-Only Agent Skills Index
Autonomous agents require explicit capability declarations. Publish a skills manifest that describes what the API can do, how to route requests, and what boundaries exist. Crucially, this must be documentation-only: no executable code, no OAuth flows, no tool escalation vectors.
{
"skills": [
{
"id": "public-discovery-v1",
"name": "Venue & Event Discovery",
"description": "Fetch verified cultural events, venues, and local news. Requires detail-first attribution routing.",
"endpoints": [
"GET /venues/",
"GET /cultural-events/",
"GET /cultural-events/{eventId}/",
"GET /search/?query=..."
],
"constraints": {
"authentication": "none",
"cors": "not-supported",
"attribution": "detail-first",
"max_page_size": 50
},
"routing_rules": [
"Always resolve canonical URLs via detail endpoint",
"Do not cache list responses beyond 5 minutes",
"Include source link in all public renderings"
]
}
]
}
Step 6: Enforce Attribution Routing in Examples
Provide client examples that demonstrate the detail-first attribution pattern. This prevents third-party developers and AI agents from rendering incomplete data.
// attribution-resolver.ts
import axios from 'axios';
const API_BASE = 'https://api.example.com/public/v2.1';
export async function resolveEventWithAttribution(eventId: string) {
const detailRes = await axios.get(`${API_BASE}/cultural-events/${eventId}/`, {
params: { expand: 'venue,images,dates,tags' }
});
const event = detailRes.data;
if (!event.canonical_url) {
throw new Error('Attribution URL missing in detail response');
}
return {
...event,
attribution: {
source: 'Public Discovery API',
canonical_link: event.canonical_url,
required_display: true
}
};
}
Architecture Decisions & Rationale
- Conservative Specification Scope: Documenting only verified endpoints prevents client errors and reduces AI hallucination. Speculative parameters create false expectations and increase support overhead.
- Split Linkset vs Rich JSON: The
/.well-known/api-catalog route follows RFC 9730 for machine-readable discovery. The separate /api-catalog.json carries domain-specific context. This separation maintains standards compliance while allowing rich metadata.
- Documentation-Only Agent Skills: Publishing executable agent tools introduces security risks and capability escalation. A documentation-only manifest teaches agents how to discover and use the API safely without granting implicit permissions.
- Detail-First Attribution: List endpoints intentionally omit canonical URLs to reduce payload size. Enforcing a detail-fetch step ensures every public rendering includes a verifiable source link, satisfying legal, SEO, and trust requirements.
Pitfall Guide
1. Speculative Endpoint Documentation
Explanation: Adding endpoints or parameters that exist in staging but lack production verification.
Fix: Freeze the specification to verified routes only. Use feature flags or versioned prefixes for experimental surfaces. Document verification status explicitly.
2. Ignoring the Attribution Chain
Explanation: Allowing list responses to be rendered publicly without canonical source links.
Fix: Enforce detail-first routing in all examples and agent skills. Validate that canonical_url exists before rendering. Reject integrations that bypass attribution.
Explanation: Advertising OAuth, OIDC, or MCP support that does not exist in production.
Fix: Omit authentication flows from discovery artifacts unless fully implemented. Explicitly state unsupported features in llms.txt and agent skills to prevent client misconfiguration.
4. CORS Assumptions in Public Examples
Explanation: Providing browser-only examples without addressing cross-origin restrictions.
Fix: Clearly mark CORS limitations. Provide server-side examples first. If browser access is planned, document preflight requirements and allowed origins explicitly.
5. Overloading llms.txt with Implementation Details
Explanation: Including verbose code snippets, internal routing logic, or version-specific quirks.
Fix: Keep llms.txt concise. Focus on capabilities, discovery URLs, attribution rules, and unsupported features. LLMs parse brevity more accurately than dense documentation.
6. Agent Skill Escalation Risks
Explanation: Publishing agent manifests that imply tool execution, OAuth delegation, or permission escalation.
Fix: Restrict agent skills to documentation-only declarations. Explicitly state no executable code, no credential handling, and no transport protocols. Validate manifests against security review checklists.
7. Versioning Without Deprecation Signals
Explanation: Updating discovery artifacts without clear versioning or sunset timelines.
Fix: Embed version numbers in all discovery URLs. Publish deprecation headers and migration guides. Maintain backward-compatible linksets during transition periods.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| AI/LLM integration | llms.txt + OpenAPI 3.1 | LLMs need concise context; specs provide contract accuracy | Low (static files) |
| Partner API onboarding | Linkset + rich catalog JSON | Registries require standards compliance; partners need domain context | Medium (maintenance) |
| Autonomous agent routing | Documentation-only skills index | Prevents tool escalation; enforces attribution boundaries | Low (declarative) |
| Developer SDK generation | OpenAPI 3.1 + Postman collection | Code generators require strict schemas; collections provide runnable examples | Medium (tooling) |
| Browser-based clients | Explicit CORS policy + server examples | Public APIs rarely support direct browser access without preflight | High (infrastructure) |
Configuration Template
# openapi.yaml (discovery-focused excerpt)
openapi: 3.1.0
info:
title: Public Discovery API
version: 0.1.0
contact:
name: API Discovery Team
url: https://docs.example.com/discovery
paths:
/venues/:
get:
operationId: listVenues
parameters:
- name: city
in: query
schema: { type: string }
responses:
'200':
description: Venue directory
content:
application/json:
schema:
type: object
properties:
items:
type: array
items: { $ref: '#/components/schemas/VenueSummary' }
components:
schemas:
VenueSummary:
type: object
required: [id, name, city]
properties:
id: { type: string }
name: { type: string }
city: { type: string }
// .well-known/api-catalog
{
"linkset": {
"self": { "href": "https://api.example.com/.well-known/api-catalog" },
"link": [
{ "rel": "describedby", "href": "https://api.example.com/openapi.json" },
{ "rel": "alternate", "href": "https://api.example.com/llms.txt" },
{ "rel": "agent-skills", "href": "https://api.example.com/.well-known/agent-skills/index.json" }
]
}
}
// .well-known/agent-skills/index.json
{
"skills": [
{
"id": "discovery-v1",
"name": "Public Data Discovery",
"description": "Fetch verified venues, events, and news. Enforces detail-first attribution.",
"endpoints": ["GET /venues/", "GET /cultural-events/", "GET /cultural-events/{id}/"],
"constraints": { "auth": "none", "cors": "unsupported", "attribution": "mandatory" }
}
]
}
Quick Start Guide
- Generate the OpenAPI spec: Run your schema generator against verified routes. Validate with
openapi-cli validate.
- Publish discovery artifacts: Host
openapi.json, llms.txt, and the linkset at their respective routes. Set correct Content-Type headers.
- Validate agent skills: Ensure the skills index contains no executable references. Run a static analysis check for OAuth/MCP claims.
- Test attribution routing: Fetch a list response, resolve the detail endpoint, and verify
canonical_url exists. Automate this in CI.
- Register with discovery crawlers: Submit the linkset URL to API registries and AI tooling directories. Monitor crawl logs for parsing errors.
This architecture transforms documentation from a static deliverable into a dynamic, multi-consumer discovery surface. By aligning each artifact with its intended parser, you reduce integration friction, enforce data integrity, and prepare your API for the next wave of AI-assisted development.