# Sentry

> Use when user references Sentry, sentry.io URLs, *.sentry.io/issues/ links, error/exception/release alerts, pastes a stack trace for triage, or checks release health on cloud or self-hosted. Multi-org via SENTRY_PROFILE.

- Skill: `tuannv14/sentry` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tuannv14/sentry`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tuannv14/sentry/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: tuannv14 (https://skillmd.com/u/tuannv14)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tuannv14/sentry

---


# /sentry — error monitoring (multi-org)

REST against `https://sentry.io/api/0/` (or self-hosted Sentry instance).
Bearer auth with API token. Profiles isolate org/project pairs.

Arguments: `$ARGUMENTS`. Profile resolution: `--profile <name>` → `SENTRY_PROFILE` → `~/.sentry/active_profile` → `[default]`.

## Overview

REST against `https://sentry.io/api/0/` (or self-hosted). Bearer auth with API token. Profiles isolate org/project pairs. Default summary view (no PII) — opt-in `--full` for raw stacktraces.

## When to Use

- Investigating issues during incidents (latest unresolved, last 24h)
- Resolving or assigning issues to teammates
- Release health (new issues per release, commit count)
- Self-hosted Sentry instances (`api_url` per profile)
- Triaging error spikes across multiple projects in one org

## When NOT to Use

- Sending events to Sentry → use the SDK in your app, not this skill
- Bulk operations on hundreds of issues → use Sentry's UI bulk actions
- Sourcemap upload → use `@sentry/cli` or Webpack/Vite plugin
- User management at scale → Sentry's admin UI

## Dependencies

`curl`, `jq`.

## Profile config

`~/.sentry/credentials` (mode 600):

```ini
[default]
api_url = https://sentry.io/api/0
auth_token = sntrys_xxxxxxxxxxxxxxxxxxxxxxxx
org = my-org-slug
project = backend

[work]
api_url = https://sentry.example.com/api/0    # self-hosted
auth_token = sntrys_xxxxxxxxxxxxxxxxxxxxxxxx
org = company-org
project = api

[client_a]
api_url = https://sentry.io/api/0
auth_token = sntrys_xxxxxxxxxxxxxxxxxxxxxxxx
org = client-a-org
project = mobile
```

**Token scopes** (least privilege):

| Operation | Required scope |
|---|---|
| Read issues, events | `event:read` + `project:read` |
| Resolve / assign issues | `event:write` + `project:read` |
| Read releases | `project:releases` |
| Manage members | `org:read` (avoid unless needed) |

Get token: User Settings → Auth Tokens → Create New Token.
Use **org-scoped** tokens (limits to one org) when possible.

## Helpers

> Shared profile/INI/`ctt_*` pattern reference: [profiles-and-credentials](../profiles-and-credentials/SKILL.md).

```bash
source "$HOME/.claude-team-toolkit/lib/credentials.sh"
ctt_load_creds sentry "$PROFILE"

sentry_api() {
  local method="$1" path="$2"; shift 2
  curl -s -X "$method" \
    -H "Authorization: Bearer $CTT_AUTH_TOKEN" \
    -H "Content-Type: application/json" \
    "$@" \
    "$CTT_API_URL$path"
}
```

## Dispatch

### `issues [--query <q>] [--limit N]` — list issues

```bash
Q="${QUERY:-is:unresolved}"
sentry_api GET "/projects/$CTT_ORG/$CTT_PROJECT/issues/?query=$(printf %s "$Q" | jq -sRr @uri)&limit=${LIMIT:-25}" \
  | jq -r '.[] | "\(.shortId)\t\(.level)\t\(.count)\t\(.title)\n  \(.permalink)"'
```

Common queries:
- `is:unresolved` — open issues
- `is:unresolved age:-24h` — last 24h
- `level:error` — errors only
- `release:1.2.3` — specific release
- `assigned:me` — yours

### `issue <issueId>` — full issue detail

```bash
sentry_api GET "/issues/$1/" | jq '{
  id: .shortId, title, level, status, count, userCount,
  firstSeen, lastSeen, assignedTo,
  release: .firstRelease.version,
  permalink
}'
```

### `events <issueId> [--limit N]` — recent events for an issue

```bash
sentry_api GET "/issues/$1/events/?limit=${LIMIT:-10}" \
  | jq -r '.[] | "\(.eventID)\t\(.dateCreated)\t\(.user.email // "—")"'
```

### `event <eventId>` — full event detail (stacktrace + context)

```bash
sentry_api GET "/projects/$CTT_ORG/$CTT_PROJECT/events/$1/" | jq '{
  id: .eventID, message, level, dateCreated,
  user, environment, release, dist,
  exception: (.entries[] | select(.type=="exception") | .data.values[0] | {type, value, frames: (.stacktrace.frames | map({function, filename, lineno}))})
}'
```

### `resolve <issueId>` — mark resolved

```bash
source "$HOME/.claude-team-toolkit/lib/confirm.sh"
ctt_confirm "Resolve issue $1 on $CTT_PROFILE?" || return 1
sentry_api PUT "/issues/$1/" -d '{"status":"resolved"}'
ctt_audit_log sentry "resolved $1"
```

### `assign <issueId> <username>`

```bash
BODY=$(jq -n --arg u "$2" '{assignedTo: $u}')
sentry_api PUT "/issues/$1/" -d "$BODY"
ctt_audit_log sentry "assigned $1 → $2"
```

### `releases [--limit N]` — recent releases

```bash
sentry_api GET "/organizations/$CTT_ORG/releases/?per_page=${LIMIT:-20}" \
  | jq -r '.[] | "\(.version)\t\(.dateCreated)\t\(.newGroups // 0) new issues\t\(.commitCount) commits"'
```

### `release <version>` — release detail + health

```bash
sentry_api GET "/organizations/$CTT_ORG/releases/$1/" | jq '{
  version, dateCreated,
  newGroups, commitCount,
  authors: [.authors[].name],
  projects: [.projects[].slug]
}'
```

### `projects` — list projects in org

```bash
sentry_api GET "/organizations/$CTT_ORG/projects/" \
  | jq -r '.[] | "\(.slug)\t\(.platform)\t\(.id)"'
```

## Safety

- Issue/event content (stacktraces, user data, error messages) often contains
  **PII** — never paste raw output into public chats. The skill should
  default to summary view; `--full` flag opt-in for raw.
- `assignedTo` field can be a user OR a team — confirm with user which.
- Resolve is reversible (status:unresolved) but `delete` is not — there is
  intentionally NO `delete` command in this skill.
- Self-hosted Sentry: `api_url` includes path `/api/0`. Don't forget.
- 429 Too Many Requests: Sentry API rate limit (40 req/s default for free).

## Common Mistakes

- Self-hosted: forgetting `/api/0` in `api_url` → 404 on every call
- Pasting raw event output publicly → leaks PII (user IDs, request bodies)
- Using user-scoped tokens → blast radius is all your orgs. Use org-scoped.
- Resolving without root cause → issue re-opens on next event, frustration loop
- 429 ignored → API gets temp-banned. Respect `Retry-After`.
- Querying with `is:resolved` then surprised it includes old issues — add `age:-Nd` for recency

## Token-saving tip

Use **organization-scoped** tokens. They're limited to one org which limits
blast radius if leaked.

