Skip to content

ref(cli): Move API calls with generated operations onto @sentry/api - #2073

Merged
BYK merged 1 commit into
mainfrom
ref/cli-sentry-api-operations
Nov 7, 2026
Merged

BYK merged 1 commit into
mainfrom
ref/cli-sentry-api-operations

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

This is step 1 of moving the CLI onto @sentry/api generated operations. The goal is an Oxlint rule that bans the apiRequest* helpers with no allowlist. @BYK chose that scope ("Also ban apiRequest* helpers") when the lint follow-up to #2062 was scoped. Since the rule can't land until every call site has moved, it isn't in this PR.

#2062 bumps @sentry/api to 1.44.0 too and moves lib/objectstore.ts. This PR leaves objectstore.ts alone, so whichever PR merges second only needs a lockfile rebase.

What changed

  • @sentry/api 1.26.0 → 1.44.0 in the catalog and in minimumReleaseAgeExclude. The regenerated alert.md and event.md skill references are included.
  • 18 apiRequest* call sites now use generated operations:
    • alerts: deleteIssueAlertRule → deleteOrganizationWorkflow
    • chunk-upload:
      • getChunkUploadOptions → getOrganizationChunkUploadOptions. ChunkServerOptionsSchema still validates the response because the SDK type doesn't include maxWait.
      • New assembleDebugFiles → assembleProjectDebugFiles
      • New assembleArtifactBundle → assembleOrganizationArtifactBundle
    • sourcemaps, dart-symbols, proguard, debug-files: their assemble and poll calls now go through the chunk-upload wrappers above (7 sites). pollAssembly/pollDifAssembly take an assemble callback.
    • conversations: getConversationSpans → retrieveOrganizationAiConversation
    • issues: getIssueByShortId → resolveOrganizationShortId
    • preprod-artifacts:
      • getBuildInstallDetails → getOrganizationPreprodArtifactInstallDetails
      • Latest base snapshot → getOrganizationPreprodArtifactSnapshotLatestBase (a 404 still returns null)
    • releases: setCommitsWithRefs → updateOrganizationRelease. The ./.. path-segment checks stay because the SDK's path serializer lets them through.
    • replays: getReplay → getOrganizationReplay
    • traces:
      • getTraceMeta → getOrganizationTraceMeta
      • fetchTransactionsPage/fetchSpansPage → listOrganizationEvents
  • infrastructure.ts gets validateResponse and unwrapValidatedResult, so call sites that already validated responses with Zod still do that on SDK results. No new as casts on query or body types.

Behavior notes

  • HTTP error messages for the moved calls now read <context>: <status> (from unwrapResult/throwApiError) instead of API request failed: …. The 401/403 enrichment is unchanged.
  • throwApiError doesn't attach the api_response_headers Sentry context that the helper path set.
  • Pagination, region routing (getOrgSdkConfig), caching and idempotency are unchanged.

Tests

  • chunk-upload.test.ts and proguard.test.ts now test at the fetch level (mockFetch + setOrgRegion). New cases cover maxWait, schema mismatch, 404, and both assemble endpoints.
  • debug-files.test.ts mocks the new assembleDebugFiles.
  • preprod-artifacts.test.ts mocks getOrgSdkConfig, with new cases for a null body, a 500, and a schema mismatch.
  • The existing fetch-level tests for alerts, short IDs, conversations, releases and traces pass unchanged.

Gate

  • pnpm run tsc and pnpm run lint: pass.
  • pnpm run test: fails only on sandbox environment problems that also fail on main. CI is the real check.
    • The root run aborts in codemod-cli3to4 because the sandbox lacks GLIBC_2.35.
    • pnpm -r --no-bail run test: every package passes except the CLI, which has 5 failures (12881 passed). All 5 are file-permission tests that fail on main in the same sandbox: completions (2), upgrade acquireLock, db/resilient readonly, and dsn/code-scanner unreadable files.

Remaining call sites (39 + 2 raw fetches)

(a) Missing from the public schema: needs getsentry/sentry API docs

  • auth.ts GET /api/0/ (root). Used for the token check; dynamic.
  • dashboards.ts GET /organizations/{org}/dashboards/{id}/revisions/
  • dashboards.ts POST /organizations/{org}/dashboards/{id}/revisions/{rev}/restore/
  • issues.ts GET /issues/{id}/ (unscoped issue lookup)
  • issues.ts PUT /issues/{id}/ (unscoped update fallback)
  • issues.ts raw customFetch GET /shared/issues/{shareId}/ (getSharedIssue)
  • logs.ts GET /organizations/{org}/trace-logs/
  • org-auth-tokens.ts GET, POST /organizations/{org}/org-auth-tokens/, DELETE …/{tokenId}/
  • organizations.ts GET /users/me/regions/
  • preprod-artifacts.ts:
    • POST /projects/{org}/{project}/files/preprodartifacts/assemble/ (build assemble)
    • GET …/preprodartifacts/snapshots/upload-options/
    • Snapshot archive status and trigger at …/snapshots/{id}/archive/
    • Raw customFetch openSnapshotArchive
  • projects.ts GET /organizations/{org}/projects/?query=dsn:{key} (×2). The list op has no query param.
  • releases.ts GET /organizations/{org}/releases/{version}/previous-with-commits/
  • traces.ts GET /projects/{org}/{project}/trace-items/{itemId}/
  • trials.ts GET /customers/{org}/ and PUT /customers/{org}/product-trial/
  • users.ts GET /auth/

These look intentionally private, so publishing them needs a call from the API owners: /customers/* (billing, getsentry), /users/me/regions/, the /api/0/ root, /auth/, previous-with-commits, trace-logs, trace-items, and the preprod/snapshot internals.

(b) In the schema, but the generated types don't match what the CLI sends or reads

  • alerts.ts ×2, GET /organizations/{org}/workflows/: no projectSlug filter, and the CLI sends string ids.
  • code-mappings.ts POST bulk code mappings: the CLI sends a camelCase body; the schema is snake_case.
  • conversations.ts list: no sort query param.
  • dashboards.ts events-timeseries: yAxis is an array and dataset is optional in the CLI.
  • dashboards.ts events: dataset can be null or a value outside the enum.
  • explore.ts events-timeseries: array yAxis, string interval.
  • explore.ts events: free-form dataset.
  • issues.ts GET org issue: the collapse enum is only release|stats|tags.
  • issues.ts PUT org issue: every update body field is required.
  • issues.ts PUT /organizations/{org}/issues/ (merge): updateOrganizationIssues also requires every body field.
  • replays.ts list: the field enum lacks info_ids/warning_ids.
  • replays.ts replay-count: no data_source=events.
  • seer.ts ×3, /organizations/{org}/issues/{id}/autofix/: no mode=explorer.
  • traces.ts GET /organizations/{org}/trace/{id}/: no timestamp/limit/project params.
  • preprod-artifacts.ts POST …/preprodartifacts/snapshots/: the body type is never, and the CLI sends a zstd body.

(c) Dynamic or upload transport, not Sentry API operations

  • Chunk uploads POST to the server-provided chunk URL.
  • Preprod build download fetches the external installUrl.
  • rawApiRequest/prepareRawApiRequest back the sentry api passthrough command.

via @BYK.

--

View Session [Sentry]

Bump @sentry/api to 1.44.0 and move the apiRequest* call sites that have
a matching generated operation onto it: alert rule delete, chunk upload
options and assemble (debug files, sourcemaps, dart symbols, proguard),
AI conversation detail, short ID resolve, preprod install details and
snapshot latest base, release commit refs, replay detail, trace meta,
and the transactions/spans event pages.

Add validateResponse/unwrapValidatedResult so call sites that already
check responses with a Zod schema keep doing that on SDK results.

This is step 1 of moving the CLI onto generated operations. The
remaining sites are blocked on missing public schema entries or type
mismatches and stay on the helpers for now.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
@vercel

vercel Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
sentry-local Ready Ready Preview Nov 7, 2026 12:57am UTC

Request Review

@github-actions github-actions Bot added the risk: medium PR risk score: medium label Nov 7, 2026
BYK added a commit to getsentry/sentry that referenced this pull request Nov 7, 2026
The OpenAPI schema for `PUT /organizations/{org}/issues/{issue_id}/`
(`updateOrganizationIssue`) and `PUT /organizations/{org}/issues/`
(`updateOrganizationIssues`) listed every `GroupValidator` field, and
every `statusDetails` field, as required. The endpoints always validate
with `partial=True` (`validate_request` in
`api/helpers/group_index/update.py`), so a request only sends the fields
it changes, for example `{"status": "resolved"}`.

This marks those fields `required=False`. Validation doesn't change,
because `partial=True` already skipped the required check. Only the two
operations above change in the generated schema. Their request body
schemas lose the `required` lists, and the body itself is no longer
marked required.

The Sentry CLI needs this to move its issue update calls onto the
generated `@sentry/api` operations (getsentry/toolkit#2073). Today the
generated body type makes every field mandatory, so a partial update
doesn't typecheck.

Checked locally with `make build-api-docs`, `pnpm run
validate-api-examples`, `tests/apidocs`,
`tests/sentry/issues/endpoints/test_group_details.py`, `GroupUpdateTest`
in `test_organization_group_index.py`,
`tests/sentry/api/helpers/test_group_index.py`, prek and mypy. Under
xdist, three snuba-backed `GroupUpdateTest` cases failed:
`test_resolve_with_integration`, `test_snooze_user_count` and
`test_bulk_resolve`. Run serially, they pass on this branch and on
master.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
BYK added a commit to getsentry/sentry that referenced this pull request Nov 7, 2026
…estamp (#130349)

Three query parameters on public Explore endpoints were documented
differently from what the code accepts:

- **`yAxis`** on `listOrganizationEventsTimeseries` was a single string.
The endpoint reads `request.GET.getlist("yAxis")` and returns one
timeseries per aggregate, so it's now an array (repeated
`?yAxis=count()&yAxis=p95(span.duration)`).
- **`interval`** (`VisibilityParams.INTERVAL`, used by
`listOrganizationEventsTimeseries` and `listOrganizationEventsDropped`)
was an integer. `get_rollup_from_request` parses it with
`parse_stats_period`, which also takes a unit suffix such as `5m` or
`1h`. It's now `anyOf: [integer, string matching ^\d+[smhdw]?$]`, so
callers that pass a number keep working.
- **`timestamp`** on `getOrganizationTrace` and
`getOrganizationTraceMeta` wasn't listed. Both endpoints pass it to
`update_snuba_params_with_timestamp`, which accepts ISO 8601 or Unix
seconds and narrows the query window. I added it as
`TraceParams.TIMESTAMP`.

Only those four operations change in the generated schema. There's no
runtime change.

The Sentry CLI needs these to move its timeseries and trace calls onto
the generated `@sentry/api` operations (getsentry/toolkit#2073). Right
now the generated types reject repeated `yAxis`, `interval=1h` and
`timestamp`. One thing to note for SDK consumers: the generated `yAxis`
type changes from `string` to `string[]`.

Checked locally with `make build-api-docs`, `pnpm run
validate-api-examples`, `tests/apidocs`, prek and mypy. I also ran
`tests/snuba/api/endpoints/test_organization_trace_meta.py` and
`test_organization_events_timeseries.py::test_simple`. Under xdist those
snuba tests miss rows from shared state, but run serially they pass.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
BYK added a commit to getsentry/sentry that referenced this pull request Nov 7, 2026
…0350)

`GET /organizations/{org}/agents/conversations/`
(`listOrganizationAIConversations`) accepts a `sort` query param.
`OrganizationAIConversationsSerializer` validates it against
`AI_CONVERSATIONS_FIELDS`, and it defaults to `-conversation.age`, but
the OpenAPI docs didn't list it. This adds the parameter with an enum of
the canonical fields, each with an optional `-` prefix. Only this
operation changes in the generated schema, and there's no runtime
change.

The enum comes from a new `AI_CONVERSATIONS_SORT_FIELDS` tuple. It takes
the keys of `AI_CONVERSATIONS_FIELDS` before the legacy spellings are
added, so `messages`, the bare field names and the function aliases stay
accepted but undocumented.

The Sentry CLI's `agent-conversation list --sort` needs this to move
onto the generated `@sentry/api` operation (getsentry/toolkit#2073). The
generated query type has no `sort` today.

Checked locally with `make build-api-docs`, `pnpm run
validate-api-examples`,
`tests/sentry/api/endpoints/test_organization_ai_conversations.py`,
`tests/apidocs`, prek and mypy.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
@sentry-junior
sentry-junior Bot added this pull request to stack #2076 November 7, 2026 01:59
@BYK
BYK enabled stacked auto-merge (squash) November 7, 2026 02:11
@BYK
BYK merged commit 2b34b73 into main Nov 7, 2026
46 checks passed
@BYK
BYK deleted the ref/cli-sentry-api-operations branch November 7, 2026 02:12
BYK added a commit that referenced this pull request Nov 7, 2026
Stacked on #2073. That PR moves the CLI onto the generated `@sentry/api`
operations. This one fixes the cases where the CLI, not the API docs,
was wrong. The full list is in getsentry/sentry#130351. Where the 1.44.0
operation now matches, the call moves onto it here too.

Each fix was checked against the getsentry/sentry endpoint code.

### Moved onto generated operations
| Area | Fix | Operation |
| --- | --- | --- |
| `alert issues list/view` | Filter workflows by `project` (the endpoint
ignores `projectSlug`) and send `id` as a number |
`listOrganizationWorkflows` (×2) |
| `code-mappings upload` | Send the bulk body in snake_case:
`default_branch`, `stack_root`, `source_root` |
`bulkUpsertOrganizationCodeMappings` |
| `explore`, `dashboard view` tables | Type `dataset` as a public Events
dataset. Explore still falls back to `errors`, and unmapped widget types
still render as unsupported | `listOrganizationEvents` (×2) |
| `issue view` and other org-scoped issue lookups | Send only the
`collapse` values the details endpoint reads (`release`, `stats`,
`tags`). It ignored `lifetime`/`filtered`/`unhandled`, and the detail
response keeps those fields either way. The short-ID endpoint still gets
every value | `getOrganizationIssue` |
| `issue view` replay lookup | Send `data_source=errors`. `events` isn't
a value the endpoint accepts | `getOrganizationReplayCount` |
| `issue explain/plan` | Drop `mode=explorer`, which the autofix
endpoint never reads | `startOrganizationIssueAutofix` (×2),
`getOrganizationIssueAutofixState` |

### Fixed, but still on the helper
- **`issue merge`**: now sends `{merge: true}` with numeric IDs.
`updateOrganizationIssues` marks every body field required until
getsentry/sentry#130348 lands.
- **`trace view`**: no longer sends `project` or `limit`, since the
endpoint reads neither. It stays on the helper until
getsentry/sentry#130349 documents `timestamp`. An unknown project in
`trace view org/project/<id>` still fails as before.
- **Dashboard and explore time series**: get the same typed `dataset`,
but stay on the helper until #130349 fixes `yAxis`/`interval`.
- **AI conversations list**: untouched, because it's waiting on
getsentry/sentry#130350 (`sort`).

### Kept on purpose
- **`replay list`** keeps requesting `info_ids` and `warning_ids`. The
server computes them and `replay list --json` prints them, so dropping
them would change the output. `fetchReplayPage` stays on the helper, and
a comment says why.

### Behavior notes
- On the moved calls, errors use the SDK wording already used by the
moved calls in #2073 and by the metric alert path, e.g. `Failed to list
issue alert rules: 403 Forbidden`. They no longer start with `API
request failed:`, and they no longer have an `Endpoint:` line. Status
codes and the 401/403 enrichment are unchanged.
- Existing bug, not fixed here: `trace view org/project/<id>` prints
"Filtered to project", but the trace endpoint has returned spans from
every project since getsentry/sentry#87314.
- Docs gap: with `returnIds=true`, the replay-count response maps each
issue ID to a list of replay IDs. The documented schema describes
counts. The CLI keeps validating that response with its own schema.

### Verification
- `pnpm run tsc` and `pnpm run lint` pass.
- CLI tests: 12884 pass and 5 fail. All 5 are the sandbox-only
permission tests that fail on `main` too (`completions` ×2, `upgrade`
acquireLock, `db/resilient` readonly, `dsn/code-scanner`).
- Unit tests were updated or added for each changed request shape,
including the new `test/lib/api/code-mappings.test.ts`.
- API discovery no longer lists the moved call sites.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
BYK pushed a commit that referenced this pull request Nov 7, 2026
…2078)

Next step after #2073 and #2075, which moved the CLI onto the generated
`@sentry/api` operations. `@sentry/api` 1.46.0 includes the API docs
fixes from getsentry/sentry#130348, #130349 and #130350, so six more
call sites now match their generated operation. This PR bumps the
package and moves them.

### Moved onto generated operations
| Command(s) | Call | Operation | Unblocked by |
| --- | --- | --- | --- |
| `issue resolve/unresolve/archive`, `feedback resolve/spam/…`
(org-scoped) | `updateIssueStatus` | `updateOrganizationIssue` |
sentry#130348 (body fields no longer all required) |
| `issue merge` | `mergeIssues` | `updateOrganizationIssues` with
numeric `id`s and `{merge: true}` | sentry#130348 |
| `dashboard view` chart widgets | `queryWidgetTimeseries` |
`listOrganizationEventsTimeseries` | sentry#130349 (`yAxis` array,
`interval` int or duration) |
| `explore` time series | `queryEventsTimeSeries` |
`listOrganizationEventsTimeseries` | sentry#130349 |
| `agent-conversation list` | `listConversations` |
`listOrganizationAiConversations` with `sort` | sentry#130350 |
| `trace view`, `span view`, `log view` | `getDetailedTrace` |
`getOrganizationTrace` with `timestamp` | sentry#130349 |

Trace meta already used `getOrganizationTraceMeta` and doesn't send
`timestamp`, so nothing changes there.

### Behavior notes
- Responses are validated exactly as before:
`EventsTimeseriesResponseSchema` for widgets and
`ConversationListItemSchema` for conversations. The explore time series
and trace detail responses were not validated before and still aren't.
- `issue merge` still turns a 204 (no matching issues) into the same
friendly error, and still clears each affected issue's cross-origin
cache.
- `agent-conversation list --sort` is now typed with the endpoint's sort
enum. The command's sort values are a subset of it.
- Errors on the moved calls use the SDK wording, as in #2073 and #2075,
e.g. `Failed to update issue: 403 Forbidden`. Status codes and the
401/403 enrichment are unchanged.
- The 1.46.0 regen also changes `uniqueValues`/`key`/`name` descriptions
in the generated `issue` skill reference, which is committed here.
Typecheck is otherwise unaffected.

### Remaining `apiRequest*` call sites and raw fetches
Details for each are in getsentry/sentry#130351.

**Not in the public schema** (needs a getsentry/sentry API docs
decision):
- `organizations.ts:66`, `projects.ts:576/591`: region lookups
- `users.ts:21`: current user
- `trials.ts:30/70`: trials list/start
- `org-auth-tokens.ts:77`: delete org auth token
- `dashboards.ts:201/235`: dashboard revisions/restore
- `issues.ts:329`: unscoped `GET /issues/{id}/`
- `issues.ts:632`: legacy unscoped `PUT /issues/{id}/`
- `logs.ts:486`: logs query
- `traces.ts:225`: trace-items detail (experimental)
- `releases.ts:425`: release set-commits
- `preprod-artifacts.ts:283/449`: build/snapshot upload
- Raw fetches: `getSharedIssue` (`issues.ts`) and `openSnapshotArchive`
(`preprod-artifacts.ts`)

**Dynamic request identity / transport:**
- `auth.ts:17`
- `org-auth-tokens.ts:37/59`
- `preprod-artifacts.ts:508/526`: snapshot downloads

**Kept on purpose:**
- `replays.ts:201`: `replay list` keeps requesting
`info_ids`/`warning_ids` because `--json` prints them (see #2075).

The Oxlint rule that bans the helpers comes last, after these are
resolved.

### Verification
- `pnpm install --frozen-lockfile` passes with the committed lockfile.
- `pnpm run tsc` and `pnpm run lint` pass.
- CLI tests: 12919 pass and 5 fail. All 5 are the sandbox-only
permission tests that also fail on `main` (`completions` ×2, `upgrade`
acquireLock, `db/resilient` readonly, `dsn/code-scanner`).
- The dashboard time series tests now mock
`listOrganizationEventsTimeseries`. I added an org-scoped
`updateIssueStatus` test. The conversation, explore and trace tests run
at the fetch layer and pass unchanged.
- API discovery no longer lists the moved call sites.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
BYK added a commit to getsentry/sentry that referenced this pull request Nov 7, 2026
Publishes `GET
/api/0/organizations/{organization_id_or_slug}/trace-logs/`
(`OrganizationTraceLogsEndpoint`, owner Data Browsing) in the public API
schema, so it reaches the generated `@sentry/api` SDK.

This comes from the #130351 proposal. The Sentry CLI is moving every
call onto `@sentry/api` operations (getsentry/toolkit#2073), and `log
list` and `trace logs` still call this endpoint by hand because it isn't
in the schema.

Evidence:
- The endpoint has existed since #92501 (2025-05). #126626 documented it
fully with `extend_schema` but left it `PRIVATE`.
- Current callers: the trace view and logs explore pages
(`useTraceOverviewData`, `useLogsApiOptions`) and the CLI's `log list`
and `trace logs` commands.

The response type was `list[dict[str, Any]]`, which the
response-coverage check rejects for public operations. The endpoint
always selects the same eight columns, so each row is now a `TraceLog`
TypedDict, and every field has an `ApiDescription`. I checked the types
against a real response from the endpoint: `project.id` and
`timestamp_precise` are integers, `timestamp` is ISO 8601, and
`confidence` is one empty object per row because logs aren't aggregated.
The example's `timestamp_precise` was a float, so it's now an integer.
`full_scan`, `bytes_scanned` and `routingHint` are optional, because a
mocked or empty RPC meta can leave them out.

The only change to the built schema (`make build-api-docs`, diffed
against master) is the new `listOrganizationTraceLogs` operation and its
`TraceLogsResponse` component. The new apidocs test validates a stored
log against the schema.


`tests/snuba/api/endpoints/test_organization_trace_logs.py::test_simple`
failed once under `-n3` because tests share trace IDs in Snuba. It
passes when the file runs serially.

The "(Optional) Shows the difference between the prod and dev schema"
check is expected to fail, because this PR changes the schema.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A22c5e57fa755cece5122380dcb72ec8e -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A22c5e57fa755cece5122380dcb72ec8e)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A22c5e57fa755cece5122380dcb72ec8e/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
BYK added a commit that referenced this pull request Nov 7, 2026
…cated endpoints (#2090)

Fixes #2089.

The CLI still called two Sentry routes that were deprecated on
2026-05-15 for cell routing (`CELL_API_DEPRECATION_DATE`). On SaaS, both
can return 410 during brownouts:

- unscoped `GET`/`PUT /issues/{id}/`, for numeric issue lookups and
status updates without an org
- `GET /projects/?query=dsn:<key>`, for DSNs with no `o<id>` in the host

### Issues
- `getIssue` (unscoped) is removed. A numeric ID with no org now goes
through `findIssueInOrgs`, which asks the user's orgs concurrently
(`ORG_FANOUT_CONCURRENCY`) through `getOrganizationIssue`. No new
request starts once one org returns the issue. 404/403 count as "not
here". Any other error is thrown only if no org has the issue.
- `resolveOrgAndIssueId` caches the owning org as before. A stale cache
entry still triggers a fresh search.
- `updateIssueStatus` now requires `orgSlug` and always uses
`updateOrganizationIssue`. Every caller already had the org, so the dead
`!org` guards in `issue events/resolve/tags/merge` are gone.
- The unscoped completion sources and the legacy `/issues/{id}/` cache
invalidation in `mergeIssues` and `issue-links` are removed. Nothing
reads that cache anymore.
- There's no fallback to the unscoped route. Org-scoped issue routes
exist on every Sentry since 24.1.

### DSN lookup
- `findProjectByDsnKey` is replaced by `findProjectByDsn(dsn,
publicKey)`. It asks each of the user's orgs with `GET
/organizations/{org}/dsn-lookup/?dsn=`, routed to the org's region. The
endpoint is still private (getsentry/sentry#130351), so this stays on
`apiRequestToRegion` with a valibot schema until it's published.
- A 404 with `DSN not found` / `Invalid DSN` means "not in this org".
Any other 404 means the server has no dsn-lookup, which happens on
self-hosted from before 2026-02, or before the flag removal on 2026-04.
Then, and only on non-SaaS hosts, the CLI falls back to
`/projects/?query=dsn:`. Sentry doesn't brown out deprecated routes on
self-hosted. On SaaS the deprecated route is never called.
- It no longer needs `/users/me/regions/`.

### Cost
Both lookups now fan out per org instead of making one request (issues)
or one per region (DSN). They run only when there's no org context and
no cache hit, they reuse the cached org list, and they stop starting
requests at the first hit.

### Tests
- New `findProjectByDsn` suite in `api-client.multiregion.test.ts`:
found, miss, a per-org failure, the self-hosted fallback, and no
fallback on SaaS.
- `findIssueInOrgs` coverage tests.
- `issue/utils` tests for the org search, a stale cache, the cache
write, and a 5xx.
- Updated the resolver, resolve-target, completion-cache and issue-links
tests.

### Gate
`pnpm run tsc && pnpm run lint` pass. In `pnpm run test`, the CLI has 5
failures, and they're the known sandbox file-permission tests
(completions ×2, upgrade, db/resilient, dsn/code-scanner). Every other
package passes. `@sentry/cli3to4` can't run here because the codemod
binary needs GLIBC_2.35.

Related: #2073 (the CLI's move onto `@sentry/api` operations),
getsentry/sentry#130351 (the private endpoints the CLI uses, dsn-lookup
among them).

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
BYK added a commit that referenced this pull request Nov 7, 2026
… revision operations (#2093)

Next batch of moving the CLI onto `@sentry/api` generated operations
(follows #2073, #2075, #2078, #2090).

`@sentry/api` 1.49.0 publishes three endpoints the CLI already calls:

| CLI function | Endpoint | Operation | Published by |
| --- | --- | --- | --- |
| `listTraceLogs` (`log list --trace`, `trace logs`) | `GET
/organizations/{org}/trace-logs/` | `listOrganizationTraceLogs` |
getsentry/sentry#130357 |
| `listDashboardRevisionsPaginated` (`dashboard revisions`) | `GET
/organizations/{org}/dashboards/{id}/revisions/` |
`listOrganizationDashboardRevisions` | getsentry/sentry#130358 |
| `restoreDashboardRevision` (`dashboard restore`) | `POST
/organizations/{org}/dashboards/{id}/revisions/{rev}/restore/` |
`restoreOrganizationDashboardRevision` | getsentry/sentry#130358 |

These calls stop using `apiRequestToRegion` and behave the same as
before:
- The CLI's valibot schemas still validate each response, so schema
mismatches still fail as "Unexpected response format".
- Region routing goes through `getOrgSdkConfig`.
- Pagination still uses the Link-header cursors, including the previous
cursor for revisions.
- 401/403 errors are still enriched by `unwrapResult`.
- On the wire, `traceId` and `sort` are sent as single repeated params,
the same as before.
- The dashboard and revision ids are passed as strings with the same
cast `getDashboard` uses. The SDK URL-encodes them the way the old code
did.

All three operations have `experimental: false`.

Bumping to 1.49.0 also pulls in the 1.47.0 regen and 1.48.0's
`experimental` flag. Typecheck and generated docs don't change.

### Still on the helpers

| Call site | Endpoint | Waiting on |
| --- | --- | --- |
| `issues.ts` `getSharedIssue` (raw fetch) | `GET
/shared/issues/{share_id}/` | getsentry/sentry#130359 merged after the
1.49.0 regen, so it needs the next release |
| `releases.ts` set-commits | `GET
/organizations/{org}/releases/{version}/previous-with-commits/` | same
as above (#130359) |
| `org-auth-tokens.ts` (3 sites) | list/create/delete
`/organizations/{org}/org-auth-tokens/` | getsentry/sentry#130360
(auto-merging) |
| `projects.ts` dsn-lookup | `GET /organizations/{org}/dsn-lookup/` |
getsentry/sentry#130367 |
| `auth.ts` | `GET /api/0/` (control silo) | getsentry/sentry#130369 |
| `users.ts` | `GET /auth/` → `GET /users/me/` | getsentry/sentry#130368
|
| `organizations.ts` | `GET /users/me/regions/` | #2091 replaces it with
`listOrganizations` |
| `traces.ts` / `logs.ts` | `GET
/projects/{org}/{project}/trace-items/{id}/` | sentry publish PR still
to come |
| `preprod-artifacts.ts` | build assemble, snapshot archive status and
trigger, snapshot create (zstd body) | sentry publish PRs still to come
|
| `projects.ts` | `GET /projects/?query=dsn:` fallback for old
self-hosted servers | not public; needs a decision (see below) |

These are the agreed exceptions: `trials.ts` (`/customers/*` and the
product-trial PUT), snapshot `upload-options` and archive `?download`,
`downloadBuildArtifact`'s install URL, chunk-upload server URLs, and
`rawApiRequest` for `sentry api`.

**Needs a decision:** the self-hosted DSN fallback from #2090 calls `GET
/projects/?query=dsn:`, which isn't in the public schema. It needs to be
added to the exception list for the lint rule, or dropped once old
self-hosted versions age out.

### Testing

- `pnpm run tsc` and `pnpm run lint` pass.
- `pnpm run test` fails only on the 5 file-permission tests that also
fail on `main` in the sandbox (`completions` ×2, `upgrade`,
`db/resilient`, `dsn/code-scanner`), plus `@sentry/cli3to4`, whose
codemod binary needs GLIBC 2.35.
- Added unit tests for the revision list (cursors, schema rejection) and
for restore (operation args, error naming). The trace-logs test now
checks `traceId` and `sort` on the wire.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
BYK added a commit that referenced this pull request Nov 9, 2026
…eveApiIndex (#2118)

Bumps `@sentry/api` from 1.53.0 to 1.55.0 and moves the CLI's `GET
/api/0/` call onto the generated `retrieveApiIndex` operation.
getsentry/sentry#130369 published that endpoint, and 1.55.0 ships it.

## Change

`getCurrentAuthScopes` (`packages/cli/src/lib/api/auth.ts`) feeds scope
recovery. It used to call the raw
`apiRequestToRegion(getControlSiloUrl(), "")` helper. It now calls
`retrieveApiIndex` with `getSdkClientConfig(getControlSiloUrl())`, then
`unwrapValidatedResult` with the same valibot schema. This is the same
pattern as `org-auth-tokens.ts`.

Behavior is unchanged:
- It still goes to the control silo URL. That's the configured server
URL on self-hosted, so self-hosted works as before.
- `auth: null` still returns `null`. Sentry sends that for anonymous
requests, session requests and requests with no token, and scope
recovery treats it as `invalid`.
- A 401 still comes back as an `ApiError` with `status: 401`. That's the
case `scope-recovery.ts` checks. CLI errors thrown by the fetch layer,
such as `AuthError`, still pass through `unwrapResult` unchanged.

With this change, the API discovery step in `typecheck` no longer
reports `auth.ts` as "not in the public catalog".

## Bump

- `pnpm-workspace.yaml`: the catalog and `minimumReleaseAgeExclude` both
move to `@sentry/api@1.55.0`.
- `pnpm-lock.yaml`: the `@sentry/api` entries move to 1.55.0 and its
integrity. 1.55.0 has the same dependencies and peer dependencies as
1.53.0.
- `pnpm install --no-frozen-lockfile` wouldn't rewrite these entries.
pnpm's lockfile age check rejected the old 1.53.0 entry before it
resolved anything, even with 1.53.0 temporarily excluded, so I edited
the entries by hand.
- `pnpm install --frozen-lockfile` passes on the result, age policy
check included.
- 1.54.0 and 1.55.0 cause no other type errors. Neither the CLI docs nor
the skill references changed when regenerated.

## Still waiting on sentry
- dsn-lookup: getsentry/sentry#130367
- `/auth/` → `GET /users/me/`, falling back to `/auth/` on a 403 from
older self-hosted servers: getsentry/sentry#130368
- trace items (`traces.ts`, logs): getsentry/sentry#130373
- After those, the no-allowlist Oxlint rule.

## Gate (local sandbox)
- `pnpm run tsc`: passes.
- `pnpm run lint`: passes.
- Tests:
- These all pass: mcp-core, mcp-cloudflare, mcp-test-client,
toolkit-core, logger, apps/local, apps/docs-mcp, apps/games-api and
apps/cli-docs.
- CLI: 12952 pass and 5 fail. All 5 are the known sandbox-only
permission cases: completions ×2, upgrade lock, resilient DB and the
code-scanner unreadable-file test.
- `@sentry/cli3to4` can't run here, because its `codemod` binary needs
GLIBC 2.35.
- `test/lib/api/auth.test.ts` now mocks `retrieveApiIndex` and covers
scopes, `auth: null`, and the 401.

No UI change, so no screenshots.

Part of the `@sentry/api` migration that started in #2073 (tracking
issue: getsentry/sentry#130351).

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Burak Yigit Kaya <126780+BYK@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview — d2dad465 Deployed Nov 7, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant