# API: Integrations

Connect your tools in both directions.

## Integrations

See [Integrations](/docs/integrations).

### GET /v1/integrations

Needs a key tied to a person. Returns `{"integrations": [{name, connected, detail, last_error}]}` for `slack`, `discord`, `webhook`, `linear` and `posthog`. `detail` is a masked URL or a short description. `last_error` says why the last release delivery failed and how to fix it; it clears on the next successful delivery, test, reconnect or disconnect. Change management providers whose flags are on are listed too.

### PUT /v1/integrations/{name}

Connects an integration. Owner only (with roles: `integrations.manage`). Integrations are included; feature availability, early access and required provider permissions still apply.

| `{name}` | Fields |
|----------|--------|
| `slack` | `url`: a `https://hooks.slack.com/` incoming webhook |
| `discord` | `url`: a Discord channel webhook |
| `webhook` | `url`: https on a public host |
| `linear` | `api_key`, `team` (ID, key or name) |
| `posthog` | `host` (`us`, `eu` or your https address), `project_id`, `api_key` (a `phx_` personal key with `annotation:write`) |

#### Change management providers

> **Beta, early access.** Flags: `jira`, `servicenow`, `teams`, `vanta`, `drata`. Until your workspace has a provider's feature, connecting it answers `404 NOT_FOUND`.

These providers take a `settings` object instead:

| `{name}` | `settings` keys |
|----------|-----------------|
| `jira` | `site`, `email`, `api_token`, `project`, `issue_type` (default `Task`), `approve`, `ship`, `rollback` (transition names) |
| `servicenow` | `instance`, `auth` (`basic` or `oauth`), `username` and `password`, or `client_id` and `client_secret`, `assignment_group` |
| `teams` | `webhook_url`: a Power Automate Workflows URL |
| `vanta` | `client_id`, `client_secret`, `document_id` |
| `drata` | `region` (`us`, `eu` or `apac`), `workspace_id`, `evidence_id`, `api_key` |

Credentials are tested when you connect and stored encrypted. Returns the integrations list.

### DELETE /v1/integrations/{name}

Owner only (with roles: `integrations.manage`). Returns `204`.

### POST /v1/integrations/{name}/test

Sends a test notification, a `webhook.test` event for `webhook`, or checks the connection for `notion`, `confluence` and change management. Owner only (with roles: `integrations.manage`). Returns `{"ok": true}`, `404 NOT_FOUND` if it isn't connected, or `502 UPSTREAM_ERROR` with what to fix if the test failed. Each test is written to the audit log as `integration.test` or `integration.test.failed`.

### POST /v1/integrations/linear/teams

Lists the Linear teams an API key can reach, so you can pick one before connecting. Owners and admins only. Send `api_key`; it isn't saved. Returns `{"teams": [{id, key, name}]}`. Use `bumpkit integrations linear teams` or the `integrations_linear_teams` MCP tool.

### POST /v1/integrations/{name}/request

Asks for access to an integration that's coming soon: `origin`, `gitlab`, `bitbucket` or `azure-devops`. Read-only roles get `403 FORBIDDEN`. Returns `{"key", "status": "requested"}`, or `404 NOT_FOUND` for any other name or when your workspace already has it. Use `bumpkit integrations request NAME` or the `integrations_request` MCP tool.

### Failed deliveries

Change management providers only, while their flag is on; any other name answers `404 NOT_FOUND`. Use `bumpkit integrations deliveries` or the `integrations_deliveries_list` and `integrations_deliveries_retry` MCP tools.

| Method | Route | Result |
| --- | --- | --- |
| GET | `/v1/integrations/{name}/deliveries` | `{"deliveries": [{id, provider, kind, attempts}]}`: deliveries that failed for good. Needs a key tied to a person |
| POST | `/v1/integrations/{name}/deliveries/{id}/retry` | Queues one more attempt. Owners and admins only. Returns `204`, or `404 NOT_FOUND` when the delivery doesn't exist or isn't failed |

### OAuth authorization

Connects `slack-app`, `jira` (flag `jira`), `notion` or `confluence` (flag `notion_confluence`) with consent in the browser, so no secret passes through the API. Needs a personal key from an owner or admin; other keys get `403 FORBIDDEN`. A provider whose flag is off gets `403 FEATURE_UNAVAILABLE`. Use `bumpkit integrations authorize` and `oauth-status`, or the `integrations_authorize` and `integrations_oauth_status` MCP tools.

| Method | Route | Result |
| --- | --- | --- |
| POST | `/v1/integration-consent/{provider}` | Send optional `settings`: `site`, `project`, `issue_type`, `approve`, `ship` and `rollback` for `jira`; `parent` for `notion`; `base_url`, `parent` and `parent_page` for `confluence`. Returns `201` with `provider`, `workspace_id`, `url`, `flow` and `expires_at`. Open `url` in a browser. `503 NOT_CONFIGURED` when OAuth isn't available for that provider |
| GET | `/v1/integration-consent/{provider}/status` | `provider`, `workspace_id`, `connected`, `name`, `channel`, `channel_name`, `auth`, `missing_scopes` and `scopes_unrecorded`. Query `flow` to also get `complete` and `needs_channel` for that authorization |

### Slack app

Manage the Slack app once it's authorized. Needs a personal key from an owner or admin. Use `bumpkit integrations slack-app`, or the `integrations_slack_app_channel`, `integrations_slack_app_posting` and `integrations_slack_app_disconnect` MCP tools.

| Method | Route | Result |
| --- | --- | --- |
| PUT | `/v1/integrations/slack-app/channel` | Send `channel`, the Slack channel ID. Returns the same fields as the status route above |
| PUT | `/v1/integrations/slack-app/posting` | Send `posting`: `every` or `approval`. Returns `{"posting"}` |
| DELETE | `/v1/integrations/slack-app` | Disconnects the app. Returns `204` |

### Webhook sources

Create the URLs that [inbound webhooks](#inbound-webhooks) receive on. `{provider}` is `webhook`, `sentry`, `pagerduty`, `datadog`, `vercel`, `argocd` or `railway`; the last five need their flag, and incident sources (`webhook`, `sentry`, `pagerduty`, `datadog`) also need `forensics` or `release_health`. These routes need a key tied to a person, and only owners and admins can change sources. Use `bumpkit sources` or the `sources_list`, `sources_add`, `sources_set_secret` and `sources_remove` MCP tools.

| Method | Route | Result |
| --- | --- | --- |
| GET | `/v1/sources/{provider}` | `{"sources": [{id, provider, repository, webhook_url, status, signing_header, secret_origin, instructions, payload}]}` |
| POST | `/v1/sources/{provider}` | Send `repository` as `owner/name`. Returns `201 {"source", "signing_secret"}`. A secret Bumpkit generates is shown once |
| PUT | `/v1/sources/{provider}/{id}/secret` | Send `secret`, the signing secret your provider issued. Returns the source |
| DELETE | `/v1/sources/{provider}/{id}` | Removes the source. Returns `204` |

## Repositories

Flag: `github_app`. Without it these answer `403 EARLY_ACCESS_REQUIRED`. All of them need a key tied to a person. Use `bumpkit repo list`, `verify`, `add` and `pulls list`; the MCP tools are `repo_list`, `repo_verify`, `repo_add` and `repo_pulls_list`.

### GET /v1/repositories

Repositories the workspace's GitHub App installations can reach. Returns `workspace_id`, `workspace_name` and `repositories`: each has `repository`, `default_branch`, `workspace_id`, `workspace_name`, `installation_id` and `verified`.

### POST /v1/repositories/verify

Checks that this workspace can reach one repository. Send `repository` as `owner/name`. Returns one repository, as above.

### POST /v1/repositories/connect

Owners and admins only. Send `repository` as `owner/name`, or `{}` to choose after connecting. Returns `authorization_url`, `workspace_id` and `workspace_name`. Open the URL in a browser to finish in GitHub.

### GET /v1/repositories/pulls

Open pull requests in a connected repository. Query `repository` and `limit` (1 to 50, default 50). Returns `repository`, `workspace_id`, `workspace_name` and `pulls`: each has `number`, `title`, `draft`, `user`, `head` and `base`.

## Inbound webhooks

These endpoints receive events from other services. They don't take an API key: a token in the path picks the source, and a signature proves the sender. An unknown token, or a feature that is off, returns `404`. A bad signature returns `401 INVALID_SIGNATURE`.

### POST /hooks/errors/{token}

> **Beta, early access.** Flag: `release_health`.

Sends production errors for [release health](/docs/release-health). Create a source on the dashboard's Integrations page: you get the URL and, for a generic webhook, a secret shown once.

| Source | Signature |
|--------|-----------|
| Generic webhook | `X-Bumpkit-Signature: sha256=<hex HMAC-SHA256 of the raw body, keyed with the source secret>` |
| Sentry | `Sentry-Hook-Signature`, with the Sentry client secret you pasted |

Generic body: one event, or `{"events": [...]}` with 1 to 500 events.

| Field | Type | Description |
|-------|------|-------------|
| `fingerprint` | string | Groups occurrences of the same error |
| `type`, `message` | string | The error |
| `environment`, `version` | string | Where it happened |
| `frames` | array | `[{file, function, line}]` stack frames |
| `count` | integer | Occurrences, default 1 |
| `timestamp` | string | RFC 3339 |

Each event needs at least one of `type`, `message`, `fingerprint` or `frames`.

```bash
body='{"events":[{"type":"TypeError","message":"x is undefined","environment":"production","version":"v2.3.0","frames":[{"file":"src/cart.ts","line":42}]}]}'
sig=$(printf '%s' "$body" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl https://bumpkit.dev/hooks/errors/$TOKEN \
  -H "Content-Type: application/json" \
  -H "X-Bumpkit-Signature: sha256=$sig" \
  -d "$body"
```

Returns `202 {"recorded": n}`. Limited to 600 requests per minute per source; batch events. The body limit is 1 MB.

### POST /hooks/incidents/{token}

> **Beta, early access.** Flags: `forensics` or `release_health`.

Opens an incident from an alert and traces it to a release. Uses the same sources and signatures as `/hooks/errors`.

Generic body: `title`, `url`, `id`, `started_at` (RFC 3339), and `error` (with its stack trace) or `files`. Sentry sends `event_alert` and new `issue` events; others are ignored with `204`.

With change management (Beta, flags `pagerduty` and `datadog`) two more sources are available:

| Source | Signature | Events used |
|--------|-----------|-------------|
| PagerDuty | `X-PagerDuty-Signature: v1=<hex HMAC-SHA256>` | `incident.triggered` |
| Datadog | `X-Bumpkit-Secret: <source secret>` | Triggered alerts |

Returns `202`. The body limit is 1 MB.

### POST /hooks/deploys/{token}

> **Beta, early access.** Flags: `vercel_deploys`, `argocd_deploys`, `railway_deploys`.

Records deployments from your deploy platform, as [`POST /v1/deployments`](/docs/api-releases#post-v1-deployments) does. Create the source on the Integrations page.

| Source | Signature | Events used |
|--------|-----------|-------------|
| Vercel | `x-vercel-signature: <hex HMAC-SHA1 of the body>` | `deployment.succeeded`, `deployment.promoted` |
| Argo CD | `X-Bumpkit-Secret: <source secret>` | Notifications with phase `Succeeded` |
| Railway | `X-Bumpkit-Secret: <source secret>` | `deployment.*` with status `SUCCESS` |

Returns `204`. Limited to 600 requests per minute per IP.


### POST /hooks/gate/{provider}/{token}

Runs the gate on GitLab merge requests, and on Bitbucket, Azure DevOps and GitHub Enterprise Server pull requests. [`POST /v1/gate/connections`](/docs/api-gate#post-v1-gate-connections) returns the URL and secret.

| `{provider}` | Signature |
|--------------|-----------|
| `gitlab` | `X-Gitlab-Token: <webhook secret>` |
| `bitbucket` | `X-Hub-Signature: sha256=<hex HMAC-SHA256 of the body>` |
| `azure_devops` | HTTP Basic auth with the webhook secret as the password |
| `ghes` | `X-Hub-Signature-256: sha256=<hex HMAC-SHA256 of the body>` |

Actionable events return `202` and the gate runs in the background, reporting a "Bumpkit release gate" status and a comment. Other events return `204`. Limited to 600 requests per minute per IP; the body limit is 5 MB.

## Outbound notifications

When an integration is connected, a successful `POST /v1/analyze` fires a release notification to Slack, Discord, Linear, PostHog and the outbound webhook. Notifications are fire-and-forget: a failure never affects the API response.

The Slack message uses [Block Kit](https://api.slack.com/block-kit): a header with the new version, fields for bump type and repository (when `repository` was sent), and the changelog, cut at 2,900 characters to fit Slack's block limit.
