# @sentry/api

The official, auto-generated TypeScript client for Sentry's public REST API.

[![npm](https://img.shields.io/npm/v/@sentry/api.svg)](https://www.npmjs.com/package/@sentry/api)
[![license](https://img.shields.io/npm/l/@sentry/api.svg)](./LICENSE.md)

## Install

```bash
npm install @sentry/api
```

## Usage

Pass `baseUrl` and an auth header to each call:

```ts
import { listYourOrganizations } from "@sentry/api";

const { data, error } = await listYourOrganizations({
  baseUrl: "https://sentry.io",
  headers: { Authorization: `Bearer ${process.env.SENTRY_AUTH_TOKEN}` },
});

if (error) throw error;
console.log(data);
```

Auth tokens and base URLs (including self-hosted and region URLs) are documented at https://docs.sentry.io/api/auth/.

## Runtime validation

The root `@sentry/api` entry has no runtime dependencies. It provides the API client and pure TypeScript types without installing a validation library.

Valibot 1 runtime schemas are available through a separate optional entry point:

```bash
npm install @sentry/api valibot
```

```ts
import * as v from "valibot";
import { vGetProjectResponse } from "@sentry/api/valibot";

const project = v.parse(vGetProjectResponse, input);
```

The existing Zod 3 entry remains supported:

```bash
npm install @sentry/api zod
```

```ts
import { zGetProjectResponse } from "@sentry/api/zod";

const project = zGetProjectResponse.parse(input);
```

`valibot` and `zod` are optional peer dependencies. Install neither when you only need the generated client and TypeScript types, or install the validator used by your application. The `@sentry/api/valibot` entry requires Valibot 1; its optional peer uses a wildcard because npm applies peer constraints to the whole package, including consumers that never import this entry.

## Error handling

Every operation with documented error responses has a generated `narrowError_<operation>` wrapper. It returns data or a `SentryApiError` that preserves the operation's status-to-body type map:

```ts
import { narrowError_getProject } from "@sentry/api";

const result = await narrowError_getProject({
  baseUrl: "https://sentry.io",
  headers: { Authorization: `Bearer ${process.env.SENTRY_AUTH_TOKEN}` },
  path: {
    organization_id_or_slug: "my-org",
    project_id_or_slug: "my-project",
  },
});

if (!result.ok) {
  if (!result.error.documented) {
    // Unexpected HTTP status or a transport failure.
    throw result.error;
  }

  switch (result.error.status) {
    case 403:
    case 404:
      throw result.error;
  }
}
```

Checking `documented` first separates the operation's finite error union from unexpected statuses and transport failures. Within the documented branch, checking `status` narrows `body` to that response's schema. Error bodies remain `unknown` where the source OpenAPI response has no schema.

## Pagination

Sentry uses cursor-based pagination via `Link` headers. Every operation in the SDK that accepts a `cursor` query parameter has three auto-generated typed wrappers:

- `fetchPage_<operation>(options, cursor?)` — fetch a single page; returns `{ data, nextCursor?, prevCursor? }`.
- `paginateAll_<operation>(options, paginateOptions?)` — eagerly fetch all pages, returning the concatenated array. Bounded by `maxPages` (default 50) for safety. Available only for endpoints whose 200 response is `Array<...>`.
- `paginateUpTo_<operation>(options, paginateOptions)` — fetch up to a hard `limit` of items; suppresses `nextCursor` when the last page is trimmed (so callers resuming pagination won't skip records). Available only for endpoints whose 200 response is `Array<...>`.

The wrappers manage `cursor` for you — passing one in `query` is a type error. Every wrapper's `query` is also widened with an optional `per_page?: number` field, since Sentry's pagination framework accepts `per_page` on every cursor-paginated route at runtime even when the spec omits it.

### Single page

```ts
import { fetchPage_listAnOrganization_sIssues } from "@sentry/api";

const { data, nextCursor } = await fetchPage_listAnOrganization_sIssues({
  baseUrl: "https://sentry.io",
  headers: { Authorization: `Bearer ${process.env.SENTRY_AUTH_TOKEN}` },
  path: { organization_id_or_slug: "my-org" },
  query: { collapse: ["stats"], limit: 25 },
});
```

### All pages

```ts
import { paginateAll_listAnOrganization_sProjects } from "@sentry/api";

const projects = await paginateAll_listAnOrganization_sProjects({
  baseUrl: "https://sentry.io",
  headers: { Authorization: `Bearer ${process.env.SENTRY_AUTH_TOKEN}` },
  path: { organization_id_or_slug: "my-org" },
});
```

### Bounded pagination

```ts
import { paginateUpTo_listAnOrganization_sIssues } from "@sentry/api";

const { data, nextCursor } = await paginateUpTo_listAnOrganization_sIssues(
  {
    baseUrl: "https://sentry.io",
    headers: { Authorization: `Bearer ${process.env.SENTRY_AUTH_TOKEN}` },
    path: { organization_id_or_slug: "my-org" },
    query: { limit: 100 },
  },
  {
    limit: 250,
    onPage: (fetched, target) => console.log(`fetched ${fetched}/${target}`),
  },
);
```

By default, `paginateUpTo` drops `nextCursor` if the last fetched page had to be trimmed to fit `limit` — returning a cursor that points past the trimmed items would cause callers resuming pagination to skip records. For endpoints with no server-side `per_page` control (e.g. `/issues/{id}/events/`), pass `keepCursorOnOvershoot: true` to preserve the cursor; the trimmed-tail items remain reachable via the same cursor on the next call.

`nextCursor` is also dropped if `paginateUpTo` reaches `maxPages` (default 50) before fulfilling `limit` — raise `maxPages` to continue paginating.

### Generic pagination helpers

The same low-level helpers used by the generated wrappers are also exported for advanced use cases:

- `parseSentryLinkHeader(header)` — `{ nextCursor?, prevCursor? }`
- `unwrapResult(sdkResult, context)` — throw-on-error data unwrap
- `unwrapPaginatedResult(sdkResult, context)` — same but with cursors
- `fetchPage`, `paginateAll`, `paginateUpTo` — generic versions taking a fetcher thunk

## Schema source

The OpenAPI schema is synced from [`getsentry/sentry`](https://github.com/getsentry/sentry/tree/master/api-docs). Schema fixes belong there; build/tooling changes belong here.

## License

FSL-1.1-Apache-2.0. See [LICENSE.md](LICENSE.md).
