|
| 1 | +--- |
| 2 | +title: Set up Sentry integration for self-hosted Plane |
| 3 | +sidebarTitle: Sentry |
| 4 | +description: Configure Sentry error tracking and issue creation for your self-hosted Plane instance by creating a custom integration and connecting it to your workspace. |
| 5 | +--- |
| 6 | + |
| 7 | +This guide shows you how to set up Sentry integration for your self-hosted Plane instance. Unlike Plane Cloud where Sentry comes pre-configured, self-hosted instances require you to create a custom integration in Sentry and configure your Plane deployment with the necessary credentials. |
| 8 | + |
| 9 | +<Note> |
| 10 | +**What you'll accomplish:** |
| 11 | +1. Create a Sentry custom integration with proper permissions and webhooks |
| 12 | +2. Configure your Plane instance with Sentry credentials |
| 13 | +3. Enable error tracking and automatic issue creation from Sentry alerts |
| 14 | +</Note> |
| 15 | + |
| 16 | +## Before you begin |
| 17 | + |
| 18 | +You'll need: |
| 19 | +- Administrator access to your Sentry organization |
| 20 | +- Access to your Plane instance configuration files |
| 21 | +- Your Plane instance domain (e.g., `plane.yourcompany.com`) |
| 22 | + |
| 23 | +## Create Sentry custom integration |
| 24 | + |
| 25 | +A custom integration (also called a public integration) connects your Sentry organization to Plane, enabling bidirectional communication for issue tracking and alert handling. |
| 26 | + |
| 27 | +1. Log in to your Sentry organization. |
| 28 | +2. Go to **Settings** → **Developer Settings** → **Custom Integrations**. |
| 29 | +3. Click **Create New Integration**. |
| 30 | +4. Select **Public Integration**. |
| 31 | +5. Fill in these fields on the integration creation screen: |
| 32 | + |
| 33 | + | Field | Value | |
| 34 | + |-------|-------| |
| 35 | + | **Name** | `Plane` (or any name you prefer) | |
| 36 | + | **Author** | Your organization name | |
| 37 | + | **Webhook URL** | `https://[YOUR_DOMAIN]/silo/api/sentry/sentry-webhook/` | |
| 38 | + | **Redirect URL** | `https://[YOUR_DOMAIN]/silo/api/oauth/sentry/auth/callback` | |
| 39 | + | **Verify Installation** | Disabled (recommended) | |
| 40 | + | **Alert Rule Action** | Enabled | |
| 41 | + |
| 42 | + <Note> |
| 43 | + Replace `[YOUR_DOMAIN]` with your actual Plane instance domain. For example, if your Plane instance is at `plane.company.com`, your Webhook URL would be `https://plane.company.com/silo/api/sentry/sentry-webhook/` |
| 44 | + </Note> |
| 45 | + |
| 46 | +  |
| 47 | + |
| 48 | +**Field explanations:** |
| 49 | + |
| 50 | +**Webhook URL** |
| 51 | +Sentry sends event notifications to this endpoint. Plane processes these webhooks to sync Sentry issues with Plane work items. |
| 52 | + |
| 53 | +**Redirect URL** |
| 54 | +After OAuth authorization, Sentry redirects users back to this URL to complete the connection. |
| 55 | + |
| 56 | +**Alert Rule Action** |
| 57 | +Enables automatic Plane work item creation when Sentry alert rules trigger. |
| 58 | + |
| 59 | +### Configure integration schema |
| 60 | + |
| 61 | +The schema defines how Sentry and Plane interact—what fields appear when creating issues and how alert rules behave. |
| 62 | + |
| 63 | +Paste this schema into the **Schema** field: |
| 64 | +```json |
| 65 | +{ |
| 66 | + "elements": [ |
| 67 | + { |
| 68 | + "link": { |
| 69 | + "uri": "/api/sentry/issues/link", |
| 70 | + "required_fields": [ |
| 71 | + { |
| 72 | + "uri": "/api/sentry/issues", |
| 73 | + "name": "identifier", |
| 74 | + "type": "select", |
| 75 | + "label": "Issue", |
| 76 | + "skip_load_on_open": true |
| 77 | + } |
| 78 | + ] |
| 79 | + }, |
| 80 | + "type": "issue-link", |
| 81 | + "create": { |
| 82 | + "uri": "/api/sentry/issues/create", |
| 83 | + "optional_fields": [ |
| 84 | + { |
| 85 | + "uri": "/api/sentry/users", |
| 86 | + "name": "assignee_ids", |
| 87 | + "type": "select", |
| 88 | + "async": false, |
| 89 | + "label": "Assignees", |
| 90 | + "multiple": true, |
| 91 | + "depends_on": ["project_id"] |
| 92 | + }, |
| 93 | + { |
| 94 | + "uri": "/api/sentry/priorities", |
| 95 | + "name": "priorities", |
| 96 | + "type": "select", |
| 97 | + "async": false, |
| 98 | + "label": "Priorities", |
| 99 | + "depends_on": ["project_id"] |
| 100 | + }, |
| 101 | + { |
| 102 | + "uri": "/api/sentry/labels", |
| 103 | + "name": "labels", |
| 104 | + "type": "select", |
| 105 | + "async": false, |
| 106 | + "label": "Labels", |
| 107 | + "multiple": true, |
| 108 | + "depends_on": ["project_id"] |
| 109 | + }, |
| 110 | + { |
| 111 | + "uri": "/api/sentry/states", |
| 112 | + "name": "state", |
| 113 | + "type": "select", |
| 114 | + "async": false, |
| 115 | + "label": "State", |
| 116 | + "depends_on": ["project_id"] |
| 117 | + }, |
| 118 | + { |
| 119 | + "uri": "/api/sentry/modules", |
| 120 | + "name": "module", |
| 121 | + "type": "select", |
| 122 | + "async": false, |
| 123 | + "label": "Module", |
| 124 | + "depends_on": ["project_id"] |
| 125 | + }, |
| 126 | + { |
| 127 | + "uri": "/api/sentry/cycles", |
| 128 | + "name": "cycle", |
| 129 | + "type": "select", |
| 130 | + "async": false, |
| 131 | + "label": "Cycle", |
| 132 | + "depends_on": ["project_id"] |
| 133 | + } |
| 134 | + ], |
| 135 | + "required_fields": [ |
| 136 | + { |
| 137 | + "name": "title", |
| 138 | + "type": "text", |
| 139 | + "label": "Title", |
| 140 | + "default": "issue.title" |
| 141 | + }, |
| 142 | + { |
| 143 | + "name": "description", |
| 144 | + "type": "textarea", |
| 145 | + "label": "Description", |
| 146 | + "default": "issue.description" |
| 147 | + }, |
| 148 | + { |
| 149 | + "uri": "/api/sentry/projects", |
| 150 | + "name": "project_id", |
| 151 | + "type": "select", |
| 152 | + "async": false, |
| 153 | + "label": "Project" |
| 154 | + } |
| 155 | + ] |
| 156 | + } |
| 157 | + }, |
| 158 | + { |
| 159 | + "type": "alert-rule-action", |
| 160 | + "title": "Create Plane Work Item or Intake Issue", |
| 161 | + "settings": { |
| 162 | + "uri": "/api/sentry/alert-rule", |
| 163 | + "type": "alert-rule-settings", |
| 164 | + "description": "Create a Plane Work Item or Intake Issue when an alert is triggered", |
| 165 | + "optional_fields": [ |
| 166 | + { |
| 167 | + "uri": "/api/sentry/users", |
| 168 | + "name": "assignee_ids", |
| 169 | + "type": "select", |
| 170 | + "async": false, |
| 171 | + "label": "Assignees", |
| 172 | + "multiple": true, |
| 173 | + "depends_on": ["project_id"] |
| 174 | + }, |
| 175 | + { |
| 176 | + "uri": "/api/sentry/states", |
| 177 | + "name": "state", |
| 178 | + "type": "select", |
| 179 | + "async": false, |
| 180 | + "label": "State", |
| 181 | + "depends_on": ["project_id"] |
| 182 | + }, |
| 183 | + { |
| 184 | + "uri": "/api/sentry/labels", |
| 185 | + "name": "labels", |
| 186 | + "type": "select", |
| 187 | + "async": false, |
| 188 | + "label": "Labels", |
| 189 | + "multiple": true, |
| 190 | + "depends_on": ["project_id"] |
| 191 | + } |
| 192 | + ], |
| 193 | + "required_fields": [ |
| 194 | + { |
| 195 | + "name": "type", |
| 196 | + "type": "select", |
| 197 | + "label": "Type", |
| 198 | + "options": [ |
| 199 | + ["intake", "Intake"], |
| 200 | + ["work_item", "Work Item"] |
| 201 | + ] |
| 202 | + }, |
| 203 | + { |
| 204 | + "uri": "/api/sentry/projects", |
| 205 | + "name": "project_id", |
| 206 | + "type": "select", |
| 207 | + "async": false, |
| 208 | + "label": "Project", |
| 209 | + "depends_on": ["type"] |
| 210 | + } |
| 211 | + ] |
| 212 | + } |
| 213 | + } |
| 214 | + ] |
| 215 | +} |
| 216 | +``` |
| 217 | + |
| 218 | +**What this schema enables** |
| 219 | + |
| 220 | +**Work item linking** |
| 221 | +The first element defines how users create new Plane work items from Sentry or link existing ones. Required fields (title, description, project) ensure every work item has essential information. Optional fields (assignees, priority, labels, state, module, cycle) provide flexibility for detailed work item tracking. |
| 222 | + |
| 223 | +**Alert rule actions** |
| 224 | +The second element enables automatic work item creation when Sentry alerts fire. You can configure whether alerts create regular work items or intake work items for triage, and set default assignees, states, and labels. |
| 225 | + |
| 226 | +### Set permissions |
| 227 | + |
| 228 | +Configure these permissions to allow Sentry to interact with Plane appropriately: |
| 229 | + |
| 230 | +| Permission | Access Level | Why This Matters | |
| 231 | +|------------|--------------|------------------| |
| 232 | +| **Project** | Read | Access project details, tags, and debug files from Sentry | |
| 233 | +| **Team** | Read | Retrieve team member lists for assignee dropdowns | |
| 234 | +| **Release** | No Access | Not required for Plane integration | |
| 235 | +| **Distribution** | No Access | Not required for Plane integration | |
| 236 | +| **Issue & Event** | Read & Write | Create and link issues, sync status updates bidirectionally | |
| 237 | +| **Organization** | Read | Resolve organization IDs and retrieve repository information | |
| 238 | +| **Member** | Read | Access member details for assignee functionality | |
| 239 | +| **Alerts** | Read | Enable alert rule actions for automatic issue creation | |
| 240 | + |
| 241 | + |
| 242 | + |
| 243 | +### Enable webhooks |
| 244 | + |
| 245 | +Webhooks keep Plane and Sentry synchronized. When issues change in Sentry, Plane receives notifications and updates accordingly. |
| 246 | + |
| 247 | +Enable the **issue** webhook with these events: |
| 248 | + |
| 249 | +| Event | Why It's Needed | |
| 250 | +|-------|-----------------| |
| 251 | +| **created** | Notify Plane when new Sentry issues are detected | |
| 252 | +| **resolved** | Update linked Plane work items when Sentry issues are resolved | |
| 253 | +| **assigned** | Sync assignee changes from Sentry to Plane | |
| 254 | +| **archived** | Reflect archived status in Plane | |
| 255 | +| **unresolved** | Update Plane when resolved issues reopen | |
| 256 | + |
| 257 | + |
| 258 | + |
| 259 | +### Save and retrieve credentials |
| 260 | + |
| 261 | +After saving your integration, Sentry generates OAuth credentials: |
| 262 | + |
| 263 | +- **Client ID** - A public identifier for your integration |
| 264 | +- **Client Secret** - A private key used to authenticate API requests |
| 265 | + |
| 266 | +<Warning> |
| 267 | +**Important** |
| 268 | +The Client Secret is only displayed once immediately after creating the integration. Copy it now and store it securely. If you lose it, you'll need to regenerate the integration. |
| 269 | +</Warning> |
| 270 | + |
| 271 | +Copy both the Client ID and Client Secret. You'll need these in the next step. |
| 272 | + |
| 273 | +## Configure your Plane instance |
| 274 | + |
| 275 | +Add Sentry credentials to your Plane instance so it can communicate with Sentry's API. |
| 276 | + |
| 277 | +### Locate your configuration file |
| 278 | + |
| 279 | +**For Docker deployments:** |
| 280 | +- Edit `plane.env` in your Plane installation directory |
| 281 | + |
| 282 | +**For Kubernetes deployments:** |
| 283 | +- Edit your `custom-values.yaml` file or ConfigMap containing environment variables |
| 284 | + |
| 285 | +### Add environment variables |
| 286 | + |
| 287 | +Add these variables to your Plane configuration: |
| 288 | + |
| 289 | +**For Docker (`plane.env`):** |
| 290 | +```env |
| 291 | +# Sentry Integration |
| 292 | +SENTRY_BASE_URL=https://sentry.io |
| 293 | +SENTRY_CLIENT_ID=<your_client_id> |
| 294 | +SENTRY_CLIENT_SECRET=<your_client_secret> |
| 295 | +SENTRY_INTEGRATION_SLUG=plane |
| 296 | +``` |
| 297 | + |
| 298 | +**For Kubernetes (`custom-values.yaml`):** |
| 299 | +```yaml |
| 300 | +env: |
| 301 | + silo_envs: |
| 302 | + sentry_base_url: 'https://sentry.io' |
| 303 | + sentry_client_id: '<your_client_id>' |
| 304 | + sentry_client_secret: '<your_client_secret>' |
| 305 | + sentry_integration_slug: 'plane' |
| 306 | +``` |
| 307 | +
|
| 308 | +Replace `<your_client_id>` and `<your_client_secret>` with the credentials from Step 1. |
| 309 | + |
| 310 | +### Environment variable reference |
| 311 | + |
| 312 | +| Variable | Required | Default | Description | |
| 313 | +|----------|----------|---------|-------------| |
| 314 | +| `SENTRY_BASE_URL` | No | `https://sentry.io` | Base URL of your Sentry instance. For self-hosted Sentry, use your Sentry domain (e.g., `https://sentry.company.com`) | |
| 315 | +| `SENTRY_CLIENT_ID` | Yes | - | Client ID from your Sentry custom integration | |
| 316 | +| `SENTRY_CLIENT_SECRET` | Yes | - | Client Secret from your Sentry custom integration (only shown once during creation) | |
| 317 | +| `SENTRY_INTEGRATION_SLUG` | No | - | The slug identifier for your integration. Find this in your integration's URL: `https://org.sentry.io/settings/developer-settings/plane-local` (here `plane-local` is the slug) | |
| 318 | + |
| 319 | +<Note> |
| 320 | +**Using self-hosted Sentry?** |
| 321 | +If you're running your own Sentry instance, change `SENTRY_BASE_URL` to your Sentry domain. All other configuration remains the same. |
| 322 | +</Note> |
| 323 | + |
| 324 | +### Restart Plane |
| 325 | + |
| 326 | +Apply the configuration changes: |
| 327 | + |
| 328 | +**For Docker:** |
| 329 | +```bash |
| 330 | +docker compose down |
| 331 | +docker compose up -d |
| 332 | +``` |
| 333 | + |
| 334 | +**For Kubernetes:** |
| 335 | +```bash |
| 336 | +helm upgrade plane-app plane-enterprise.tgz \ |
| 337 | + --namespace plane \ |
| 338 | + -f custom-values.yaml |
| 339 | +``` |
| 340 | + |
| 341 | +## Activate integration in your workspace |
| 342 | + |
| 343 | +Once you’ve completed the instance configuration, [activate the Sentry integration](https://docs.plane.so/integrations/sentry#set-up-sentry-integration) in Plane. |
| 344 | + |
| 345 | + |
| 346 | +For questions about Sentry integration, contact [[email protected]](mailto:[email protected]). |
0 commit comments