Skip to content

docs: add operation descriptions and typed error bodies to the OpenAPI spec - #426

Open
shirgoldbird wants to merge 2 commits into
mainfrom
docs/openapi-error-schemas-and-descriptions
Open

docs: add operation descriptions and typed error bodies to the OpenAPI spec#426
shirgoldbird wants to merge 2 commits into
mainfrom
docs/openapi-error-schemas-and-descriptions

Conversation

@shirgoldbird

@shirgoldbird shirgoldbird commented Sep 2, 2026

Copy link
Copy Markdown
Member

Spec-only changes, from an audit of how machine-readable our API surface is. No behavior claims beyond what the live API already does, but points 2 and 3 assert things about the API's error contract, so they want a review from someone who owns that contract.

Precedent: #366 ("fix(openapi): add response body to generic error responses", merged 2026-06-08) did exactly this for 7 shared error responses — BadRequest, Unauthorized, Forbidden, NotFound, PayloadTooLarge, TooManyRequests, QuotaExceeded — for the same reason. Point 2 below covers the 6 that PR left behind, so most of this change is following that precedent rather than setting new policy. Point 3 is the one genuinely new judgment call.

1. description on 27 operations

27 of 62 operations had only a summary. Anything that generates a client or a tool definition from the spec had nothing to work from for those endpoints.

2. A JSON schema on every error response

141 error responses declared only a description and no content. Reading the spec, a client could not tell that DeepL returns a parseable JSON body on an error at all.

Fixed at the source rather than per-operation, continuing #366: 6 shared components/responses (InternalServerError, ServiceUnavailable, ForbiddenGlossaries, URITooLong, UnsupportedMediaTypeGlossaries, QuotaExceededGlossaries) now declare application/json, and two inline 415s now $ref the shared UnsupportedMediaType response instead of duplicating it.

3. InfrastructureErrorResponse, with oneOf on 414/500/503/504

While probing the live API I found the edge layer returns a different error shape than the application:

$ curl 'https://api.deepl.com/v2/languages?type=<9000 chars>'
HTTP/2 502
{"error": {"message": "Bad Gateway."}}

versus the application's {"message": "..."}. So declaring only ErrorResponse on the codes that either layer can produce would have been wrong. Those four now use oneOf: [ErrorResponse, InfrastructureErrorResponse].

This is the part #366 did not have to decide, since the codes it touched are all application-level.

Questions for the reviewer

  1. Is the oneOf split on 414/500/503/504 right? I mapped the two shapes by observation. Someone who knows which layer serves which status code should confirm, especially for 503/504.
  2. HTTP 502 is undocumented. It appears in no operation's responses, but the edge clearly returns it. Adding it across 62 operations is a call on what the edge guarantees, so I left it out.
  3. New operation descriptions are worth a skim for accuracy, particularly the Admin API and Style Rules ones.
  4. POST /v2/glossaries (v2) got "For new integrations we recommend POST /v3/glossaries" rather than any deprecation wording, per CLAUDE.md. Flagging in case a stronger signal is wanted.

Verification

  • openapi.json regenerated with yq -o=json, verified structurally identical to the YAML.
  • mint broken-links --check-anchors: same finding count before and after, all pre-existing (pipeline/drafts/ and existing anchor drift).
  • mint dev: all API reference pages still render, new descriptions confirmed on the rendered pages.
  • Rebased onto main on 2026-09-09 to pick up the six intervening openapi.yaml changes (Voice French variants, hr/tl translated speech, glossary term-matching limits). Clean rebase, no conflicts; the only removed lines in the diff are the two inline 415 descriptions replaced by the $ref.

Note

openapi.yaml also lives in the openapi repo. These changes still need porting there. (sync-openapi-specs.yml runs on push to main and may handle this automatically — worth confirming on merge.)

🤖 Generated with Claude Code

…I spec

Three changes, all to make the spec self-describing for API clients and
agents that read it instead of the prose docs.

1. Add `description` to the 27 operations that only had a `summary`.
2. Give every error response a JSON schema. 141 error responses declared
   only a description and no `content`, so a client reading the spec could
   not tell that DeepL returns a parseable JSON body at all. Fixed at the
   source by filling in 6 shared `components/responses` plus two inline
   415s that now `$ref` the existing shared response.
3. Add `InfrastructureErrorResponse` and use `oneOf` on 414/500/503/504.
   Probing the live API showed the edge layer returns a different body
   shape than the application does: `{"error":{"message":"Bad Gateway."}}`
   versus `{"message":"..."}`. Declaring only the application shape on
   those codes would have been wrong.

openapi.json regenerated from the YAML with yq, and verified structurally
identical to it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@shirgoldbird
shirgoldbird force-pushed the docs/openapi-error-schemas-and-descriptions branch from 6693029 to 8738571 Compare September 9, 2026 14:33
@mintlify

mintlify Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
deepl-c950b784 🟢 Ready View Preview Sep 9, 2026, 2:34 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

The spec as served could not be parsed by strict OpenAPI 3.0 tooling, so
anything generating clients or tool definitions from it got nothing,
regardless of how complete the descriptions were.

Three causes, all pre-existing:

- 512 response-code keys were unquoted, so YAML parses them as integers.
  OpenAPI requires Responses Object keys to be strings; validators don't
  just warn, they fail ("expected string or bytes-like object, got 'int'").
  The generated JSON was unaffected, but that file is in .mintignore and
  isn't published, so the only fetchable spec was the one that wouldn't
  parse.
- OutlineDetectionOptionStr declared enum: ['0'] with default: '1', a
  default outside its own enum. Its siblings PreserveFormattingOptionStr
  and SplitSentencesOption both list '0' and '1', and the boolean
  OutlineDetectionOption defaults to true, so the enum was simply missing
  '1'.
- VoiceSourceLanguage had an empty default:, which parses as null against
  a string enum. Removed rather than given a value; an absent default is
  how "no default" is expressed.

Verified against the deployed preview: openapi-spec-validator reports 0
errors on the spec as served, all 512 response keys parse as strings, the
auth_header security scheme is now visible to parsers, and 62/62
operations expose an operationId and a description. The YAML and JSON also
now parse to identical structures without key normalization.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant