From a7092aa12cae50ed15cb92aa401a3c2a4e82693d Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 9 Jun 2026 13:08:34 +0200 Subject: [PATCH 1/4] feat(api): derive reference title and slug from summary The API reference uses the OpenAPI operationId as the page title and the URL slug (slugify(operationId)). That blocks the backend from using operationId as a proper machine token, because changing it would change every title and (SEO-critical) URL. Prefer the human-readable summary for the title and slug, falling back to operationId. This is a no-op today (no operations define summary yet), and once the backend sets summary = the original sentence, titles and slugs stay byte-identical while operationId becomes a clean token. The deprecation prefix is detected/stripped from whichever source wins, so deprecated endpoints keep their exact slug too. --- src/build/resolveOpenAPI.ts | 15 +++++++++++---- src/components/apiPage/index.tsx | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/build/resolveOpenAPI.ts b/src/build/resolveOpenAPI.ts index f2c45abb5c9a9..9ceef265f4d7a 100644 --- a/src/build/resolveOpenAPI.ts +++ b/src/build/resolveOpenAPI.ts @@ -130,8 +130,15 @@ async function apiCategoriesUncached(): Promise { Object.entries(data.paths).forEach(([apiPath, methods]) => { Object.entries(methods).forEach(([method, apiData]) => { - const isDeprecated = isDeprecatedOperationId(apiData.operationId); - const cleanOperationId = stripDeprecatedPrefix(apiData.operationId ?? ''); + // Prefer the human-readable `summary` for the rendered title and the URL + // slug, falling back to `operationId`. This lets the backend use + // `operationId` as a proper machine token (e.g. `addProjectTeam`) without + // changing titles or — SEO-critical — slugs, since `summary` carries the + // original sentence. The deprecation prefix is detected/stripped from + // whichever source wins, so deprecated endpoints keep their exact slug too. + const titleSource = apiData.summary || apiData.operationId || ''; + const isDeprecated = isDeprecatedOperationId(titleSource); + const cleanName = stripDeprecatedPrefix(titleSource); let server = 'https://sentry.io'; if (apiData.servers && apiData.servers[0]) { @@ -141,10 +148,10 @@ async function apiCategoriesUncached(): Promise { categoryMap[tag].apis.push({ apiPath, method, - name: cleanOperationId, + name: cleanName, deprecated: isDeprecated, server, - slug: slugify(cleanOperationId), + slug: slugify(cleanName), summary: apiData.summary, descriptionMarkdown: apiData.description, pathParameters: (apiData.parameters || []).filter( diff --git a/src/components/apiPage/index.tsx b/src/components/apiPage/index.tsx index d49fd86eaa155..c0caa97bbcfd3 100644 --- a/src/components/apiPage/index.tsx +++ b/src/components/apiPage/index.tsx @@ -137,7 +137,7 @@ export function ApiPage({api}: Props) {
- {api.summary &&

{api.summary}

} + {api.summary && api.summary !== api.name &&

{api.summary}

} {api.descriptionMarkdown && parseMarkdown(api.descriptionMarkdown)} From 3de606b361290d24da49357058d8a7e6bf54067d Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 9 Jun 2026 19:06:14 +0200 Subject: [PATCH 2/4] ref(api): drop verbose comment, let code self-document --- src/build/resolveOpenAPI.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/build/resolveOpenAPI.ts b/src/build/resolveOpenAPI.ts index 9ceef265f4d7a..607519c34d355 100644 --- a/src/build/resolveOpenAPI.ts +++ b/src/build/resolveOpenAPI.ts @@ -130,12 +130,6 @@ async function apiCategoriesUncached(): Promise { Object.entries(data.paths).forEach(([apiPath, methods]) => { Object.entries(methods).forEach(([method, apiData]) => { - // Prefer the human-readable `summary` for the rendered title and the URL - // slug, falling back to `operationId`. This lets the backend use - // `operationId` as a proper machine token (e.g. `addProjectTeam`) without - // changing titles or — SEO-critical — slugs, since `summary` carries the - // original sentence. The deprecation prefix is detected/stripped from - // whichever source wins, so deprecated endpoints keep their exact slug too. const titleSource = apiData.summary || apiData.operationId || ''; const isDeprecated = isDeprecatedOperationId(titleSource); const cleanName = stripDeprecatedPrefix(titleSource); From cc4ae04e08724c606c731d9e54364f84678a9618 Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 9 Jun 2026 19:15:32 +0200 Subject: [PATCH 3/4] fix(api): detect deprecation from operationId and summary independently Reading the (DEPRECATED) marker off the display title (summary || operationId) would miss it once an op has a summary, since the marker sits on operationId. Check both fields directly so deprecation detection is independent of which string supplies the title. --- src/build/resolveOpenAPI.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/build/resolveOpenAPI.ts b/src/build/resolveOpenAPI.ts index 474da0c33a7d2..ed07cd3383c76 100644 --- a/src/build/resolveOpenAPI.ts +++ b/src/build/resolveOpenAPI.ts @@ -130,8 +130,12 @@ async function apiCategoriesUncached(): Promise { Object.entries(data.paths).forEach(([apiPath, methods]) => { Object.entries(methods).forEach(([method, apiData]) => { + // Detect deprecation from either field independently — the (DEPRECATED) + // marker may sit on operationId even when a summary is present. + const isDeprecated = + isDeprecatedOperationId(apiData.operationId) || + isDeprecatedOperationId(apiData.summary); const titleSource = apiData.summary || apiData.operationId || ''; - const isDeprecated = isDeprecatedOperationId(titleSource); const cleanName = stripDeprecatedPrefix(titleSource); let server = 'https://sentry.io'; From 0c9ac492c11885444891ef4c42cc16ddcbf36c98 Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 9 Jun 2026 19:20:22 +0200 Subject: [PATCH 4/4] fix(api): strip deprecated prefix from stored summary too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit name was stripped of the (DEPRECATED) prefix but summary was stored raw, so the apiPage dedup (api.summary !== api.name) compared a stripped value to a raw one — a prefixed summary would defeat the dedup and render the prefix in the summary paragraph. Strip both consistently. --- src/build/resolveOpenAPI.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/build/resolveOpenAPI.ts b/src/build/resolveOpenAPI.ts index ed07cd3383c76..19af07487db9d 100644 --- a/src/build/resolveOpenAPI.ts +++ b/src/build/resolveOpenAPI.ts @@ -150,7 +150,9 @@ async function apiCategoriesUncached(): Promise { deprecated: isDeprecated, server, slug: slugify(cleanName), - summary: apiData.summary, + summary: apiData.summary + ? stripDeprecatedPrefix(apiData.summary) + : apiData.summary, descriptionMarkdown: apiData.description, pathParameters: (apiData.parameters || []).filter( p => p.in === 'path'