Outbound Integrations
Forward Incident Management events to Slack, Microsoft Teams, Discord, Jira, PagerDuty, Opsgenie or a generic webhook, and inspect or retry deliveries.
GET /api/im/outbound · POST /api/im/outbound · GET /api/im/outbound/:id · PATCH /api/im/outbound/:id · DELETE /api/im/outbound/:id · GET /api/im/outbound/:id/history · POST /api/im/outbound/:id/retry
An outbound integration forwards incident events (created, acknowledged, severity or status changed, comment, resolved) to an external system. Unlike a channel, which is a paging target of an escalation, an outbound integration mirrors the incident lifecycle into another tool. An integration is either org-wide (teamId: null) or belongs to one team.
Authentication
Requires the base IM access every endpoint in this API needs (an IM-eligible role admin, editor or responder, or an organization-wide API token; Incident Management must be enabled for the organization). Reading (list, detail, history) is open to any IM-eligible role. Creating, updating, deleting and retrying require the admin role for an org-wide integration, and the admin role or team-admin membership for a team integration. An organization-wide API token runs with the role of the user who created it, so a token created by an organization admin passes this bar. Team-admin membership never applies to a token.
Integration types and config
type | Required config fields | Optional config fields | Write-only fields |
|---|---|---|---|
slack | webhookUrl | webhookUrl | |
teams | webhookUrl | webhookUrl | |
discord | webhookUrl | webhookUrl | |
jira | baseUrl (must start with https://), email, apiToken, projectKey | issueType (default Task) | apiToken |
pagerduty_v2 | routingKey | routingKey | |
opsgenie | apiKey | region (us or eu, default eu) | apiKey |
webhook | webhookUrl | bearerToken, headers (object of header name to value) | webhookUrl, bearerToken, headers |
Write-only fields are encrypted at rest and never returned. Every response sets them to null and adds a flag per field: hasWebhookUrl, hasApiToken, hasRoutingKey, hasApiKey, hasBearerToken, and for webhook also hasHeaders (the whole headers object is returned as null).
URLs are not checked for reachability when you save them. Customer-supplied destinations are checked at send time: a URL that resolves to a private or local address fails the delivery, which then shows up as failed in the delivery history.
The filters object
| Field | Type | Description |
|---|---|---|
teams | integer[] | Forward only events of incidents of these team IDs. |
severities | string[] | Subset of sev1, sev2, sev3, sev4. |
event_kinds | string[] | Subset of incident_created, acknowledged, severity_changed, status_changed, comment, resolved. |
An empty object {} forwards everything. For jira, an omitted event_kinds defaults to ["incident_created"], so Jira gets one issue per incident.
List integrations
GET /api/im/outbound
Returns all integrations of your organization, sorted by name, secrets redacted.
Example (cURL)
curl -X GET "$BASE_URL/api/im/outbound" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json"Response
200 OK
[
{
"id": 6,
"organizationId": 1,
"teamId": null,
"type": "pagerduty_v2",
"name": "PagerDuty bridge",
"config": { "routingKey": null, "hasRoutingKey": true },
"filters": { "severities": ["sev1", "sev2"] },
"isActive": true,
"createdAt": "2026-09-22T12:00:00.000Z",
"updatedAt": "2026-09-22T12:00:00.000Z"
}
]Get an integration
GET /api/im/outbound/:id
Returns one integration, same shape as a list item.
Create an integration
POST /api/im/outbound
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | One of the types above. Cannot be changed later. |
name | string | Yes | Trimmed, max 200 characters. |
config | object | Yes | See integration types and config. |
teamId | integer | null | No | Team of your organization, or null/omitted for an org-wide integration. |
filters | object | No | See the filters object. Default {}. |
New integrations are always active.
Example (cURL)
curl -X POST "$BASE_URL/api/im/outbound" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "jira",
"name": "Jira OPS",
"teamId": 3,
"config": {
"baseUrl": "https://example.atlassian.net",
"email": "ops-bot@example.com",
"apiToken": "ATATT3xFfGF0aB12",
"projectKey": "OPS"
}
}'Response
200 OK: the created integration, secrets redacted.
{
"id": 7,
"organizationId": 1,
"teamId": 3,
"type": "jira",
"name": "Jira OPS",
"config": {
"baseUrl": "https://example.atlassian.net",
"email": "ops-bot@example.com",
"apiToken": null,
"projectKey": "OPS",
"hasApiToken": true
},
"filters": { "event_kinds": ["incident_created"] },
"isActive": true,
"createdAt": "2026-09-22T12:10:00.000Z",
"updatedAt": "2026-09-22T12:10:00.000Z"
}Update an integration
PATCH /api/im/outbound/:id
Partial update of name, teamId, filters, config and isActive (boolean, or the strings "true"/"false"). Other keys, including type, are ignored. You need the write bar on the integration's current scope, and, when you change teamId, also on the new scope (null makes it org-wide).
filtersreplaces the stored filters completely.configis merged onto the stored config, then the result is validated against the type. Omit a write-only field, or send it asnullor"", to keep the stored value.- For
webhook, sendingheadersreplaces all stored headers, including their values. Omitheadersto keep them.
curl -X PATCH "$BASE_URL/api/im/outbound/7" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "isActive": false }'200 OK: the updated integration, secrets redacted.
Delete an integration
DELETE /api/im/outbound/:id
Also deletes the integration's delivery history.
curl -X DELETE "$BASE_URL/api/im/outbound/7" \
-H "Authorization: Bearer $TOKEN"200 OK
{ "ok": true }Delivery history
GET /api/im/outbound/:id/history
Delivery attempts of one integration, newest first. Deliveries are kept for 90 days.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Page size, default 50, clamped to 1 to 200. |
before | integer | Cursor: return deliveries with an id lower than this. Pass the previous page's nextCursor. |
curl -X GET "$BASE_URL/api/im/outbound/6/history?limit=20" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json"200 OK. requestSummary holds only the type, the target origin (no path) and the method; responseSummary holds the status code and a truncated body. Neither contains secrets.
{
"deliveries": [
{
"id": 88412,
"integrationId": 6,
"incidentId": 42,
"eventKind": "incident_created",
"status": "failed",
"requestSummary": { "type": "pagerduty_v2", "url": "https://events.pagerduty.com", "method": "POST" },
"responseSummary": { "statusCode": 429, "body": "Rate limit exceeded" },
"at": "2026-09-22T13:02:11.000Z"
}
],
"nextCursor": null,
"hasMore": false
}status is sent, failed or retrying.
Retry a delivery
POST /api/im/outbound/:id/retry
Queues a new send for one delivery of this integration. The retry sends the current state of the incident (title, severity, status as of now), not the original payload. The call returns as soon as the job is queued; the outcome appears as a new entry in the delivery history.
Limited to 10 retries per minute per integration. The limit counts every authorized call, also one that then fails validation.
| Field | Type | Required | Description |
|---|---|---|---|
deliveryId | integer | Yes | id of a delivery from this integration's history. |
curl -X POST "$BASE_URL/api/im/outbound/6/retry" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "deliveryId": 88412 }'200 OK
{ "ok": true }Common errors
401 Unauthorizedwhen not authenticated403 Forbidden(imAccessDenied) when using a customer-scoped token, or a session without an IM-eligible role403 Forbidden(imNotEnabled) when Incident Management is not enabled for the organization403 Forbidden(imOutboundWriteDenied) when writing an org-wide integration without theadminrole403 Forbidden(imTeamWriteDenied) when writing a team integration without the write bar on that team400 Bad Request(invalidRequestBody) when:idis not a positive integer,nameortypeis missing or invalid, a requiredconfigfield is missing,jira'sbaseUrlis nothttps, afiltersentry is invalid,teamIdorisActiveis invalid, ordeliveryIdis missing404 Not Found(imOutboundNotFound) when the integration does not exist, or belongs to another organization404 Not Found(imOutboundDeliveryNotFound) whendeliveryIdis not a delivery of this integration422 Unprocessable Entity(invalidTeamId) whenteamIddoes not belong to your organization422 Unprocessable Entity(imOutboundNotRetryable) when the integration's type cannot be retried429 Too Many Requests(imOutboundRetryRateLimited) when the retry limit is reached.data.retryAfteris the wait in seconds
Channels
List, create, update and delete Incident Management paging channels (webhook, Slack, email), including the write-only handling of webhook URLs and headers.
Status Page Rules
Couple Incident Management to your status pages: rules that flip a page to warning or degraded on incidents of a team, plus a manual override.