Skip to content

Redocly CLIexperimental command

Your traffic already knows your API.
Write it down.

generate-spec reads recorded HTTP traffic and writes an OpenAPI 3.2 description in under a second, with no model involved.

Add --with-ai and every operation is refined one at a time, grounded in the requests it actually saw.

$
cafe.har1 of 5 exchanges
# request
GET /menu?category=dessert
host: api.cafe.redocly.com

# response
200 · application/json
{
  "items": [{
    "id": "prd_01jb…",
    "name": "tiramisu",
    "price": 650,
    "category": "dessert",
    "calories": 420,
    "createdAt": "2026-08-02T09:14:31Z"
  }]
}

# 4 more exchanges
GET /menu?category=beverage · 200
POST /menu · 201
GET /menu-item-images/img_01jc… · 200
GET /menu-item-images/img_01jd… · 200

2 operations · 0.4sno AI needed

openapi.yamlOpenAPI 3.2
openapi: 3.2.0servers:  - url: https://api.cafe.redocly.compaths:  /menu:    get:      operationId: get-menu      summary: List menu items      parameters:        - name: category          in: query          description: Filter by menu section.          schema: { type: string }  /menu-item-images/{menu-item-imageId}: …components:  schemas:    MenuItem:      properties:        price:          type: integer          minimum: 0          description: Price in cents.        createdAt:          type: string          format: date-time

Plenty of production APIs have no description. The usual fixes do not scale.

An OpenAPI description is the contract SDK generators, docs, and AI agents read to learn how to call your API. Writing it by hand is slow. Asking an AI to derive it from a large codebase gives you a convincing description that is impossible to verify. Traffic is the one source that cannot lie about what the API does.

By hand

engineer + editor
Time to first draft
Weeks, usually next quarter
Source of truth
Memory, tickets, old docs
How it goes wrong
Incomplete, and stale on day one
Verifiable
By another reviewer
Coverage
Whatever got written

AI from source code

assistant + repository
Time to first draft
Minutes
Source of truth
The part of the code the model can hold in context
How it goes wrong
Plausible but subtly wrong – hallucinated behavior
Verifiable
Hard – there is nothing to check the output against
Coverage
Whatever the model read

From traffic

generate-spec
Time to first draft
Under a second
Source of truth
Every recorded request and response
How it goes wrong
Conservative – claims only what was observed
Verifiable
Yes – every claim traces back to an exchange
Coverage
Whatever the traffic touched – record more to cover more

Record, infer, refine, verify

Recording and inference are enough for a usable description. Refinement is opt-in, and every AI answer passes through the same gate before it is merged.

  1. 01
    Record

    Capture real traffic, or bring the logs you have

    Put redocly proxy in front of the API and point any client at it – a test suite, a browser, a curl script. Or skip this step: HAR exports, Kong, Nginx, and Apache JSON logs, and NDJSON are read directly, as a file or a folder.

    $ redocly proxy --target https://api.example.com --har ./capture.har
    capture.har
    proxy command
  2. 02
    Infer

    Build the baseline deterministically

    Path parameters, merged schemas, components, formats, and enums – all derived from the recorded exchanges with fixed rules. Finishes in under a second, and nothing leaves your machine. Use --server to scope a capture with several hosts or a gateway base path.

    $ redocly generate-spec ./capture.har --title "Example API" -o openapi.yaml
    openapi.yaml · baseline
    generate-spec command
  3. 03
    Refine

    Optionally, let AI fill in what traffic cannot show

    With --with-ai, each operation is sent to the AI CLI already on your machine – claude, codex, or cursor – together with a small, shape-diverse sample of its own recorded exchanges. Operations run in parallel; tune it with --ai-concurrency.

    $ redocly generate-spec ./capture.har --with-ai --ai-provider claude -o openapi.yaml
    openapi.yaml · refined
  4. 04
    Verify

    Accept only what passes the gates

    A refined operation is merged only if it keeps its path and method, keeps every observed status code, keeps an operationId, does not redefine components it does not own, and passes the spec ruleset. Anything else falls back to the baseline, with the reason reported.

    › [3/6] POST /menu — refined (16s)6 of 6 accepted
  5. 05
    Keep it honest

    Lint with your rules, then guard against drift

    The acceptance lint is pinned to the built-in ruleset on purpose, so lint the result with your own redocly.yaml afterward. Then replay traffic against it with redocly drift on every test run, so the description never goes stale again.

    $ redocly lint openapi.yaml && redocly drift ./capture.har --api openapi.yaml
    exit code 0
    drift command

Anatomy of an inferred description

The baseline is built without any model. Every line traces back to a rule and an observation – here is the Cafe API after five requests, and what each inference did.

openapi.yamlbaseline
openapi: 3.2.0
info:
title: Cafe API
servers:
- url: https://api.cafe.redocly.com
paths:
/menu:
get:
operationId: get-menu
parameters:
- name: category
in: query
required: false
3 schema: { type: string }
responses:
'200':
content:
application/json:
schema:
5 $ref: '#/components/schemas/Menu'
1 /menu-item-images/{menu-item-imageId}:
get:
parameters:
- name: menu-item-imageId
in: path
required: true
components:
schemas:
MenuItem:
properties:
price: { type: integer }
category:
type: string
3 enum: [beverage, dessert]
4 createdAt: { type: string, format: date-time }
photoUrl: { type: string, format: uri }
volume: { type: integer }
calories: { type: integer }
2 required: [id, name, price, category, createdAt, photoUrl]
# volume and calories are typed, not required
Still a hypothesis

The description knows only what the traffic showed. price is an integer because every observed price was whole. Endpoints nobody called are missing, and names like {menu-item-imageId} are mechanical. More traffic makes the hypothesis stronger – record an e2e run and feed it in.

The one place the baseline was wrong – and what the AI did about it

POST /menu accepts multipart/form-data, and every value in a multipart form travels as a string. The baseline can only write down what it saw. With --with-ai, the same request body came back with correct types, constraints, descriptions, and a shape the Cafe team had designed by hand.

openapi.yaml
  • Types corrected
  • Shape recovered
  • Meaning added
Baseline
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
name: { type: string }
price: { type: string }
category: { type: string }
volume: { type: string }
containsCaffeine: { type: string }
calories: { type: string }
required: [name, price, category]
--with-ai
requestBody:
content:
multipart/form-data:
schema:
oneOf:
- $ref: '#/components/schemas/BeverageCreate'
- $ref: '#/components/schemas/DessertCreate'
discriminator:
propertyName: category
# components/schemas/BeverageCreate
allOf:
- $ref: '#/components/schemas/MenuItemCreateBase'
- properties:
category: { enum: [beverage] }
volume:
type: integer
minimum: 0
description: Serving volume in millilitres.
example: 180
containsCaffeine: { type: boolean }

Types corrected

price and volume became integers with minimum: 0, containsCaffeine a boolean. The samples showed numbers; the model read them as numbers.

Shape recovered

Beverages and desserts carry different fields, so the union is explicit: a oneOf over two allOf compositions, selected by a category discriminator. The handwritten Cafe description models it exactly the same way.

Meaning added

Descriptions and examples on nearly every property, constraints inferred from what a field means rather than from how often a value repeated.

Nothing is trusted blindly.

Small prompts, strict checks. --with-ai adds meaning to the description without letting a model rewrite what the traffic proved.

1

operation per prompt, however large the capture

5

gates every AI answer must pass before it is merged

0

headers sent – tokens and cookies stay on your machine

100%

fallback to the deterministic baseline, with the reason reported

How much does --with-ai add? We recorded one full session on Redocly Cafe – errors included – generated the description twice, and scored both against the handwritten original.

21numeric and length constraints added by AI, against 0 in the baseline
<1sfor the deterministic run
1–15 minwith AI, depending on the model and --ai-concurrency. A rerun at concurrency 6 finished in under a minute.
Correct types on response schemas were 100% in both runs. Full method and every number in the announcement post.
MetricDeterministic--with-aiChange
Response schemas
Properties recoveredDeterministic97.5%--with-ai98.3%+0.8
Correct required listsDeterministic69.2%--with-ai72.2%+3
Documented formats recoveredDeterministic53.1%--with-ai62.5%+9.4
Properties carrying a descriptionDeterministic0%--with-ai97.5%+97.5
Request bodies
Properties recoveredDeterministic55.9%--with-ai61.8%+5.9
Correct typesDeterministic78.9%--with-ai100%+21.1
Correct required listsDeterministic81.8%--with-ai100%+18.2
Properties carrying a descriptionDeterministic0%--with-ai85.7%+85.7

Works with the logs and the AI you already have

Traffic parsing is shared with redocly drift, so any log that works there works here. AI refinement runs through the coding CLI already installed on your machine.

HARKongNginx JSONApache JSONNDJSON

a file or a whole folder, auto-detected

$ redocly generate-spec
OpenAPI 3.2YAML

to stdout or a file with -o

claudecodexcursor

your local CLI, your subscription, nothing else to set up

Experimental: flags, output, and behavior may change – including breaking changes – while we shape the command with your feedback.

Scope
--server picks the host or base path to describe
Tuning
--ai-model and --ai-concurrency (default 4)
Runs
Locally or in CI. Deterministic mode needs no network.
License
MIT, part of the open-source Redocly CLI. No account.

Frequently asked questions

No. Without “--with-ai” the command is fully deterministic, runs in under a second, and sends nothing anywhere. AI refinement is opt-in and only adds descriptions, constraints, and better shapes on top of the baseline.

Start with the traffic
you already have.

Three commands from a recorded session to a description you can lint, publish, and hand to an agent. Add --with-ai when you want the explanations.

  1. 1$ npm i -g @redocly/cli@latest
    Redocly CLI install guide
  2. 2$ redocly proxy --target http://localhost:9000 --har capture.har
    Proxy command docs
  3. 3$ redocly generate-spec capture.har -o openapi.yaml
    Generate-spec command docs
  4. 4Keep it in sync with redocly driftReplay the same capture against your description and fail CI the moment they disagree.