@rustrak/client
v0.22.2
Published
Type-safe TypeScript client for the Rustrak self-hosted error tracking API
Maintainers
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/clientRequirements: 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' },
});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_errornever carries the server's message.AppError::InternalandAppError::Databaseinterpolate arbitrary internal text (a pool error, an OS errno, a filesystem path); the client discards it at construction and substitutesSERVER_ERROR_MESSAGE.networkcarries nocause. The underlying error embeds the resolved host and port.invalid_responsecarries 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
unwrapOris 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 theResultAPI 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 onresult.successand readerror.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.
