Uptimeify Docs
Incident management

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

typeRequired config fieldsOptional config fieldsWrite-only fields
slackwebhookUrlwebhookUrl
teamswebhookUrlwebhookUrl
discordwebhookUrlwebhookUrl
jirabaseUrl (must start with https://), email, apiToken, projectKeyissueType (default Task)apiToken
pagerduty_v2routingKeyroutingKey
opsgenieapiKeyregion (us or eu, default eu)apiKey
webhookwebhookUrlbearerToken, 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

FieldTypeDescription
teamsinteger[]Forward only events of incidents of these team IDs.
severitiesstring[]Subset of sev1, sev2, sev3, sev4.
event_kindsstring[]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

FieldTypeRequiredDescription
typestringYesOne of the types above. Cannot be changed later.
namestringYesTrimmed, max 200 characters.
configobjectYesSee integration types and config.
teamIdinteger | nullNoTeam of your organization, or null/omitted for an org-wide integration.
filtersobjectNoSee 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).

  • filters replaces the stored filters completely.
  • config is merged onto the stored config, then the result is validated against the type. Omit a write-only field, or send it as null or "", to keep the stored value.
  • For webhook, sending headers replaces all stored headers, including their values. Omit headers to 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

ParameterTypeDescription
limitintegerPage size, default 50, clamped to 1 to 200.
beforeintegerCursor: 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.

FieldTypeRequiredDescription
deliveryIdintegerYesid 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 Unauthorized when not authenticated
  • 403 Forbidden (imAccessDenied) when using a customer-scoped token, or a session without an IM-eligible role
  • 403 Forbidden (imNotEnabled) when Incident Management is not enabled for the organization
  • 403 Forbidden (imOutboundWriteDenied) when writing an org-wide integration without the admin role
  • 403 Forbidden (imTeamWriteDenied) when writing a team integration without the write bar on that team
  • 400 Bad Request (invalidRequestBody) when :id is not a positive integer, name or type is missing or invalid, a required config field is missing, jira's baseUrl is not https, a filters entry is invalid, teamId or isActive is invalid, or deliveryId is missing
  • 404 Not Found (imOutboundNotFound) when the integration does not exist, or belongs to another organization
  • 404 Not Found (imOutboundDeliveryNotFound) when deliveryId is not a delivery of this integration
  • 422 Unprocessable Entity (invalidTeamId) when teamId does not belong to your organization
  • 422 Unprocessable Entity (imOutboundNotRetryable) when the integration's type cannot be retried
  • 429 Too Many Requests (imOutboundRetryRateLimited) when the retry limit is reached. data.retryAfter is the wait in seconds

On this page