@keystrokehq/sentry
v0.0.11
Published
Official Keystroke integration for [Sentry](https://sentry.io).
Readme
@keystrokehq/sentry
Official Keystroke integration for Sentry.
- 176 operations across 21 resource domains (issues, releases, projects, monitors, replays, dashboards, alerts, Discover, SCIM, …).
- 27 direct-binding triggers: 16 integration-platform webhooks (issue / error / comment / alert lifecycle), 7 Seer Autofix events, 2 preprod-artifact events, 1 legacy project service-hook, and 1 parameterised Discover polling trigger.
- OAuth 2.0 (refreshable) public connection with provider-specific
exchangeCodeandextractInstallationInfohooks. Bearer-token tenants (user / organisation / internal-integration tokens) run through the same runtime path — the token is resolved into the sameAuthorization: Bearerheader. - Built-in HMAC-SHA256 verification, region-URL override for
us.sentry.io/de.sentry.io/ self-hosted deployments, RFC 5988Link-header pagination, and DSN-vs-token guard-rails.
Install
pnpm add @keystrokehq/sentryThe package is ESM-only, side-effect free, and ships typed subpath exports.
End-user authoring code imports from subpaths (./connection, ./issues,
./triggers, …) — never from the package root.
Credentials
Most customers connect via OAuth. The platform persists the access token
under SENTRY_ACCESS_TOKEN, the refresh token under SENTRY_REFRESH_TOKEN,
and (when resolved at install time) the installed organisation slug plus
region URL. Workflow authors receive the resolved SentryIntegrationCredentials
shape:
import type { SentryIntegrationCredentials } from '@keystrokehq/sentry/connection';| Field | Required | Notes |
| ------------------- | -------- | ------------------------------------------------------ |
| SENTRY_ACCESS_TOKEN | ✅ | OAuth access token or long-lived auth token. |
| SENTRY_REGION_URL | optional | Defaults to https://sentry.io. Set for us. / de. multi-region, or a self-hosted host. |
| SENTRY_ORG_SLUG | optional | Default organisation slug used when an operation omits organization_slug. |
Platform operators supply the Sentry app credentials (client_id,
client_secret, webhook_secret) through the environment keys
KEYSTROKE_OFFICIAL_SENTRY_CLIENT_ID, _CLIENT_SECRET, and _WEBHOOK_SECRET.
These are internal — they never reach authored workflow code.
OAuth scope matrix
| Capability | Scopes you need |
| -------------------------------------------- | ------------------------------------------------ |
| Read orgs, projects, teams, members, issues | org:read project:read event:read member:read team:read |
| Update issue status / assignee / archive | event:admin project:write |
| Manage alert rules (metric + issue) | alerts:read alerts:write |
| Create and update releases / deploys / files | release:admin |
| Manage project keys (DSN rotation) | project:admin |
| SCIM user / group provisioning | member:write team:admin |
The default OAuth flow requests the superset above. Trim the scope list in your platform config if you only need a narrower subset.
Quickstart — Sentry → Linear + Slack fan-out
import { getOrganizationIssueDetails, updateIssueAttributesInOrganization } from '@keystrokehq/sentry/issues';
import { on } from '@keystrokehq/sentry/triggers';
import { createIssue } from '@keystrokehq/linear/issues';
import { sendMessage } from '@keystrokehq/slack/platform/messages';
import { Workflow } from '@keystrokehq/core';
export const sentryFanOut = new Workflow({
id: 'sentry-fan-out',
name: 'Sentry → Linear + Slack',
triggers: [
on.issueCreated({
filter: (event) => event.data.issue.level !== 'info',
transform: (event) => ({
sentryIssueId: event.data.issue.id,
title: event.data.issue.title ?? 'Unknown Sentry issue',
url: event.data.issue.web_url ?? null,
}),
}),
],
run: async ({ input, run }) => {
const issue = await run(getOrganizationIssueDetails, {
input: { issue_id: input.sentryIssueId },
});
const linearIssue = await run(createIssue, {
input: {
teamId: 'TEAM_UUID',
title: issue.title ?? input.title,
description: `Sentry: ${input.url ?? 'no url'}`,
},
});
await run(sendMessage, {
input: {
channel: '#oncall',
text: `:rotating_light: ${input.title}\nLinear: ${linearIssue.issue?.url ?? 'pending'}\nSentry: ${input.url ?? ''}`,
},
});
await run(updateIssueAttributesInOrganization, {
input: {
id: [input.sentryIssueId],
assignedTo: 'team:oncall',
statusDetails: { linear_url: linearIssue.issue?.url },
},
});
},
});Public surface
End-user authoring (operations)
| Subpath | Domain |
| ----------------------------------------------- | ---------------------------------------------------------- |
| @keystrokehq/sentry/organizations | 15 operations — orgs, stats, integrations |
| @keystrokehq/sentry/members | 7 operations — member CRUD + invites |
| @keystrokehq/sentry/teams | 14 operations — team CRUD + external-team links |
| @keystrokehq/sentry/projects | 19 operations — project CRUD, filters, ownership, symbols |
| @keystrokehq/sentry/project-keys | 5 operations — DSN / client key management |
| @keystrokehq/sentry/issues | 13 operations — issue lifecycle + external-issue links |
| @keystrokehq/sentry/events-api | 7 operations — issue events, project events, Discover |
| @keystrokehq/sentry/releases | 21 operations — releases, files, repos, commits |
| @keystrokehq/sentry/deploys | 2 operations — record + list deploys per release |
| @keystrokehq/sentry/environments | 3 operations — project environment management |
| @keystrokehq/sentry/alerts | 11 operations — metric + issue alert rules |
| @keystrokehq/sentry/notifications | 5 operations — notification actions |
| @keystrokehq/sentry/monitors | 10 operations — cron monitors + check-ins |
| @keystrokehq/sentry/dashboards | 5 operations — organisation dashboards |
| @keystrokehq/sentry/discover | 5 operations — saved Discover queries |
| @keystrokehq/sentry/webhooks | 5 operations — legacy project service hook CRUD |
| @keystrokehq/sentry/replays | 9 operations — Session Replay |
| @keystrokehq/sentry/feedback | 2 operations — user feedback |
| @keystrokehq/sentry/debug-files | 3 operations — DIF / dSYM upload |
| @keystrokehq/sentry/scim | 11 operations — SCIM + SAML + external users |
| @keystrokehq/sentry/user-emails | 4 operations — account email management |
End-user authoring (triggers)
import { on, webhooks, polling } from '@keystrokehq/sentry/triggers';on.<event>({ filter, transform, name, description })— integration-platform webhooks. Available events:installationCreated,installationDeletedissueCreated,issueResolved,issueAssigned,issueUnresolved,issueIgnored,issueArchivederrorCreatedcommentCreated,commentUpdated,commentDeletedeventAlertTriggeredmetricAlertCritical,metricAlertWarning,metricAlertResolved- Seer:
seerRootCauseStarted,seerRootCauseCompleted,seerSolutionStarted,seerSolutionCompleted,seerCodingStarted,seerCodingCompleted,seerPrCreated - Preprod artifacts:
preprodSizeAnalysisCompleted,preprodBuildDistributionCompleted
webhooks.serviceHookEventAlert({ filter, transform })— legacy per-project service-hook binding. Signs with the per-hook secret, not the integration client secret.polling.discoverEvents(params, options?)— parameterised polling over/api/0/organizations/{slug}/events/. Supplyfield,query,project,environment,statsPeriod,dataset, and optionallyschedule.
Support surfaces
import { sentry, type SentryIntegrationCredentials } from '@keystrokehq/sentry/connection';
import { createSentryClient, type SentryCredentials } from '@keystrokehq/sentry/client';
import { sentryIssueSchema, sentryProjectSummarySchema } from '@keystrokehq/sentry/schemas';
import { sentryIssueCreatedEventSchema } from '@keystrokehq/sentry/events';
import { verifySentryWebhookRequest, verifySentryServiceHookRequest } from '@keystrokehq/sentry/verification';Internal (do not import from application code)
@keystrokehq/sentry/_official— bundle export used by@keystrokehq/official-integration-catalog.@keystrokehq/sentry/_runtime— reserved for hosted runtime plumbing.
Caveats
- DSN ≠ auth token. The client rejects any value that parses as a Sentry
DSN (a URL with a
user@hostcomponent). DSNs are ingest-only. - Region routing. If a customer installs from
us.sentry.ioorde.sentry.io, the OAuth hook captures the region URL at install time. Self-hosted deployments must explicitly setSENTRY_REGION_URLon the credentials or the client will default tohttps://sentry.io. - Webhook timing. Integration-platform webhooks must respond within one
second per Sentry's docs. Keep the transform path cheap and push work onto
runcallbacks. - Issue state migration. Sentry is migrating from
ignored→archived; both triggers are exposed. Collapse them intransformif you want a single "muted" state downstream. - Service hooks are deprecated. Prefer the integration-webhook path for new installs. The service-hook operations + trigger remain for backward compatibility.
Not yet supported
seer.coding_completedand preprod webhooks currently pass through raw payloads. When the payload schemas stabilise upstream, we will upgrade them to a strict shape.
License: MIT.
