OpenAPI-First skill for Codex
The spec leads, the server proves it matches. Codex treats openapi.yaml as the contract: it designs paths and components in the spec first, lints them against a Spectral or Redocly style guide, diffs the bundle against main with oasdiff, regenerates server types so handlers fail to compile when they drift, and contract-tests the running service with Schemathesis or response validation.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- openapi-first Installs to ~/.agents/skills/openapi-first/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install openapi-first The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project openapi-first Installs to .agents/skills/openapi-first/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/openapi-first.zipunzip -o openapi-first.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) openapi-first PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $openapi-first, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Runs a design-first API workflow where the OpenAPI 3.1 document is the source of truth - authoring reusable components, linting with Spectral or Redocly CLI, bundling multi-file specs, detecting breaking changes with oasdiff, generating server types or stubs, mocking with Prism and contract-testing the implementation. Use when starting an API from a spec, adding paths to openapi.yaml, setting up API linting or a style guide in CI, or when the spec and the running server have drifted apart.
Use it when
- Starting a new API from a spec rather than from code
- Adding or changing operations in an OpenAPI document
- Adding API linting or breaking-change gates to CI
- The published spec and the running server disagree
Use something else when
- Generating and publishing client SDKs from a finished spec: use openapi-client-generation
- Writing reference docs and guides around the spec: use api-documentation
How to check the result
Spectral or Redocly lint passes, oasdiff breaking reports nothing unexpected against main, regenerated code has no diff, and contract tests pass against the running server.
The mistake it prevents
Keeping a hand-written spec alongside framework annotations that also emit one, so two sources of truth drift apart within weeks.
Design-first is working when the spec changes in the same pull request as the code (or before it), CI fails on lint errors and unapproved breaking changes, server types are generated from the spec rather than hand-copied, and a contract test proves the running server matches the document.
Decide the source of truth first
| Situation | Approach |
|---|---|
| Public or partner API, several client teams, long-lived contract | Design-first: hand-written spec, generated server types |
| Framework already emits a good spec (FastAPI, NestJS, Huma, springdoc) and one team owns both sides | Code-first, but commit the emitted spec and run the same lint and diff gates on it |
| Spec exists but nobody trusts it | Contract-test the server against it first, fix drift, then pick a side |
Never maintain two sources of truth: a hand-written spec plus annotations that also emit one will drift.
Workflow
- [ ] 1. Locate the spec, tooling and generation steps
- [ ] 2. Design the change in the spec
- [ ] 3. Lint against the style guide
- [ ] 4. Check for breaking changes
- [ ] 5. Regenerate types/stubs and implement
- [ ] 6. Mock and contract-test
1. Locate
- Read
AGENTS.md, then findopenapi.yaml|json,api/,spec/,.spectral.yaml,redocly.yaml, and codegen configs (openapi-ts.config.*,oapi-codegen.yaml,openapitools.json). Checkpackage.jsonscripts or Makefile targets that lint, bundle or generate. - Note the OpenAPI version. Default to 3.1 for new specs. OpenAPI 3.2 was published in 2025; adopt it only when every linter, generator and doc tool in the chain supports it.
2. Design in the spec
Structure large specs as multiple files and bundle for distribution:
openapi/
openapi.yaml # info, servers, security, paths -> $ref
paths/invoices.yaml
components/schemas/Invoice.yaml
components/responses/Problem.yaml
Authoring rules:
- Every operation has a unique, stable
operationIdin camelCase (listInvoices); generators turn it into function names, so renaming it is a breaking change for SDKs. - Every operation declares
security(or inherits it explicitly), at least one 2xx, the 4xx responses it really returns, and a default error response. - Put shared shapes in
components/schemas,components/parametersandcomponents/responses; reference them with$ref. Inline only one-off shapes. - Use JSON Schema 2020-12 features available in 3.1:
type: [string, "null"]instead ofnullable,const,examplesarrays. - Mark server-generated fields
readOnly: trueand secretswriteOnly: trueso one schema serves request and response. - Add
additionalProperties: falseon request bodies only if the server really rejects unknown fields; otherwise the spec lies. - Give every schema and property a
descriptionand at least one example; documentation and mocks come from them.
paths:
/invoices/{invoiceId}:
get:
operationId: getInvoice
tags: [Invoices]
parameters:
- $ref: '#/components/parameters/InvoiceId'
responses:
'200':
description: The invoice
content:
application/json:
schema:
$ref: '#/components/schemas/Invoice'
'404':
$ref: '#/components/responses/NotFound'
default:
$ref: '#/components/responses/Problem'
3. Lint
Pick one linter; do not run two overlapping rule sets.
Spectral (.spectral.yaml):
extends: ["spectral:oas"]
rules:
operation-operationId: error
operation-tag-defined: error
paths-kebab-case:
description: Path segments must be kebab-case
severity: error
given: "$.paths[*]~"
then:
function: pattern
functionOptions:
match: "^(/([a-z0-9-]+|\\{[a-zA-Z]+\\}))+$"
npx @stoplight/spectral-cli lint openapi/openapi.yaml --fail-severity=warn
Redocly CLI (redocly.yaml with extends: [recommended]):
npx @redocly/cli lint openapi/openapi.yaml
npx @redocly/cli bundle openapi/openapi.yaml -o dist/openapi.yaml
npx downloads packages on first use, which needs network; in an offline sandbox prefer
the versions pinned in devDependencies or ask before installing.
4. Breaking-change gate
Compare the bundled spec against the base branch with oasdiff:
git show origin/main:dist/openapi.yaml > /tmp/base-openapi.yaml
oasdiff breaking /tmp/base-openapi.yaml dist/openapi.yaml --fail-on ERR
Breaking examples: removing a path, operation, response field or enum value; adding a
required request field or parameter; narrowing a type; changing operationId.
When a break is intended, it goes with a version bump or deprecation plan (see
api-versioning), not a quiet merge.
5. Generate and implement
| Stack | Generate |
|---|---|
| TypeScript server or client types | openapi-typescript to a .d.ts; validate requests with the spec at runtime (e.g. express-openapi-validator, Fastify route schemas) |
| Go | oapi-codegen with a config file producing models plus a strict server interface |
| Java / Kotlin Spring | openapi-generator spring generator with interfaceOnly=true |
| Python | datamodel-code-generator for Pydantic models; Connexion for spec-routed apps |
- Generated code is never edited by hand. Commit it or regenerate in CI, matching what the repo already does.
- Implement against the generated interface so a spec change produces a compile error where the handler no longer matches.
- Add a script (
npm run api:generate,make api) so the step is one command.
6. Mock and contract-test
- Mock for frontend or partner work before the server exists:
npx @stoplight/prism-cli mock dist/openapi.yaml(serves examples; add-dfor dynamic data). - Contract-test the real server against the spec with Schemathesis (property-based
requests from the schema; the base URL flag is
--urlin v4) or with response validation middleware enabled in the test environment. - Fail CI when the server returns a status or shape the spec does not declare.
Gotchas
nullable: trueis ignored in 3.1; use a type array. Mixing 3.0 and 3.1 syntax passes some linters and breaks some generators.allOfwithadditionalProperties: falsein each branch rejects every object; useunevaluatedProperties: falseat the top level in 3.1.oneOfwithout adiscriminatorgenerates poor types in most generators.- Example values that violate their own schema make mocks lie; Spectral’s
oas3-valid-schema-examplerule catches them. - Path templating must match the server router exactly (
{invoiceId}vs:id); a mismatch makes validation middleware skip routes silently.
Verification
- Lint passes with zero errors; warnings are fixed or explicitly waived in config.
oasdiff breakingshows no unexpected breaks against the base branch.- Regenerated code has no diff after regeneration (
git diff --exit-codeon the generated directory). - Contract tests pass against the running server.
Output template
## API spec change
- Operations: <operationId, method, path>
- Lint: <tool, result>
- Breaking changes: <none | list + versioning plan>
- Generated artifacts: <paths, regenerated with command>
- Contract test: <tool, result> What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "OpenAPI-First"
short_description: "The spec leads, the server proves it matches"
default_prompt: "$openapi-first Add the invoices endpoints to openapi.yaml before we implement them" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.