getsentry / getsentry/sentry

Merge integration platform into API applications

Open
#120,931 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Feature
Dominant language
Python
Stars
44.8k
Forks
4.9k
Avg merge
21h 23m
Merged PRs (30d)
607

Description

API applications and Integration Platform apps are two configuration surfaces for the same underlying idea. Make API Application the single app primitive: every app uses the same OAuth foundation, while webhook subscriptions, organization installations, installation/service tokens, and UI hooks become optional application capabilities.

A second goal of the merge: land OAuth 2.1, CIMD, and ID-JAG once, correctly, on that shared application layer — not as separate stacks for "OAuth apps" vs "integrations" vs MCP/tooling clients. Related work already exists in getsentry/sentry#99002, getsentry/sentry#106326, getsentry/sentry#79647, and getsentry/sentry#107873.

Both private/org-specific ("internal integration") and shared/public third-party application paths must stay low friction. Unify the foundation without making either happy path harder.

ELI5 model

Today an integration already wraps an API application:

SentryApp --1:1--> ApiApplication
    |
    +--> SentryAppInstallation --?> ApiGrant / ApiToken
    +--> webhook URL + events
    +--> UI schema/components

The target is:

ApiApplication
    +--> OAuth client configuration
    +--> optional webhook configuration
    +--> optional UI components
    +--> OrganizationInstallation --?> installation token

There should not be separate "OAuth app" and "integration" creation paths, auth flows, or runtime behavior. An integration is simply an API application with more capabilities configured.

Expected user stories

Keep these as the simple behavioral contract. Implementation details can change underneath them.

Both org-specific (private/internal) and shared (public/third-party) applications should stay low friction. Same foundation, different defaults.

Create and manage an application
  • User can create an application for an organization.
  • User can create a private application for only their organization in one short flow.
  • User can create a shared/public application that other organizations can install.
  • User can view and manage applications owned by their organization.
  • User can configure OAuth redirect URLs and scopes for an application.
  • User can configure webhook URLs, headers, and event subscriptions for an application.
  • User can configure UI hooks for an application.
  • User can choose whether an application is private to its owner organization or usable by other organizations.
Org-specific / "internal integration" path
  • User who just wants an org bot/webhook/token can create a private application, install it into that org, and get a token without learning a second product.
  • User can do that private create+install path in effectively one step by default.
  • User can later add webhooks, UI hooks, or more tokens to that same private application without recreating it.
  • User can uninstall or revoke the private application later even if create defaulted to installed.
Shared / third-party application path
  • Third-party developer can create a shared application once and offer it to many Sentry organizations.
  • Organization admin can install a third-party application through a normal install/OAuth flow.
  • Organization admin can review requested scopes and capabilities before installing a shared application.
  • Third-party developer can send users through install/OAuth more than once when needed.
  • Organization admin can uninstall a shared application and revoke its access.
Install an application
  • User can install an application into an organization they administer.
  • User can install a private application into its owner organization.
  • User can reinstall or repeat authorization for an existing installation.
  • User can install the same shared application more than once when separate external workspaces/accounts require separate installations.
  • User can uninstall an application and revoke the access granted by that installation.
Use OAuth and tokens
  • User can authorize any application through the same OAuth flow, whether or not it uses webhooks or UI hooks.
  • User can create, name, rotate, inspect, and revoke installation/service tokens when an installation needs machine access.
  • User can get a first installation token automatically on the private/org-bot path when that is the point of the app.
  • User can see when an installation/service token was created and last used.
  • User can see application and installation actions attributed to a first-class application actor, not a fake user.
Use modern auth once
  • User can use OAuth 2.1 behavior consistently for every application.
  • User can use CIMD with the same foundational application model.
  • User can use ID-JAG with the same foundational application model.
  • User does not encounter a separate auth protocol because an application also uses integrations features.
Preserve existing behavior
  • Existing internal integrations remain as easy as today for the "I just need a bot/token/webhook in my org" case.
  • Existing public/third-party integrations remain easy to create once and install into customer orgs.
  • Existing Integration Platform applications continue to authorize, install, send webhooks, and render UI hooks during migration.
  • Existing API applications can gain integrations capabilities without becoming a different application or changing auth flows.

What exists today

  • ApiApplication owns the OAuth identity: client ID/secret, redirect URIs, origins, URLs, scopes, client type, and protocol version (src/sentry/models/apiapplication.py).
  • SentryApp already has a one-to-one ApiApplication, but separately owns organization ownership, slug/publication state, webhook URL/headers/events, UI schema, proxy user, and integration metadata (src/sentry/sentry_apps/models/sentry_app.py).
  • SentryAppInstallation is the organization-scoped installation and links to an ApiGrant and, for published apps, one ApiToken (src/sentry/sentry_apps/models/sentry_app_installation.py).
  • Internal integrations can have multiple tokens through SentryAppInstallationToken; the model comment explicitly distinguishes this from the published-app token (src/sentry/sentry_apps/models/sentry_app_installation_token.py). So an "installation token" is already a thing, but represented in two ways.
  • Webhooks are materialized as ServiceHook rows associated with an application/installation, while dispatch, signatures, event validation, and UI-component callbacks live under src/sentry/sentry_apps/.
  • Integration visibility is encoded as UNPUBLISHED, PUBLISHED, INTERNAL, and PUBLISH_REQUEST_INPROGRESS (src/sentry/constants.py). Related publication behavior remains covered by getsentry/sentry#19261.

Gap analysis

Capability API application today Integration Platform today Unified result
OAuth client + redirect URIs Yes Via linked ApiApplication Keep on ApiApplication
OAuth scopes Yes Duplicated/exposed through app configuration One scope source on ApiApplication
Organization installation No first-class model SentryAppInstallation Rename/generalize as application installation
Installation/service token Ordinary OAuth tokens only Public install token or multiple internal tokens One installation-token relationship and lifecycle
Outgoing webhooks No configuration surface URL, headers, events, signatures, delivery log Add as optional application capability
UI hooks No JSON schema + materialized components Add as optional application capability
Ownership User owner (ApiApplication.owner) Org owner (SentryApp.owner_id) + creator + proxy user Org-owned apps are fine; keep creator as admin metadata
Visibility/publication Not modeled Internal/unpublished/published/reviewing Apply the public/private direction from getsentry/sentry#19261 to applications
Public OAuth clients Supported (client_secret=None) Sentry Apps assert a client secret for signing Separate webhook signing credentials from OAuth client authentication
Admin/API/UI surface OAuth application endpoints/pages Separate Sentry App/internal integration endpoints/pages One API Application surface; compatibility aliases during migration

Foundational auth goal (do it once)

Merging platforms is partly about product shape, but also about not paying the OAuth tax twice.

Today:

  • OAuth modernization work targets ApiApplication / /oauth/* (getsentry/sentry#99002, getsentry/sentry#79647, getsentry/sentry#107873).
  • Integration Platform mostly reuses that client, then adds its own install/token/proxy-user edges.
  • New client patterns (MCP, editor tooling, enterprise SSO→API) need CIMD and ID-JAG (getsentry/sentry#99002, getsentry/sentry#106326).

If integrations stay a parallel product surface, every modern auth feature risks being implemented, half-implemented, or forked twice.

Target:

one ApiApplication identity
        |
        +--> OAuth 2.1 authorize/token behavior
        +--> CIMD client registration/metadata
        +--> ID-JAG / enterprise assertion grants
        +--> optional install, webhooks, UI hooks, installation tokens

ELI5: Cursor-via-Okta, a private internal bot, and a public partner integration should all be applications on the same auth chassis. Capabilities differ; the token server should not.

Implications for this merge:

  • Do not invent a second install auth protocol for integrations.
  • Do not special-case Sentry Apps outside ApiApplication versioning/feature gates (ApiApplication.version, PKCE, strict redirect URIs, refresh rotation).
  • Prefer additive grants/capabilities on the shared application rather than new app types for MCP/CIMD/ID-JAG.
  • Implementation order can still be staged (unify model → harden OAuth 2.1 core → CIMD → ID-JAG), but the design target is one foundation.

Small migration plan

  1. Expand ApiApplication
    • Move or associate the generic app metadata currently on SentryApp: slug, organization owner, visibility/publication state, webhook configuration, UI schema, and disabled state.
    • Prefer child tables for independently changing capabilities instead of making the OAuth row enormous, for example:
class ApiApplicationWebhook(Model):
    application = OneToOneField(ApiApplication)
    url = URLField()
    events = ArrayField(TextField(), default=list)
    headers = ArrayField(EncryptedTextField(), default=list)
    signing_secret = EncryptedTextField()

class ApiApplicationInstallation(Model):
    application = ForeignKey(ApiApplication)
    organization_id = HybridCloudForeignKey("sentry.Organization")
    status = BoundedPositiveIntegerField(...)
  1. Use one installation/token path
    • Generalize SentryAppInstallation to ApiApplicationInstallation (rename later; an initial alias is fine).
    • Replace the special public installation.api_token vs. internal SentryAppInstallationToken split with one join table that supports one or more tokens.
    • Keep the canonical OAuth authorization-code/grant exchange. An installation token is a token attached to an organization installation, not a separate app type or auth protocol.
  2. Keep modern OAuth on the shared application foundation
    • Treat getsentry/sentry#99002 / getsentry/sentry#79647 / getsentry/sentry#107873 as work on the one ApiApplication auth chassis, not a side quest for plain OAuth apps only.
    • Design CIMD (getsentry/sentry#99002) and ID-JAG (getsentry/sentry#106326) against application identity + org/install grants, so installable integrations inherit them for free.
    • Avoid integration-only token exchange or client-registration paths.
  3. Move capability code behind application services
    • Make webhook validation/delivery, service-hook creation, installation lookup, and UI-component preparation accept ApiApplication / application installation IDs.
    • Keep webhook payloads, signatures, retries, event names, and UI schema behavior unchanged initially. This is consolidation, not a redesign of those contracts.
  4. Merge product and API surfaces
    • Make API Application create/edit pages expose optional sections for webhooks and UI hooks.
    • Show installations and installation tokens on the same application.
    • Route new writes through the unified endpoints. Keep existing Sentry App/internal integration endpoints as compatibility adapters until callers and docs migrate.
  5. Backfill, switch reads, then contract
    • Backfill every SentryApp.application_id and its capability/install data into the new representation.
    • Dual-read/compare, switch reads, then switch writes.
    • Remove the separate Sentry App model/status branches, proxy plumbing that is no longer required, and old UI only after parity tests and usage checks.

Suggested slices

  • Add application webhook configuration + tests without changing dispatch.
  • Add generalized application installations and token join model.
  • Backfill existing Sentry Apps/installations/tokens idempotently.
  • Adapt webhook delivery and signatures to application IDs.
  • Adapt UI-component parsing/rendering/callbacks to applications.
  • Add the unified API Application editor and installation/token management.
  • Add compatibility adapters for existing Integration Platform APIs.
  • Land OAuth 2.1 core behavior on shared ApiApplication auth (getsentry/sentry#99002, getsentry/sentry#79647, getsentry/sentry#107873).
  • Design CIMD client metadata against application identity, not a parallel client type (getsentry/sentry#99002).
  • Design ID-JAG grants against application + org/install access, reusable by tools and integrations (getsentry/sentry#106326).
  • Switch reads/writes with parity telemetry.
  • Migrate docs and terminology; remove legacy models/routes after deprecation.

Ownership vs installation (design note)

Two different axes that get conflated today:

own / administer app          install / enable app in an org
-------------------------     --------------------------------
who can edit client config    who granted org access
who sees secrets              where webhooks fire
who publishes/shares          where installation tokens work

ELI5: owning the app definition is not the same as enabling it somewhere. But for private org bots, those two steps should still feel like one easy action.

Ease-of-use bar

Both paths must stay low friction:

Path Who Happy path Must not require
Private / org-specific ("internal integration") Someone who just wants a bot, webhook, or token in their own org Create private app → installed in this org → token ready Learning a second product, multi-step publish/review, or "install from marketplace" ceremony
Shared / public (third-party application) Partner or external developer + customer org admin Create shared app once → customer installs via normal OAuth/install link A different auth stack, or forced "internal integration" hacks to ship
Current behavior worth preserving
  • Internal integrations: create ⇒ auto-install into owner org + default token. Keep this as the default private-app convenience, even if the model underneath is still application + installation.
  • Public/unpublished integrations: create ⇒ explicit install/OAuth into each customer org. Keep this as the shared-app path.
  • Plain API applications: no installation concept unless org-level capabilities are needed.
Target sketch
ApiApplication
  owner_organization_id      # org-owned for installable apps
  created_by_user_id
  visibility: private|public
  capabilities: oauth, webhooks?, ui?, installation_tokens?

ApiApplicationInstallation
  application_id
  organization_id
  status
  tokens[]                   # optional machine credentials for this install

Recommended UX defaults:

  • Private app create: default checked Install into this organization + optional Create installation token. One screen, one submit, same ease as today's internal integration.
  • Shared app create: create the app definition only; give the developer an install/OAuth link customers can use. No approval queue required just to be installable (getsentry/sentry#110317).
  • Model always separates own vs install, so uninstall/revoke/audit/reinstall work cleanly for both paths.
  • Do not make private users suffer public ceremony, and do not make public apps reinvent internal-only shortcuts.

Subjective open questions

  1. Name: use "API Application" everywhere, or shorten the user-facing noun to "Application" while keeping ApiApplication in code?
  2. Visibility: what replaces internal/unpublished/published/reviewing? A simple proposal is private (installable only in owner org) vs. public (installable elsewhere), with review/featured as listing workflow rather than an app type. Private must stay one-step easy; public must stay easy to create once and install many times. Follow getsentry/sentry#19261 / getsentry/sentry#110317.
  3. Installation tokens: should every installation get a service token automatically, only on request, or only when the app asks for machine access? Recommendation: opt-in and revocable; do not mint credentials for webhook/UI-only apps.
  4. Token cardinality: preserve multiple tokens per private/internal installation, or normalize to one rotating token? Recommendation: support multiple named tokens because existing internal integrations already do.
  5. Public OAuth clients + webhooks: public clients have no client secret, but webhook signing currently uses it. Recommendation: use a dedicated rotatable webhook signing secret; never require or reuse an OAuth client secret.
  6. Ownership: today is mixed. Plain OAuth apps are user-owned (ApiApplication.owner), while integrations are org-owned (SentryApp.owner_id) with a creator user and a proxy user for runtime. Org ownership of an application is fine and probably the right end state for anything that can be installed, emit webhooks, or hold installation tokens. Still needs thought for pure user OAuth clients (CLIs/SPAs) that are not org products — keep those user-owned, or always require a home org?
  7. Install vs own-org enablement: model own and install as separate records, but keep defaults easy. Today internal integrations auto-install into the creating org and mint a token (SentryAppCreator when is_internal) — preserve that as the private-app default so "I just need an internal integration" stays one step. Shared/public apps should keep an explicit customer install/OAuth flow, and that flow must stay low friction too (repeatable install/auth, no fake second product). Recommendation: always store application + installation; private create defaults to install-into-owner-org + optional token; public create yields an install link for other orgs.
  8. Install meaning for OAuth grants: does an ordinary user OAuth grant create an organization installation automatically, or only when organization-level capabilities are requested? Recommendation: create an installation only for org-level scopes, webhooks, UI hooks, or installation tokens; plain user OAuth remains plain user OAuth. Re-running install/OAuth after an existing install should be allowed (see getsentry/sentry#72379), not blocked as "already installed."
  9. Scope changes: when an app adds scopes/events/UI capabilities, do existing installations require re-consent? Recommendation: yes for expanded API scopes; webhook event changes must continue validating against granted scopes.
  10. OAuth modernization sequencing: unify models first and then land OAuth 2.1/CIMD/ID-JAG on the shared chassis, or harden OAuth 2.1 core on ApiApplication in parallel while integrations are still wrapping it? Recommendation: parallelize OAuth 2.1 core on ApiApplication now; require CIMD/ID-JAG designs to assume the merged application/install model so they are not rewritten.
  11. Compatibility lifetime: how long do /sentry-apps/ endpoints, payload names (sentry_app, installation UUID), and audit event names remain aliases? Decide after measuring external use; avoid a flag-day rename.

Related open issues

No open issues titled specifically for "service tokens" or "integration actors" were found. Those still look like real platform gaps that should be decided during this merge:

  • Service / installation tokens already exist in two forms (published install api_token vs multi-token internal SentryAppInstallationToken) and sit next to org auth tokens. Token lifecycle/permissions/expiry pain is real.
  • Integration actors still lean on proxy users / bot-like identities for installs. That model is brittle (audit, permissions, hybrid-cloud sharding) and should be replaced by a first-class application/installation actor if we unify apps.
Closely related (should inform this work)
  • getsentry/sentry#110317 — Rethink integrations: open OAuth, featured partners, no approval queue (public/private + listing model; pairs with getsentry/sentry#19261*)*
  • getsentry/sentry#19261 — Ability to publish public integrations in on-premise installations with multiple organizations
  • getsentry/sentry#50219 — Integration Platform should support multiple installations per app
  • getsentry/sentry#65224 — Custom Integration tokens need expiry
  • getsentry/sentry#84566 — Internal Integration Auth Token Permissions
  • getsentry/sentry#90037 — Last Used column for internal integration auth tokens
  • getsentry/sentry#60072 — List internal integration endpoint only works with personal token
  • getsentry/sentry#51993 — Incomplete internal integration audit logs
  • getsentry/sentry#72379 — Integrations Platform OAuth external install can only initiate once
  • getsentry/sentry#99002 — OAuth 2.1 plan and tracking, including CIMD (foundational auth chassis)
  • getsentry/sentry#106326 — ID-JAG support (enterprise SSO→API; must ride the same chassis)
  • getsentry/sentry#79647 — Implement PKCE support for API applications
  • getsentry/sentry#107873 — Consider removing or extending refresh token expiration for OAuth apps
  • getsentry/sentry#86732 — ServiceHooks missing events for updated Sentry Apps
  • getsentry/sentry#116069 — Alerts UI "Unknown integration" for sentry_app actions without schema
  • getsentry/sentry#94731 — Internal integrations: scope webhooks per project
  • getsentry/sentry#59940 — 403 on history endpoint for custom integration
Nearby token surface (not Integration Platform, but same identity mess)
  • getsentry/sentry#119786 — Configurable expiration for personal and organization auth tokens
  • getsentry/sentry#89421 — Granular control over organization token creation
  • getsentry/sentry#67329 — AuditLog for Org Auth Token usage
  • getsentry/sentry#120692 — Audit-only token scope without org:write

Not in scope

  • Introducing a second installation auth protocol or a parallel OAuth stack for integrations/MCP/tooling.
  • Fully shipping every OAuth 2.1 / CIMD / ID-JAG checklist item inside the first model-merge PR — but those features must target the unified application foundation, not a side path.
  • Changing webhook payload contracts, UI schema semantics, retry behavior, or permissions as part of the initial merge.
  • Solving every existing webhook/UI-hook bug before consolidation; preserve behavior first, then fix on the unified platform.

Requested by David Cramer via Junior.

--

View Junior Session in Sentry

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading src/sentry/models/apiapplication.py, src/sentry/sentry_apps/models/sentry_app.py, src/sentry/sentry_apps/models/sentry_app_installation.py, and src/sentry/sentry_apps/models/sentry_app_installation_token.py, then review the linked OAuth and publication issues. Done means a staged design and migration that unifies application identity, installation tokens, OAuth, webhooks, and UI capabilities without breaking existing application or integration flows.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, authentication, authorization, backend, backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.