From 62543f82bd6a825cadc3f869e2525cdb3119cd8e Mon Sep 17 00:00:00 2001 From: Adam Sobaniec Date: Wed, 15 Jul 2026 16:11:49 +0200 Subject: [PATCH 1/7] feat: generate-spec command article --- blog/generate-openapi-from-traffic.md | 225 ++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 blog/generate-openapi-from-traffic.md diff --git a/blog/generate-openapi-from-traffic.md b/blog/generate-openapi-from-traffic.md new file mode 100644 index 000000000..41d4d79a8 --- /dev/null +++ b/blog/generate-openapi-from-traffic.md @@ -0,0 +1,225 @@ +--- +template: ../@theme/templates/BlogPost +title: No OpenAPI description yet? Generate one from real traffic +description: The new generate-spec command infers an OpenAPI description from recorded HTTP traffic - deterministically first, with optional AI refinement. +seo: + title: No OpenAPI description yet? Generate one from real traffic + description: The new generate-spec command infers an OpenAPI description from recorded HTTP traffic - deterministically first, with optional AI refinement. +author: adam-sobaniec +publishedDate: "2026-07-15" +categories: + - redocly:redocly-cli + - redocly:product-updates + - api-specifications:openapi +--- + +Plenty of production APIs have no OpenAPI description at all. +The endpoints live in code, the knowledge lives in people's heads, and the closest thing to documentation is a wiki page nobody fully trusts. + +For years that was survivable - human developers can read source code, ask a colleague, poke around with curl. +Your newest consumers can't. +AI agents don't attend onboarding calls or absorb tribal knowledge; they learn what your API can do from a machine-readable description of it. +An OpenAPI description is the foundation that everything an agent touches is built on: reference docs, SDKs, MCP tools, request validation. +Without one, your API's capabilities are simply invisible to them. + +So if your API doesn't have an OpenAPI description yet, this is a good moment to start. +The obstacle was never conviction - it's the blank page. +Writing a description by hand for an API with dozens of existing endpoints is exactly the kind of backfill work that never makes it into a sprint. + +The new `generate-spec` command in Redocly CLI removes the blank page. +Your API already produces the raw material every time someone uses it: traffic. +`generate-spec` reads recorded HTTP traffic and infers an OpenAPI 3.1 description from it. + +## From traffic to description + +The command accepts HAR files, Kong logs, Nginx/Apache JSON logs, and NDJSON - a file or a whole folder of them. +Traffic parsing is shared with the [`drift` command](./catch-api-drift.md), so any log that works with `drift` works here too. + +From the recorded exchanges it builds a baseline deterministically - no AI involved, same traffic in, same description out: + +- Identifier-like path segments (numeric IDs, UUIDs, prefixed tokens like `prd_…`) become named path parameters, so a hundred URLs collapse into one templated path. +- Request and response schemas are merged across all observations; a property becomes optional as soon as one sample omits it. +- Alternative body shapes for the same operation are preserved as `oneOf` variants, and object shapes that repeat across the document are extracted into `components/schemas`. +- String values are analyzed conservatively: consistent well-known patterns get a `format` (`uuid`, `date-time`, `email`, `uri`), and strings that only ever take a small set of repeated values become an `enum`. + +## Try it on the Cafe API + +Like last time, you don't need your own API to see it work - Redocly Cafe, our public demo API, will do. +Pretend for a moment that its OpenAPI description doesn't exist, and let's reconstruct it from traffic. + +First, record some. +The `proxy` command starts a local reverse proxy that captures everything passing through into a HAR file: + +```bash +redocly proxy --target https://api.cafe.redocly.com --har ./cafe.har +``` + +``` +Proxy listening on http://127.0.0.1:4040 → forwarding to https://api.cafe.redocly.com/ +Recording traffic to ./cafe.har +Press Ctrl+C to stop. +``` + +Send a few requests through it - browse the menu, filter it, download some menu item photos: + +```bash +curl http://127.0.0.1:4040/menu +curl "http://127.0.0.1:4040/menu?category=dessert" +curl "http://127.0.0.1:4040/menu?category=beverage" +curl -o tiramisu.png http://127.0.0.1:4040/menu-item-images/prd_0000000000seedtrams0000000 +curl -o tea.png http://127.0.0.1:4040/menu-item-images/prd_0000000000seedteabv0000000 +curl -o cheesecake.png http://127.0.0.1:4040/menu-item-images/prd_0000000000seedchesc0000000 +``` + +Press `Ctrl` + `C` to stop the proxy and write the HAR file: + +``` +Captured 6 exchange(s) to ./cafe.har +``` + +Now ask for a description: + +```bash +redocly generate-spec ./cafe.har --title "Cafe API" -o cafe-openapi.yaml +``` + +``` +Inferred a baseline OpenAPI description from traffic: 2 operation(s). +Written to: cafe-openapi.yaml +Done in 0s. +``` + +The result is a valid OpenAPI 3.1 description - about a hundred lines of it, from six requests. +The frame: a server URL inferred from the capture, and one path per discovered endpoint: + +```yaml +openapi: 3.1.0 +info: + title: Cafe API + version: 1.0.0 +servers: + - url: https://api.cafe.redocly.com +paths: + /menu: + # … + /menu-item-images/{menu-item-imageId}: + # … +``` + +Rather than paste the whole file, let's zoom in on what the inference did. + +Start with the paths: the three photo URLs collapsed into one templated path, because the `prd_…` identifiers were recognized as IDs and became a required path parameter: + +```yaml +/menu-item-images/{menu-item-imageId}: + get: + operationId: get-menu-item-images-menu-item-imageId + responses: + '200': + description: OK + parameters: + - name: menu-item-imageId + in: path + required: true + schema: + type: string +``` + +Inside the `/menu` response schema, every menu item property got a type, and the observed values were mined for more: + +```yaml +properties: + # … + price: + type: integer + category: + type: string + enum: + - beverage + - dessert + createdAt: + type: string + format: date-time + photoUrl: + type: string + format: uri +``` + +`category` became an enum because all eighteen observed values were one of the two, and `createdAt` and `photoUrl` matched well-known patterns in every sample. +The `category` query parameter on the same operation stayed a plain string - two observations aren't enough evidence, so the inference stays conservative rather than guessing: + +```yaml +parameters: + - name: category + in: query + required: false + schema: + type: string +``` + +The merge across samples also noticed which properties come and go. +Beverages have volume, desserts have calories, so `volume`, `containsCaffeine`, and `calories` are typed but absent from the `required` list: + +```yaml +properties: + # … + volume: + type: integer + containsCaffeine: + type: boolean + calories: + type: integer +required: + - id + - name + - price + - photoTextDescription + - category + - createdAt + - updatedAt + - object + - photoUrl +``` + +It's still a hypothesis, though - the description only knows what the traffic showed. +`price` is an integer because every observed price happened to be whole. +The photo response has no schema because its body is a PNG, not JSON. +Endpoints nobody called don't exist, nothing has a human-readable description, and names like `{menu-item-imageId}` are derived mechanically - rename them when you review. +More traffic tightens the hypothesis, and your e2e test suite is a free source of it: route the tests through `proxy` once, and the whole run becomes input. + +## Refine it with AI + +For everything determinism can't reach, add `--with-ai`: + +```bash +redocly generate-spec ./cafe.har --title "Cafe API" --with-ai --ai-provider claude -o cafe-openapi.yaml +``` + +The command refines the baseline one operation at a time, sending each operation together with a shape-diverse sample of its real recorded exchanges to an AI provider. +The AI narrows types, adds formats, enums, descriptions, and examples, and models alternative payloads explicitly with `oneOf` and `allOf`. + +Three providers are supported - `claude` (Claude Code), `codex` (Codex CLI), and `cursor` (Cursor CLI). +Each runs the corresponding locally installed CLI in non-interactive mode, so the provider you already use and pay for is the one doing the work; pick a model with `--ai-model` or let the provider use its default. + +The AI's answer is never trusted blindly. +A refined operation is only accepted when it keeps the operation's path and method, keeps every response status code observed in the traffic, and passes validation with the `spec` ruleset. +A rejected refinement keeps its deterministic baseline, and if refinement fails entirely, the command falls back to the baseline description - you never end up with less than the deterministic run would have given you. + +{% admonition type="warning" name="Traffic leaves your machine" %} +`--with-ai` sends samples of the recorded traffic - URLs, query strings, request and response bodies - to the selected AI provider. +Capture in a sandboxed environment with synthetic data, and make sure the traffic contains no secrets or personal data you are not allowed to share. +{% /admonition %} + +{% admonition type="warning" name="Experimental" %} +The `generate-spec` command is experimental. +Flags, output, and behavior may change - including breaking changes - in upcoming releases while we shape it with your feedback. +{% /admonition %} + +## Get started + +The `generate-spec` command is available now in the latest [Redocly CLI](https://redocly.com/docs/cli) - see the [command reference](https://redocly.com/docs/cli/commands/generate-spec) for all options. +Run it on your traffic, review what comes out, lint it with your own ruleset, and tell us what you think on the [Redocly CLI GitHub repository](https://github.com/Redocly/redocly-cli/issues). + +And once you have a description, don't let it rot. +Point the [`drift` command](./catch-api-drift.md) at next week's traffic and it tells you the moment reality moves. +Generate, then guard. From d8fb1151fc9c693975baf90a6d8fa45fb1872bd9 Mon Sep 17 00:00:00 2001 From: Adam Sobaniec Date: Wed, 5 Aug 2026 13:01:25 +0200 Subject: [PATCH 2/7] chore: adjust generate spec article --- blog/generate-openapi-from-traffic.md | 208 +++++++++++++++++++++++--- 1 file changed, 184 insertions(+), 24 deletions(-) diff --git a/blog/generate-openapi-from-traffic.md b/blog/generate-openapi-from-traffic.md index 41d4d79a8..923f0ea3f 100644 --- a/blog/generate-openapi-from-traffic.md +++ b/blog/generate-openapi-from-traffic.md @@ -1,12 +1,12 @@ --- template: ../@theme/templates/BlogPost -title: No OpenAPI description yet? Generate one from real traffic -description: The new generate-spec command infers an OpenAPI description from recorded HTTP traffic - deterministically first, with optional AI refinement. +title: Generate OpenAPI from real traffic (with AI) +description: The new generate-spec command infers an OpenAPI description from recorded HTTP traffic - with AI refinement. seo: - title: No OpenAPI description yet? Generate one from real traffic - description: The new generate-spec command infers an OpenAPI description from recorded HTTP traffic - deterministically first, with optional AI refinement. + title: Generate OpenAPI from real traffic (with AI) + description: The new generate-spec command infers an OpenAPI description from recorded HTTP traffic - with AI refinement. author: adam-sobaniec -publishedDate: "2026-07-15" +publishedDate: "2026-08-05" categories: - redocly:redocly-cli - redocly:product-updates @@ -16,35 +16,30 @@ categories: Plenty of production APIs have no OpenAPI description at all. The endpoints live in code, the knowledge lives in people's heads, and the closest thing to documentation is a wiki page nobody fully trusts. -For years that was survivable - human developers can read source code, ask a colleague, poke around with curl. -Your newest consumers can't. -AI agents don't attend onboarding calls or absorb tribal knowledge; they learn what your API can do from a machine-readable description of it. -An OpenAPI description is the foundation that everything an agent touches is built on: reference docs, SDKs, MCP tools, request validation. -Without one, your API's capabilities are simply invisible to them. +Human developers cope with that - they read the source, ask a colleague, poke around with curl. +AI agents can't. +They learn what your API can do from a machine-readable description of it, and without one its capabilities are invisible to them. -So if your API doesn't have an OpenAPI description yet, this is a good moment to start. -The obstacle was never conviction - it's the blank page. -Writing a description by hand for an API with dozens of existing endpoints is exactly the kind of backfill work that never makes it into a sprint. - -The new `generate-spec` command in Redocly CLI removes the blank page. +So the blank page is worth removing, and it is usually the only thing in the way: writing a description by hand for dozens of existing endpoints is exactly the kind of backfill work that never makes it into a sprint. +The new `generate-spec` command in Redocly CLI removes it. Your API already produces the raw material every time someone uses it: traffic. -`generate-spec` reads recorded HTTP traffic and infers an OpenAPI 3.1 description from it. +`generate-spec` reads recorded HTTP traffic and infers an OpenAPI 3.2 description from it. ## From traffic to description The command accepts HAR files, Kong logs, Nginx/Apache JSON logs, and NDJSON - a file or a whole folder of them. Traffic parsing is shared with the [`drift` command](./catch-api-drift.md), so any log that works with `drift` works here too. -From the recorded exchanges it builds a baseline deterministically - no AI involved, same traffic in, same description out: +From the recorded exchanges it builds a baseline deterministically - no AI involved yet, same traffic in, same description out: - Identifier-like path segments (numeric IDs, UUIDs, prefixed tokens like `prd_…`) become named path parameters, so a hundred URLs collapse into one templated path. - Request and response schemas are merged across all observations; a property becomes optional as soon as one sample omits it. - Alternative body shapes for the same operation are preserved as `oneOf` variants, and object shapes that repeat across the document are extracted into `components/schemas`. - String values are analyzed conservatively: consistent well-known patterns get a `format` (`uuid`, `date-time`, `email`, `uri`), and strings that only ever take a small set of repeated values become an `enum`. -## Try it on the Cafe API +## Example: the Cafe API -Like last time, you don't need your own API to see it work - Redocly Cafe, our public demo API, will do. +Here is what that looks like end to end, on Redocly Cafe - our public demo API, so you can follow along on the same traffic if you want. Pretend for a moment that its OpenAPI description doesn't exist, and let's reconstruct it from traffic. First, record some. @@ -60,6 +55,12 @@ Recording traffic to ./cafe.har Press Ctrl+C to stop. ``` +{% admonition type="warning" name="Record in a sandboxed environment" %} +The proxy records complete exchanges - URLs, headers, cookies, and request and response bodies - so a capture taken against production holds whatever that traffic held, including credentials and personal data. +Record against a test environment with synthetic data. +This matters in every mode, not only with AI: observed values reach the generated description as enums and examples, so a description inferred from real user data isn't safe to pass around either. +{% /admonition %} + Send a few requests through it - browse the menu, filter it, download some menu item photos: ```bash @@ -89,11 +90,11 @@ Written to: cafe-openapi.yaml Done in 0s. ``` -The result is a valid OpenAPI 3.1 description - about a hundred lines of it, from six requests. +The result is a valid OpenAPI 3.2 description - about a hundred lines of it, from six requests. The frame: a server URL inferred from the capture, and one path per discovered endpoint: ```yaml -openapi: 3.1.0 +openapi: 3.2.0 info: title: Cafe API version: 1.0.0 @@ -145,7 +146,7 @@ properties: format: uri ``` -`category` became an enum because all eighteen observed values were one of the two, and `createdAt` and `photoUrl` matched well-known patterns in every sample. +`category` became an enum because every observed value was one of the two, and `createdAt` and `photoUrl` matched well-known patterns in every sample. The `category` query parameter on the same operation stayed a plain string - two observations aren't enough evidence, so the inference stays conservative rather than guessing: ```yaml @@ -183,6 +184,7 @@ required: It's still a hypothesis, though - the description only knows what the traffic showed. `price` is an integer because every observed price happened to be whole. +`id` and `name` came out as enums of the five menu items the capture contained, which is over-fitting rather than a rule of the API - the same mechanism that turns real values into published enums if you record real traffic. The photo response has no schema because its body is a PNG, not JSON. Endpoints nobody called don't exist, nothing has a human-readable description, and names like `{menu-item-imageId}` are derived mechanically - rename them when you review. More traffic tightens the hypothesis, and your e2e test suite is a free source of it: route the tests through `proxy` once, and the whole run becomes input. @@ -199,7 +201,8 @@ The command refines the baseline one operation at a time, sending each operation The AI narrows types, adds formats, enums, descriptions, and examples, and models alternative payloads explicitly with `oneOf` and `allOf`. Three providers are supported - `claude` (Claude Code), `codex` (Codex CLI), and `cursor` (Cursor CLI). -Each runs the corresponding locally installed CLI in non-interactive mode, so the provider you already use and pay for is the one doing the work; pick a model with `--ai-model` or let the provider use its default. +Each runs the corresponding locally installed CLI in non-interactive mode, so the provider you already use and pay for is the one doing the work. +`--ai-provider` is optional and defaults to `claude`; pick a model with `--ai-model` or let the provider use its default. The AI's answer is never trusted blindly. A refined operation is only accepted when it keeps the operation's path and method, keeps every response status code observed in the traffic, and passes validation with the `spec` ruleset. @@ -207,7 +210,164 @@ A rejected refinement keeps its deterministic baseline, and if refinement fails {% admonition type="warning" name="Traffic leaves your machine" %} `--with-ai` sends samples of the recorded traffic - URLs, query strings, request and response bodies - to the selected AI provider. -Capture in a sandboxed environment with synthetic data, and make sure the traffic contains no secrets or personal data you are not allowed to share. +Beyond recording in a sandbox, make sure the traffic holds no secrets or personal data you are not allowed to share with that provider. +{% /admonition %} + +## How much does `--with-ai` actually add? + +Fair question, and the Cafe API can answer it with ground truth: its real, handwritten [OpenAPI description](https://cafe.redocly.com/openapi/cafe) exists - we only pretended it doesn't. +Whatever `generate-spec` reconstructs from traffic can be scored against what the API team actually wrote. + +So we recorded a fuller session than the six-request capture above - 30 requests covering every endpoint: the OAuth2 client registration dance, menu items created in both categories, orders placed, updated, and deleted, photo downloads, and the errors a real session produces along the way (a `400`, a few `404`s, even a `409`). +Then we generated a description twice from that one capture - once deterministically, once with `--with-ai` - and scored both against the handwritten description. + +Two things are worth measuring separately: + +- **Precision** - of every checkable claim the generated description makes about a property the handwritten description also documents (its type, its format, its enum values, whether it's required), how many agree. + This is the "can I trust what it says" number. +- **Recall** - of everything the handwritten description documents, how much the generated one recovered. + This is the "how much work is left" number. + +For response schemas: + +| | Deterministic | `--with-ai` | +| -------------------------------------- | ------------- | ----------- | +| Response properties recovered | 97.5% | 98.3% | +| Correct types | 100% | 100% | +| Correct formats | 100% | 100% | +| Correct enum values | 100% | 100% | +| Correct `required` | 69.2% | 72.2% | +| **Precision, all claims** | **87.8%** | **89.2%** | +| Formats documented, recovered | 53.1% | 62.5% | +| Enums documented, recovered | 66.7% | 66.7% | +| `required` documented, recovered | 91.3% | 94.2% | +| Properties carrying a description | 0% | 97.5% | +| Numeric and length constraints | 0 | 21 | +| Run time | under 1s | 6-15 min | + +Responses tell the same story as the walkthrough: the deterministic baseline is already *correct* - it never contradicted the handwritten description on a single type, format, or enum value - and what `--with-ai` adds there is everything determinism structurally cannot produce: descriptions on nearly every property, constraints, examples, and formats inferred from context rather than repetition. + +Request bodies are a different story: + +| | Deterministic | `--with-ai` | +| ----------------------------------- | ------------- | ----------- | +| Request properties recovered | 55.9% | 61.8% | +| Correct types | 78.9% | **100%** | +| Correct `required` | 81.8% | **100%** | +| **Precision, all claims** | **80.0%** | **100%** | +| Properties carrying a description | 0% | 85.7% | + +## What actually changed + +`POST /menu` accepts `multipart/form-data`, and in a multipart form every value is a string on the wire. +The deterministic baseline can only write down what it saw: + +```yaml +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 +``` + +This is the one place in the whole experiment where the baseline was *wrong* rather than merely incomplete - `price`, `volume`, `calories`, and `containsCaffeine` are not strings - and it is exactly what the AI fixed. +The same request body after `--with-ai`: + +```yaml +requestBody: + content: + multipart/form-data: + schema: + oneOf: + - $ref: '#/components/schemas/BeverageCreate' + - $ref: '#/components/schemas/DessertCreate' + discriminator: + propertyName: category + mapping: + beverage: '#/components/schemas/BeverageCreate' + dessert: '#/components/schemas/DessertCreate' +``` + +```yaml +BeverageCreate: + description: Creation request for a beverage menu item. + allOf: + - $ref: '#/components/schemas/MenuItemCreateBase' + - type: object + properties: + category: + type: string + enum: + - beverage + volume: + type: integer + minimum: 0 + description: Serving volume in millilitres. + example: 180 + containsCaffeine: + type: boolean + description: Whether the beverage contains caffeine. + example: true +MenuItemCreateBase: + type: object + description: Attributes shared by every menu item creation request. + properties: + name: + type: string + description: Human-readable name of the menu item. + example: flat-white + price: + type: integer + minimum: 0 + description: Price in the smallest currency unit (for example cents). + example: 450 + # … + required: + - name + - price + - category +``` + +Every type is corrected, and constraints, descriptions, and examples appeared - but the bigger change is the shape itself. +The AI noticed from the samples that beverages and desserts carry different fields and modeled the union explicitly: `allOf` composition over the shared attributes, selected by a `category` discriminator. +The handwritten description models menu items exactly the same way - `oneOf` beverage or dessert, discriminated by `category`. +Traffic plus AI converged on the design the API team chose by hand, where the baseline could only offer one merged object with everything optional. + +The rest of the delta, briefly: + +- Descriptions went from zero to 97.5% of response properties, plus a summary and description on every refined operation. +- 21 numeric and length constraints appeared, along with realistic `example` values. +- Identifier patterns were inferred on path parameters, such as `pattern: ^ord_[0-9a-z]+$` on the order ID. +- The over-fitting the walkthrough warned about got cleaned up: the baseline turned the two observed order IDs into an `enum`, and the AI demoted it to a plain string with a description and a realistic `example` - while real enums like `category` and order `status` stayed. +- `required` was cleaned up where the traffic over-claimed it - optional client-registration fields that every sample happened to include. + +The guardrails did real work during this run, too: one operation's refinement failed on a transient provider error and kept its deterministic baseline, so the output stayed valid and complete. + +One caveat travels from API to API: path parameters. +Every Cafe path parameter was recognized, because its identifiers are prefixed tokens (`prd_…`, `ord_…`) that the deterministic inference detects. +On APIs whose path segments are ordinary words - organization names, repository names, branches - those segments stay hardcoded, and AI refinement can't rescue them because a refined operation must keep its path. +Reviewing paths by hand is the one non-optional step. + +{% admonition type="info" name="About these numbers" %} +They come from one capture of one small API, scored by a script we wrote for the occasion, so treat them as an illustration rather than a benchmark. +The handwritten description isn't perfect ground truth either: the traffic hit a `409` on `DELETE /menu/{menuItemId}` and a live `/health` endpoint that the handwritten description doesn't document - real API behavior that counts against the generated description as "not in the spec". {% /admonition %} {% admonition type="warning" name="Experimental" %} From 3795990f3407d9b58b5b8a41a7f750e6d7e628e2 Mon Sep 17 00:00:00 2001 From: Adam Sobaniec <112623347+sobanieca-redocly@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:25:03 +0200 Subject: [PATCH 3/7] Update blog/generate-openapi-from-traffic.md Co-authored-by: Adam Altman --- blog/generate-openapi-from-traffic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog/generate-openapi-from-traffic.md b/blog/generate-openapi-from-traffic.md index 923f0ea3f..001d795ad 100644 --- a/blog/generate-openapi-from-traffic.md +++ b/blog/generate-openapi-from-traffic.md @@ -249,7 +249,7 @@ Responses tell the same story as the walkthrough: the deterministic baseline is Request bodies are a different story: -| | Deterministic | `--with-ai` | +| Metric | Deterministic | `--with-ai` | | ----------------------------------- | ------------- | ----------- | | Request properties recovered | 55.9% | 61.8% | | Correct types | 78.9% | **100%** | From 72065435ad4c139bbe25156b33c1ad84a7cdf049 Mon Sep 17 00:00:00 2001 From: Adam Sobaniec <112623347+sobanieca-redocly@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:25:11 +0200 Subject: [PATCH 4/7] Update blog/generate-openapi-from-traffic.md Co-authored-by: Adam Altman --- blog/generate-openapi-from-traffic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog/generate-openapi-from-traffic.md b/blog/generate-openapi-from-traffic.md index 001d795ad..37fbdd351 100644 --- a/blog/generate-openapi-from-traffic.md +++ b/blog/generate-openapi-from-traffic.md @@ -230,7 +230,7 @@ Two things are worth measuring separately: For response schemas: -| | Deterministic | `--with-ai` | +| Metric | Deterministic | `--with-ai` | | -------------------------------------- | ------------- | ----------- | | Response properties recovered | 97.5% | 98.3% | | Correct types | 100% | 100% | From ed037248a69911de7782c79b7bbbea82ebd39d52 Mon Sep 17 00:00:00 2001 From: Adam Sobaniec <112623347+sobanieca-redocly@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:25:20 +0200 Subject: [PATCH 5/7] Update blog/generate-openapi-from-traffic.md Co-authored-by: Adam Altman --- blog/generate-openapi-from-traffic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog/generate-openapi-from-traffic.md b/blog/generate-openapi-from-traffic.md index 37fbdd351..cf2757083 100644 --- a/blog/generate-openapi-from-traffic.md +++ b/blog/generate-openapi-from-traffic.md @@ -84,7 +84,7 @@ Now ask for a description: redocly generate-spec ./cafe.har --title "Cafe API" -o cafe-openapi.yaml ``` -``` +```sh Inferred a baseline OpenAPI description from traffic: 2 operation(s). Written to: cafe-openapi.yaml Done in 0s. From d0324a1d08adaec51edcdd4a4474b5be395156c0 Mon Sep 17 00:00:00 2001 From: Adam Sobaniec <112623347+sobanieca-redocly@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:25:30 +0200 Subject: [PATCH 6/7] Update blog/generate-openapi-from-traffic.md Co-authored-by: Adam Altman --- blog/generate-openapi-from-traffic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog/generate-openapi-from-traffic.md b/blog/generate-openapi-from-traffic.md index cf2757083..d9c6e9c96 100644 --- a/blog/generate-openapi-from-traffic.md +++ b/blog/generate-openapi-from-traffic.md @@ -74,7 +74,7 @@ curl -o cheesecake.png http://127.0.0.1:4040/menu-item-images/prd_0000000000seed Press `Ctrl` + `C` to stop the proxy and write the HAR file: -``` +```sh Captured 6 exchange(s) to ./cafe.har ``` From 39f75f3b05d6e5902ec8174953d8271bc3169dce Mon Sep 17 00:00:00 2001 From: Adam Sobaniec <112623347+sobanieca-redocly@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:25:39 +0200 Subject: [PATCH 7/7] Update blog/generate-openapi-from-traffic.md Co-authored-by: Adam Altman --- blog/generate-openapi-from-traffic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog/generate-openapi-from-traffic.md b/blog/generate-openapi-from-traffic.md index d9c6e9c96..6d96e3643 100644 --- a/blog/generate-openapi-from-traffic.md +++ b/blog/generate-openapi-from-traffic.md @@ -49,7 +49,7 @@ The `proxy` command starts a local reverse proxy that captures everything passin redocly proxy --target https://api.cafe.redocly.com --har ./cafe.har ``` -``` +```sh Proxy listening on http://127.0.0.1:4040 → forwarding to https://api.cafe.redocly.com/ Recording traffic to ./cafe.har Press Ctrl+C to stop.