npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rustrak/client

v0.22.2

Published

Type-safe TypeScript client for the Rustrak self-hosted error tracking API

Readme


@rustrak/client is the official TypeScript client for Rustrak — an ultra-lightweight, self-hosted error tracking system compatible with any Sentry SDK. It is generated from the server's OpenAPI spec, so it covers every endpoint the server documents, and wraps each one with runtime validation, retries and structured errors. No runtime dependency besides Zod; HTTP is the platform's fetch.

Installation

npm install @rustrak/client
# or
pnpm add @rustrak/client
# or
yarn add @rustrak/client

Requirements: Node.js ≥ 20 (or any runtime with fetch), TypeScript ≥ 5

Quick Start

import { RustrakClient } from '@rustrak/client';

const client = new RustrakClient({
  baseUrl: 'https://your-rustrak-instance.example.com',
  token: process.env.RUSTRAK_API_TOKEN!,
});

// Every method returns a Result. Nothing throws for an expected failure.
const projects = await client.projects.list();
if (!projects.success) {
  console.error(projects.error.kind, projects.error.message);
  return;
}
console.log(projects.data.items);

// Path parameters, query and body go in one object, named as the API names them
const issues = await client.issues.list({
  path: { project_id: 1 },
  query: { sort: 'last_seen', order: 'desc', per_page: 50 },
});

// Resolve an issue
const resolved = await client.issues.update({
  path: { project_id: 1, issue_id: 'issue-id' },
  body: { status: 'resolved' },
});

Full documentation →

Features

  • Complete: one method per operation in the server's OpenAPI spec, regenerated with the server, never written by hand
  • Validated both ways: requests and responses are checked against Zod schemas generated from the same spec; a mismatch is a typed error, not a crash
  • No exceptions: every method returns Result<T, RustrakError>, so failure is in the type, and the value survives the React server/client boundary
  • Tree-shakeable: ESM is published one module per file with sideEffects: false
  • Retries: exponential backoff on 408 and 5xx for idempotent methods (GET, PUT, DELETE)
  • Redacted 5xx: a server error never carries a server-supplied message, so internal detail cannot reach a UI

Configuration

const client = new RustrakClient({
  baseUrl: 'https://rustrak.example.com', // required
  token: 'your-bearer-token',             // optional: omit to use the session cookie
  timeout: 30000,                         // optional, ms per attempt (default: 30000)
  maxRetries: 2,                          // optional (default: 2)
  headers: {},                            // optional custom headers
});

Every method takes the request's own parts and nothing else: path, query, body, plus headers and an AbortSignal as signal.

Error Handling

Methods do not throw. Each returns a Result, a plain discriminated union mirroring Zod's safeParse:

type Result<T, E> =
  | { success: true;  data: T }
  | { success: false; error: E };

Failures are one closed union keyed on kind, so the compiler forces you to handle the case you care about and lets you ignore the rest deliberately:

import { isRetryable, type RustrakError } from '@rustrak/client';

const result = await client.projects.list();

if (!result.success) {
  switch (result.error.kind) {
    case 'unauthenticated':
      redirect('/login');
      break;
    case 'rate_limited':
      console.log(`Retry after ${result.error.retryAfter ?? '?'}s`);
      break;
    case 'not_found':
      console.log('Gone');
      break;
    default:
      if (isRetryable(result.error)) scheduleRetry();
      else console.error(result.error.message);
  }
  return;
}

console.log(result.data);

The error union

| kind | HTTP | status | Retryable | Notes | |---|---|---|---|---| | validation | 400 | ✅ | ❌ | The server rejected the request | | unauthenticated | 401 | ✅ | ❌ | No session, or it expired | | forbidden | 403 | ✅ | ❌ | Authenticated but not allowed | | not_found | 404 | ✅ | ❌ | | | conflict | 409 | ✅ | ❌ | | | gone | 410 | ✅ | ❌ | | | payload_too_large | 413 | ✅ | ❌ | Envelope ingestion only | | rate_limited | 429 | ✅ | ✅ | Optional retryAfter in seconds | | client_error | other 4xx | ✅ | ❌ | Catch-all, so no status is unrepresentable | | server_error | 5xx | ✅ | ✅ | Message is always a fixed generic string | | network | n/a | no | ✅ | DNS, refused connection, TLS, timeout | | invalid_response | n/a | no | ❌ | 2xx whose body failed its schema | | invalid_request | n/a | no | ❌ | Your input failed its schema; nothing was sent |

status is number, never a literal, so a proxy-generated 502 or a future status is a value you can log rather than a type error.

Three things the union deliberately does not carry, because each embeds information that must not cross a serialization boundary:

  • server_error never carries the server's message. AppError::Internal and AppError::Database interpolate arbitrary internal text (a pool error, an OS errno, a filesystem path); the client discards it at construction and substitutes SERVER_ERROR_MESSAGE.
  • network carries no cause. The underlying error embeds the resolved host and port.
  • invalid_response carries no Zod issues. They embed the offending response data.

Result helpers

import { Ok, Err, unwrap, unwrapOr, mapResult } from '@rustrak/client';

const project = await client.projects.get({ path: { id: 1 } });
mapResult(project, (p) => p.name);
unwrap(project); // throws on failure: opting back in
unwrapOr(project, null); // see the warning below

unwrapOr is not for rendering. unwrapOr(await client.projects.list(), []) shows the same empty state for "this account has no projects" and "the server is unreachable": the exact confusion the Result API exists to prevent, one line after the failure was produced. Use it only where the fallback is correct regardless of why the call failed (a cached-count optimisation, a best-effort telemetry read, a script that already logged the error). Anywhere a human sees the outcome, branch on result.success and read error.kind.

Every value in a Result is a plain object with Object.prototype. That is what lets a failure be returned from a Server Component or a Server Action and read in a Client Component: React's Flight serializer rejects class instances, which is why there are no error classes here.

TypeScript

Every request and response type in the spec is exported under the name the spec gives it:

import type {
  IssueResponse,     // a schema
  ProjectResponse,
  IssuesListData,    // what `client.issues.list` takes
  IssuesListResponse, // what it returns on success
  Result,
  RustrakError,
} from '@rustrak/client';

API Reference

Every method, grouped as it hangs off the client (client.issues.list(...)).

Agents

| Method | Endpoint | Description | |---|---|---| | agents.conversation({ path }) | GET /api/projects/{project_id}/agents/conversations/{conversation_id} | Returns every AI span of a conversation's turns, oldest first, with its input and output. | | agents.conversations({ path, query? }) | GET /api/projects/{project_id}/agents/conversations | Lists conversations -- traces sharing a gen_ai.conversation.id -- the most recently active first. | | agents.duration({ path, query? }) | GET /api/projects/{project_id}/agents/duration | Time-bucketed avg/p95 duration for agent/ai_client spans. | | agents.environments({ path }) | GET /api/projects/{project_id}/agents/environments | The options the environment filter offers, read from the data rather than hardcoded — an installation may name its environments anything. | | agents.modelsCalls({ path, query? }) | GET /api/projects/{project_id}/agents/models/calls | Top models by LLM call count (gen_ai.operation.type:ai_client). | | agents.modelsTable({ path, query? }) | GET /api/projects/{project_id}/agents/models | | | agents.modelsTokens({ path, query? }) | GET /api/projects/{project_id}/agents/models/tokens | Top models by total tokens used (gen_ai.operation.type:ai_client). | | agents.runs({ path, query? }) | GET /api/projects/{project_id}/agents/runs | Time-bucketed count of agent-run spans (gen_ai.operation.type:agent). | | agents.summary({ path, query? }) | GET /api/projects/{project_id}/agents/summary | Agent runs, LLM calls, tool calls, errors, tokens and latency over the selected window — the numbers the six charts cannot state outright. | | agents.tools({ path, query? }) | GET /api/projects/{project_id}/agents/tools | Top tools by call count (gen_ai.operation.type:tool). | | agents.toolsTable({ path, query? }) | GET /api/projects/{project_id}/agents/tools/stats | | | agents.traces({ path, query? }) | GET /api/projects/{project_id}/agents/traces | Paginated per-trace_id aggregate (duration, tokens, tool usage) across all AI spans sharing that trace, regardless of origin. |

Alert Channels

| Method | Endpoint | Description | |---|---|---| | alertChannels.create({ body }) | POST /api/integrations | | | alertChannels.delete({ path }) | DELETE /api/integrations/{id} | | | alertChannels.get({ path }) | GET /api/integrations/{id} | | | alertChannels.list() | GET /api/integrations | | | alertChannels.previewTemplate({ body }) | POST /api/integrations/preview-template | Renders a Custom Webhook template against a sample payload and hands back the body that would be sent. | | alertChannels.test({ path, body }) | POST /api/integrations/{id}/test | | | alertChannels.update({ path, body }) | PATCH /api/integrations/{id} | |

Alert Rules

| Method | Endpoint | Description | |---|---|---| | alertRules.create({ path, body }) | POST /api/projects/{project_id}/alert-rules | | | alertRules.delete({ path }) | DELETE /api/projects/{project_id}/alert-rules/{rule_id} | | | alertRules.get({ path }) | GET /api/projects/{project_id}/alert-rules/{rule_id} | | | alertRules.historyByDay({ path, query? }) | GET /api/projects/{project_id}/alert-history/daily | Count a project's alert deliveries per day, by outcome. | | alertRules.list({ path }) | GET /api/projects/{project_id}/alert-rules | | | alertRules.listHistory({ path, query? }) | GET /api/projects/{project_id}/alert-history | | | alertRules.update({ path, body }) | PATCH /api/projects/{project_id}/alert-rules/{rule_id} | |

Auth

| Method | Endpoint | Description | |---|---|---| | auth.acceptInvitation({ body }) | POST /auth/accept-invitation | Accept a pending invitation: creates the user (with the invite's email + role) and logs in. | | auth.changePassword({ body }) | POST /auth/me/password | Change the current user's password | | auth.confirmSsoLink({ body }) | POST /auth/sso/link | Link the pending SSO identity to its account once the account's password is given, and sign in. | | auth.getCurrentUser() | GET /auth/me | Get current authenticated user | | auth.getInvitation({ path }) | GET /auth/invitation/{token} | Returns the invitation's details if it is still acceptable (for the accept page). | | auth.login({ body }) | POST /auth/login | Authenticate user and create session | | auth.logout() | POST /auth/logout | Clear session | | auth.register({ body }) | POST /auth/register | Registration is invite-only — always rejected. | | auth.ssoCallback({ query? }) | GET /auth/sso/callback | Validate the provider response, resolve or provision the local account, and establish the normal Rustrak session. | | auth.ssoConfig() | GET /auth/sso/config | Public, non-sensitive information used to render the login page. | | auth.ssoLink() | GET /auth/sso/link | The account an SSO login matched, shown while its owner confirms. | | auth.ssoStart() | POST /auth/sso/start | Create a one-time OIDC authorization request and retain its protections in the encrypted session cookie. | | auth.updateCurrentUser({ body }) | PATCH /auth/me | Update the current user's presentation preferences |

Events

| Method | Endpoint | Description | |---|---|---| | events.downloadAttachment({ path }) | GET /api/projects/{project_id}/attachments/{attachment_id} | Downloads an attachment. | | events.get({ path }) | GET /api/projects/{project_id}/issues/{issue_id}/events/{event_id} | Gets a single event with full data | | events.getBySentryId({ path }) | GET /api/projects/{project_id}/events/sentry/{event_id} | Resolve the ID returned by a Sentry SDK without already knowing the issue. | | events.list({ path, query? }) | GET /api/projects/{project_id}/issues/{issue_id}/events | Lists events for an issue with cursor-based pagination | | events.listAttachments({ path }) | GET /api/projects/{project_id}/issues/{issue_id}/events/{event_id}/attachments | Lists the files sent with an event, such as a feedback's screenshot. |

GitHub

| Method | Endpoint | Description | |---|---|---| | github.callback({ query }) | GET /api/github/callback | GitHub sends the browser here after creating the app; the code trades for its credentials. | | github.createIssue({ path, body }) | POST /api/projects/{project_id}/issues/{issue_id}/github/issues | Opens a GitHub issue and links it. | | github.deleteProjectRepository({ path }) | DELETE /api/projects/{project_id}/github | | | github.disconnect() | DELETE /api/github | | | github.getMyLogin() | GET /api/github/me | The GitHub username assignees sync through. | | github.getProjectRepository({ path }) | GET /api/projects/{project_id}/github | | | github.installed({ query }) | GET /api/github/installed | GitHub sends the browser here after the app is installed on an account. | | github.issue({ path }) | GET /api/projects/{project_id}/issues/{issue_id}/github | The issue's links, the commits that may have caused it, and who owns its culprit file. | | github.linkIssue({ path, body }) | POST /api/projects/{project_id}/issues/{issue_id}/github/links | Links an existing GitHub issue or pull request. | | github.manifest({ body }) | POST /api/github/manifest | What the dashboard posts to GitHub so GitHub creates an app already configured for this server. | | github.putMyLogin({ body }) | PUT /api/github/me | | | github.putProjectRepository({ path, body }) | PUT /api/projects/{project_id}/github | | | github.repositories({ query }) | GET /api/github/repositories | The repositories a project can point at, for whoever may change that project's settings. | | github.status() | GET /api/github | | | github.syncCodeowners({ path }) | POST /api/projects/{project_id}/github/codeowners | Copies the repository's CODEOWNERS so ownership can be answered without asking GitHub each time. | | github.unlinkIssue({ path }) | DELETE /api/projects/{project_id}/issues/{issue_id}/github/links/{link_id} | | | github.webhook({ body }) | POST /api/github/webhook | Public, and trusted only through the signature GitHub computes with the app's webhook secret. |

Health

| Method | Endpoint | Description | |---|---|---| | health.liveness() | GET /health | Liveness check - is the process running? | | health.readiness() | GET /health/ready | Readiness check - is the service ready to handle requests? | | health.version() | GET /health/version | Version check - returns the server version compiled from Cargo.toml. |

Invitations

| Method | Endpoint | Description | |---|---|---| | invitations.create({ body }) | POST /api/invitations | Create an invitation (admin only) | | invitations.list() | GET /api/invitations | List invitations (admin only) | | invitations.revoke({ path }) | DELETE /api/invitations/{token} | Revoke a pending invitation (admin only) |

Issues

| Method | Endpoint | Description | |---|---|---| | environments.list({ path }) | GET /api/projects/{project_id}/environments | Lists the environments a project's issues have been seen in, by name. | | feedback.list({ path, query? }) | GET /api/projects/{project_id}/feedback | Lists user feedback with what each user submitted. | | issues.bulkDelete({ path, body }) | DELETE /api/projects/{project_id}/issues | Bulk delete by ids or filter | | issues.bulkUpdate({ path, body }) | PUT /api/projects/{project_id}/issues | Bulk mutate | | issues.createComment({ path, body }) | POST /api/projects/{project_id}/issues/{issue_id}/comments | Add a note. | | issues.createUserReport({ path, body }) | POST /api/projects/{project_id}/issues/{issue_id}/user-reports | Attach user feedback to an issue. | | issues.delete({ path }) | DELETE /api/projects/{project_id}/issues/{issue_id} | Hard-deletes an issue and all associated events | | issues.get({ path }) | GET /api/projects/{project_id}/issues/{issue_id} | Gets a single issue by ID | | issues.getActivity({ path }) | GET /api/projects/{project_id}/issues/{issue_id}/activity | Chronological activity log (incl. comments). | | issues.getAggregates({ path }) | GET /api/projects/{project_id}/issues/{issue_id}/aggregates | Returns the unique user count and top tags for the issue. | | issues.getHashes({ path }) | GET /api/projects/{project_id}/issues/{issue_id}/hashes | Lists the grouping hashes that map to an issue. | | issues.getStats({ path, query? }) | GET /api/projects/{project_id}/issues/{issue_id}/stats | Zero-filled event-count timeseries. | | issues.getTagValues({ path }) | GET /api/projects/{project_id}/issues/{issue_id}/tags/{key} | Lists the distinct values (with counts) for a tag key across the issue. | | issues.list({ path, query? }) | GET /api/projects/{project_id}/issues | Lists issues for a project with offset-based pagination | | issues.listUserReports({ path, query? }) | GET /api/projects/{project_id}/issues/{issue_id}/user-reports | List user feedback for an issue. | | issues.markSeen({ path }) | POST /api/projects/{project_id}/issues/{issue_id}/seen | Mark the issue as seen by the current user. | | issues.setBookmark({ path, body }) | PUT /api/projects/{project_id}/issues/{issue_id}/bookmark | Set/clear the per-user bookmark. | | issues.setSubscription({ path, body }) | PUT /api/projects/{project_id}/issues/{issue_id}/subscription | Set/clear the per-user subscription. | | issues.update({ path, body }) | PATCH /api/projects/{project_id}/issues/{issue_id} | Updates issue state (resolve, mute, etc.) |

Logs

| Method | Endpoint | Description | |---|---|---| | logs.get({ path }) | GET /api/projects/{project_id}/logs/{log_id} | Returns one of a project's logs, wherever it falls in the list. | | logs.list({ path, query? }) | GET /api/projects/{project_id}/logs | Lists logs for a project with offset-based pagination (newest first by log timestamp), optionally filtered by level, trace id, message and window. | | logs.volume({ path, query? }) | GET /api/projects/{project_id}/logs/volume | Counts a project's logs per level over time, with the list's filters. |

Members

| Method | Endpoint | Description | |---|---|---| | members.list({ path }) | GET /api/projects/{project_id}/members | | | members.remove({ path }) | DELETE /api/projects/{project_id}/members/{user_id} | | | members.upsert({ path, body }) | PUT /api/projects/{project_id}/members | |

Monitors

| Method | Endpoint | Description | |---|---|---| | monitors.create({ path, body }) | POST /api/projects/{project_id}/monitors | | | monitors.delete({ path }) | DELETE /api/projects/{project_id}/monitors/{slug} | | | monitors.get({ path }) | GET /api/projects/{project_id}/monitors/{slug} | | | monitors.list({ path }) | GET /api/projects/{project_id}/monitors | | | monitors.listCheckins({ path, query? }) | GET /api/projects/{project_id}/monitors/{slug}/checkins | | | monitors.stats({ path, query? }) | GET /api/projects/{project_id}/monitor-stats | Counts the check-ins of a project's monitors by status over a window, bucket by bucket. | | monitors.update({ path, body }) | PUT /api/projects/{project_id}/monitors/{slug} | |

OAuth

| Method | Endpoint | Description | |---|---|---| | oauth.decideAuthorization({ body }) | POST /api/oauth/authorize | Approves or denies an authorization request as the signed-in user. | | oauth.getAuthorization({ query }) | GET /api/oauth/authorize | Verifies an authorization request and says who is asking. | | oauth.listGrants() | GET /api/oauth/grants | Lists the assistants connected to the signed-in user's account. | | oauth.revokeGrant({ path }) | DELETE /api/oauth/grants/{id} | Disconnects an assistant: its tokens stop working at once. |

Projects

| Method | Endpoint | Description | |---|---|---| | projects.create({ body }) | POST /api/projects | Create a new project | | projects.delete({ path }) | DELETE /api/projects/{id} | Delete a project | | projects.get({ path }) | GET /api/projects/{id} | Get a project by ID | | projects.getRateLimits() | GET /api/rate-limits | What a project without limits of its own follows | | projects.list({ query? }) | GET /api/projects | List projects with pagination | | projects.update({ path, body }) | PATCH /api/projects/{id} | Update a project |

Releases

| Method | Endpoint | Description | |---|---|---| | releases.create({ path, body }) | POST /api/0/projects/{org_slug}/{project_slug}/releases/ | Sentry-compatible release creation. | | releases.finalize({ path, body }) | PUT /api/0/projects/{org_slug}/{project_slug}/releases/{version}/ | Generic partial update (ref, url, dateReleased). | | releases.get({ path, query? }) | GET /api/projects/{project_id}/releases/{release} | Gets one release with its events, new issues, environments and session health. | | releases.list({ path, query? }) | GET /api/projects/{project_id}/releases | Lists a project's releases, newest first, with the events, new issues, environments and session health of each. | | releases.newIssues({ path, query? }) | GET /api/projects/{project_id}/releases/{release}/new-issues | |

Schedules

| Method | Endpoint | Description | |---|---|---| | schedules.preview({ body }) | POST /api/schedules/preview | Validates a schedule and lists its next five runs from now, so no client ever parses a crontab itself. |

Sentry API

| Method | Endpoint | Description | |---|---|---| | sentry.getIssue({ path }) | GET /api/0/issues/{issue_id}/ | One issue with its releases and its 24h and 30d series (Sentry's GroupDetailsEndpoint). | | sentry.getLatestEvent({ path }) | GET /api/0/issues/{issue_id}/events/latest/ | The issue's most recent event (Sentry's GroupEventDetailsEndpoint, issues/endpoints/group_event_details.py). | | sentry.listIssues({ path, query? }) | GET /api/0/projects/{org_slug}/{project_slug}/issues/ | A project's issues, filtered by query, sorted by sort and paginated by cursor (Sentry's ProjectGroupIndexEndpoint, issues/endpoints/project_group_index.py). | | sentry.listOrganizations() | GET /api/0/organizations/ | The organizations the token belongs to (Sentry's OrganizationIndexEndpoint): always the one synthetic org. | | sentry.listProjects({ query? }) | GET /api/0/projects/ | The projects the token can see, newest first (Sentry's ProjectIndexEndpoint, core/endpoints/project_index.py). | | sentry.listRepos({ path }) | GET /api/0/organizations/{org_slug}/repos/ | The code repositories connected to the organization (Sentry's OrganizationRepositoriesEndpoint). | | sentry.openIssue({ path }) | GET /{org_slug}/{project_slug}/issues/{issue_id} | The issue page GitLab's "View in Sentry" opens: Sentry redirects this legacy path to the issue (static/app/router/routes.tsx), Rustrak to its dashboard. | | sentry.submitUserFeedback({ path, body }) | POST /api/0/projects/{org_slug}/{project_slug}/user-feedback/ | Submit a user report about an error event (Sentry's ProjectUserReportsEndpoint, feedback/endpoints/project_user_reports.py). | | sentry.updateIssue({ path, body }) | PUT /api/0/issues/{issue_id}/ | Sets an issue's status and returns the issue (Sentry's GroupDetailsEndpoint.put). |

Sessions

| Method | Endpoint | Description | |---|---|---| | sessions.getStats({ path, query? }) | GET /api/projects/{project_id}/sessions/stats | | | sessions.getSummary({ path, query? }) | GET /api/projects/{project_id}/sessions/summary | | | sessions.getTimeseries({ path, query? }) | GET /api/projects/{project_id}/sessions/timeseries | |

Settings

| Method | Endpoint | Description | |---|---|---| | settings.getMcp() | GET /api/settings/mcp | Whether the MCP endpoint at /mcp is on. | | settings.updateMcp({ body }) | PUT /api/settings/mcp | Turns the MCP endpoint at /mcp on or off. |

Source Maps

| Method | Endpoint | Description | |---|---|---| | debugFiles.assemble({ path, body }) | POST /api/0/projects/{org_slug}/{project_slug}/files/difs/assemble/ | Assemble uploaded chunks into debug information files (ProGuard mappings, JVM source bundles). | | debugFiles.upload({ path, body }) | POST /api/0/projects/{org_slug}/{project_slug}/files/dsyms/ | Upload a zip of debug information files (the path sentry-cli upload-proguard takes). | | sourceMaps.artifactBundleAssemble({ path, body }) | POST /api/0/organizations/{org_slug}/artifactbundle/assemble/ | | | sourceMaps.chunkUpload({ path, body }) | POST /api/0/organizations/{org_slug}/chunk-upload/ | | | sourceMaps.chunkUploadCapability({ path }) | GET /api/0/organizations/{org_slug}/chunk-upload/ | | | sourceMaps.list({ path }) | GET /api/0/projects/{org_slug}/{project_slug}/files/source-maps/ | | | sourceMaps.orgDetails({ path }) | GET /api/0/organizations/{org_slug}/ | Always returns 200 with synthetic org data — sentry-cli validates this before uploading. |

Spans

| Method | Endpoint | Description | |---|---|---| | spans.get({ path }) | GET /api/projects/{project_id}/spans/{span_id} | Returns one span together with its attributes and tags — the gen_ai.* payload (prompts, responses, tool arguments and results) that the list response deliberately omits, since spans.data is untrimmed and a whole trace's worth of it would dwarf the waterfall it is drawn from. | | spans.list({ path, query? }) | GET /api/projects/{project_id}/spans | Lists spans for a project with offset-based pagination (newest by start_timestamp first), optionally filtered by op/status/trace_id. |

Stats

| Method | Endpoint | Description | |---|---|---| | stats.getEventTimeseries({ path, query? }) | GET /api/projects/{project_id}/events/stats | | | stats.getSummary({ path, query? }) | GET /api/projects/{project_id}/stats/summary | |

Storage

| Method | Endpoint | Description | |---|---|---| | storage.compact() | POST /api/storage/compaction | Rewrites a SQLite file created before incremental mode, so it gives freed space back from then on. | | storage.executeCleanup({ body }) | POST /api/storage/cleanup | Starts deleting old data and the issues it empties. | | storage.gcSourceMaps() | POST /api/storage/source-maps/gc | Delete orphaned source maps (DB + disk). | | storage.getAge() | GET /api/storage/age | Rows per age bucket, per project and kind. | | storage.getCleanupStatus() | GET /api/storage/cleanup/status | Progress of the running cleanup, or the outcome of the last one. | | storage.getCompaction() | GET /api/storage/compaction | Whether the database file shrinks when data is deleted, and the disk a one-off compaction needs. | | storage.getProjects() | GET /api/storage/projects | Per-project counts and estimated weight. | | storage.getRetention() | GET /api/storage/retention | | | storage.getSummary() | GET /api/storage/summary | Row counts, DB size, and source-map weight. | | storage.previewCleanup({ body }) | POST /api/storage/cleanup/preview | Dry-run count, mutates nothing. | | storage.previewRetention({ body }) | POST /api/storage/retention/preview | What saving these settings would delete on the next cleanup, without saving or deleting anything. | | storage.previewSourceMapGc() | POST /api/storage/source-maps/gc/preview | Dry-run orphan count, deletes nothing. | | storage.runRetention() | POST /api/storage/retention/run | Enforces the saved retention now, without waiting for the schedule. | | storage.updateRetention({ body }) | PUT /api/storage/retention | |

Team

| Method | Endpoint | Description | |---|---|---| | team.deleteUser({ path }) | DELETE /api/team/{user_id} | Remove a user from the instance (admin only) | | team.list() | GET /api/team | List all users (admin only) | | team.updateRole({ path, body }) | PATCH /api/team/{user_id}/role | Change a user's global role (admin only) |

Telemetry

| Method | Endpoint | Description | |---|---|---| | telemetry.preview() | GET /api/telemetry/preview | |

Tokens

| Method | Endpoint | Description | |---|---|---| | tokens.create({ body }) | POST /api/tokens | Create a new token (scoped to the acting user) | | tokens.delete({ path }) | DELETE /api/tokens/{id} | Revoke a token | | tokens.get({ path }) | GET /api/tokens/{id} | Reveal full token value | | tokens.list() | GET /api/tokens | List tokens (masked) |

Transactions

| Method | Endpoint | Description | |---|---|---| | transactions.get({ path }) | GET /api/projects/{project_id}/transactions/{transaction_id} | Returns a single transaction with its full Sentry payload (spans, contexts, measurements, tags) for the performance detail view. | | transactions.getSpans({ path }) | GET /api/projects/{project_id}/transactions/{transaction_id}/spans | Returns the indexed spans extracted from a transaction, ordered for the waterfall view (by start_timestamp ascending). | | transactions.getStatGroup({ path, query }) | GET /api/projects/{project_id}/transactions/stats/group | Returns the aggregate stats for a single transaction group — a direct lookup so the summary header works regardless of how many groups the project has. | | transactions.getStatGroupTimeseries({ path, query }) | GET /api/projects/{project_id}/transactions/stats/group/timeseries | Returns one endpoint's trend over the window, every bucket present: how often it ran and failed, and its p50 and p95. | | transactions.getStats({ path, query? }) | GET /api/projects/{project_id}/transactions/stats | Returns paginated aggregate performance stats (count, time spent, p50/p95/p99 latency, failure rate) grouped by transaction name + op, the most time spent first unless sort asks otherwise. | | transactions.list({ path, query? }) | GET /api/projects/{project_id}/transactions | Lists transactions for a project with offset-based pagination (newest first). |

Related Packages

| Package | Description | |---|---| | MCP server | Built into the Rustrak server at /mcp — gives Claude, Cursor and other MCP clients direct access to your instance |

What is Rustrak?

Rustrak is a self-hosted error tracking server written in Rust that accepts events from any Sentry SDK: point the SDK's DSN at it and nothing else changes. One container serves the API and the dashboard.

License

MIT