Summary
After the operationId → token migration (sentry #117337 / #117339 / #117403, now in @sentry/api@0.228.0), 32 public endpoints still appear in openapi-derefed.json with sentence-style operationIds and no summary (e.g. "operationId": "List a Project's Issues", summary: null). This is not caused by that migration and does not affect the SDK — but it does affect the public API-reference docs, and it explains why these ops look "unmigrated."
Symptom
In openapi-derefed.json on main:
GET /api/0/projects/{organization_id_or_slug}/{project_id_or_slug}/issues/
operationId: "List a Project's Issues"
summary: null
vs. a properly-migrated op:
GET /api/0/organizations/{organization_id_or_slug}/issues/{issue_id}/
operationId: "getOrganizationIssue"
summary: "Retrieve an Issue"
Root cause
Sentry's public spec is assembled from two systems:
- drf-spectacular
@extend_schema decorators — only methods with publish_status = PUBLIC are emitted.
- The legacy hand-maintained
api-docs/paths/**.json files in getsentry/sentry.
The 31 affected endpoints are published only through the legacy api-docs/ system. Their source publish_status is PRIVATE or EXPERIMENTAL, so they're excluded from the spectacular (decorator) output:
| Endpoint group |
source publish_status |
| project issues (list / bulk mutate / bulk remove) |
all EXPERIMENTAL |
| project tags / tag keys |
PRIVATE |
| user feedback |
PRIVATE (# TODO: deprecate) |
| service hooks |
PRIVATE |
| external issues |
PRIVATE |
| org/project release files, org releases |
PRIVATE |
The legacy api-docs/ files use sentence operationIds and carry no summary, so that's what ends up in the merged spec. The operationId token initiative only touched the spectacular-PUBLIC set (the 216 ops), which is correct — these were never in it.
Why the SDK is unaffected
@sentry/api names come from the normalizer (normalizeOperationId(method, path)) in build.mjs, which derives clean names from method+path regardless of the spec's operationId. So the SDK already exposes clean names for these endpoints (e.g. listProjectIssues). Consumers are not blocked. The gap is in the rendered API-reference docs (title/slug derived from summary/operationId).
Why this is not a regression
These endpoints have always shown sentence operationIds in the docs — that's how the legacy api-docs/ system works. Nothing regressed; the token migration simply didn't (and couldn't) cover them.
Fix (per endpoint, owner-gated, in getsentry/sentry)
This is the legacy api-docs/ → drf-spectacular migration, done per endpoint:
- Set the method(s)
publish_status = ApiPublishStatus.PUBLIC (a deliberate public-API commitment — needs the owning team).
- Add a full
@extend_schema(operation_id="<token>", summary="<old sentence>", parameters=..., responses=..., examples=...) at parity with the legacy file.
- Delete the legacy
api-docs/paths/**.json file and its $ref in api-docs/openapi.json.
- Regenerate (
make build-api-docs) and confirm the op now carries the token + summary.
Because step 1 is a public-API commitment, this can't be a blanket mechanical sweep — each endpoint needs its owning team's sign-off.
Affected endpoints (31, by legacy file)
events/project-issues.json List a Project's Issues; Bulk Mutate a List of Issues; Bulk Remove a List of Issues
events/tag-values.json + projects/tag-values.json List a Tag's Values
projects/service-hooks.json List a Project's Service Hooks; Register a New Service Hook
projects/service-hook-details.json Retrieve / Update / Remove a Service Hook
projects/user-feedback.json List a Project's User Feedback; Submit User Feedback
integration-platform/sentry-app-installations.json List an Organization's Integration Platform Installations
integration-platform/sentry-app-external-issues.json Create or update an External Issue
integration-platform/sentry-app-external-issue-details.json Delete an External Issue
releases/organization-releases.json List an Organization's Releases; Create a New Release for an Organization
releases/organization-release-commits.json List an Organization Release's Commits
releases/organization-release-commit-files.json Retrieve Files Changed in a Release's Commits
releases/release-files.json List an Organization's Release Files; Upload a New Organization Release File
releases/release-file.json Retrieve / Update / Delete an Organization Release's File
releases/project-release-commits.json List a Project Release's Commits
releases/project-release-files.json List a Project's Release Files; Upload a New Project Release File
releases/project-release-file.json Retrieve / Update / Delete a Project Release's File
Two special cases
Bulk Update Project Environments is NOT a legacy api-docs endpoint — it's a plain operation_id="Bulk Update Project Environments" literal in src/sentry/core/endpoints/project_environments.py that the token codemod missed. This one is a trivial operation_id → token rename in getsentry/sentry (no promotion needed).
Enable/Disable Spike Protection (api-docs/paths/projects/spike-protection.json): the legacy file is in sentry, but the endpoint implementation lives in getsentry, so the @extend_schema would be added there.
Verification
After each endpoint is migrated: make build-api-docs then confirm in tests/apidocs/openapi-derefed.json that the op shows the token operationId + summary (and that the path/shape is unchanged). The published spec re-syncs here via the openapi workflow.
Filed from analysis during the operationId token migration. SDK at @sentry/api@0.228.0 already exposes clean names for these endpoints; this issue tracks closing the docs-side gap.
Summary
After the operationId → token migration (sentry #117337 / #117339 / #117403, now in
@sentry/api@0.228.0), 32 public endpoints still appear inopenapi-derefed.jsonwith sentence-styleoperationIds and nosummary(e.g."operationId": "List a Project's Issues",summary: null). This is not caused by that migration and does not affect the SDK — but it does affect the public API-reference docs, and it explains why these ops look "unmigrated."Symptom
In
openapi-derefed.jsononmain:vs. a properly-migrated op:
Root cause
Sentry's public spec is assembled from two systems:
@extend_schemadecorators — only methods withpublish_status = PUBLICare emitted.api-docs/paths/**.jsonfiles ingetsentry/sentry.The 31 affected endpoints are published only through the legacy
api-docs/system. Their sourcepublish_statusisPRIVATEorEXPERIMENTAL, so they're excluded from the spectacular (decorator) output:publish_statusEXPERIMENTALPRIVATEPRIVATE(# TODO: deprecate)PRIVATEPRIVATEPRIVATEThe legacy
api-docs/files use sentenceoperationIds and carry nosummary, so that's what ends up in the merged spec. The operationId token initiative only touched the spectacular-PUBLIC set (the 216 ops), which is correct — these were never in it.Why the SDK is unaffected
@sentry/apinames come from the normalizer (normalizeOperationId(method, path)) inbuild.mjs, which derives clean names from method+path regardless of the spec'soperationId. So the SDK already exposes clean names for these endpoints (e.g.listProjectIssues). Consumers are not blocked. The gap is in the rendered API-reference docs (title/slug derived fromsummary/operationId).Why this is not a regression
These endpoints have always shown sentence operationIds in the docs — that's how the legacy
api-docs/system works. Nothing regressed; the token migration simply didn't (and couldn't) cover them.Fix (per endpoint, owner-gated, in
getsentry/sentry)This is the legacy
api-docs/→ drf-spectacular migration, done per endpoint:publish_status = ApiPublishStatus.PUBLIC(a deliberate public-API commitment — needs the owning team).@extend_schema(operation_id="<token>", summary="<old sentence>", parameters=..., responses=..., examples=...)at parity with the legacy file.api-docs/paths/**.jsonfile and its$refinapi-docs/openapi.json.make build-api-docs) and confirm the op now carries the token + summary.Because step 1 is a public-API commitment, this can't be a blanket mechanical sweep — each endpoint needs its owning team's sign-off.
Affected endpoints (31, by legacy file)
Two special cases
Bulk Update Project Environmentsis NOT a legacy api-docs endpoint — it's a plainoperation_id="Bulk Update Project Environments"literal insrc/sentry/core/endpoints/project_environments.pythat the token codemod missed. This one is a trivialoperation_id→ token rename ingetsentry/sentry(no promotion needed).Enable/Disable Spike Protection(api-docs/paths/projects/spike-protection.json): the legacy file is insentry, but the endpoint implementation lives in getsentry, so the@extend_schemawould be added there.Verification
After each endpoint is migrated:
make build-api-docsthen confirm intests/apidocs/openapi-derefed.jsonthat the op shows the tokenoperationId+summary(and that the path/shape is unchanged). The published spec re-syncs here via theopenapiworkflow.Filed from analysis during the operationId token migration. SDK at
@sentry/api@0.228.0already exposes clean names for these endpoints; this issue tracks closing the docs-side gap.