Merge integration platform into API applications
Nobody has claimed this yet.
- 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
ApiApplicationowns the OAuth identity: client ID/secret, redirect URIs, origins, URLs, scopes, client type, and protocol version (src/sentry/models/apiapplication.py).SentryAppalready has a one-to-oneApiApplication, 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).SentryAppInstallationis the organization-scoped installation and links to anApiGrantand, for published apps, oneApiToken(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
ServiceHookrows associated with an application/installation, while dispatch, signatures, event validation, and UI-component callbacks live undersrc/sentry/sentry_apps/. - Integration visibility is encoded as
UNPUBLISHED,PUBLISHED,INTERNAL, andPUBLISH_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
ApiApplicationversioning/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
- 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:
- Move or associate the generic app metadata currently on
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(...)
- Use one installation/token path
- Generalize
SentryAppInstallationtoApiApplicationInstallation(rename later; an initial alias is fine). - Replace the special public
installation.api_tokenvs. internalSentryAppInstallationTokensplit 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.
- Generalize
- Keep modern OAuth on the shared application foundation
- Treat getsentry/sentry#99002 / getsentry/sentry#79647 / getsentry/sentry#107873 as work on the one
ApiApplicationauth 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.
- Treat getsentry/sentry#99002 / getsentry/sentry#79647 / getsentry/sentry#107873 as work on the one
- 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.
- Make webhook validation/delivery, service-hook creation, installation lookup, and UI-component preparation accept
- 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.
- Backfill, switch reads, then contract
- Backfill every
SentryApp.application_idand 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.
- Backfill every
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
ApiApplicationauth (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+ optionalCreate 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
- Name: use "API Application" everywhere, or shorten the user-facing noun to "Application" while keeping
ApiApplicationin code? - 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. - 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.
- 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.
- 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.
- 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? - 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 (
SentryAppCreatorwhenis_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 storeapplication+installation; private create defaults to install-into-owner-org + optional token; public create yields an install link for other orgs. - 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."
- 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.
- 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
ApiApplicationin parallel while integrations are still wrapping it? Recommendation: parallelize OAuth 2.1 core onApiApplicationnow; require CIMD/ID-JAG designs to assume the merged application/install model so they are not rewritten. - 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_tokenvs multi-token internalSentryAppInstallationToken) 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.
--
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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