---
title: "Ausgehende Integrationen"
description: "Incident-Management-Ereignisse an Slack, Microsoft Teams, Discord, Jira, PagerDuty, Opsgenie oder einen generischen Webhook weiterleiten, Zustellungen einsehen und erneut senden."
---

`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`

Eine **ausgehende Integration** leitet Incident-Ereignisse (angelegt, bestätigt, Severity oder Status geändert, Kommentar, gelöst) an ein externes System weiter. Anders als ein [Kanal](/de/api/incident-management/channels), der ein Paging-Ziel einer Eskalation ist, spiegelt eine ausgehende Integration den Lebenszyklus des Incidents in ein anderes Werkzeug. Eine Integration gilt entweder organisationsweit (`teamId: null`) oder gehört zu einem [Team](/de/api/incident-management/teams).

## Authentifizierung

Erfordert den Basiszugriff auf Incident Management, den jeder Endpunkt dieser API braucht (eine IM-berechtigte Rolle `admin`, `editor` oder `responder`, oder ein organisationsweites API-Token; Incident Management muss für die Organisation aktiviert sein). Lesen (Liste, Detail, Verlauf) darf jede IM-berechtigte Rolle. **Anlegen, Ändern, Löschen und erneutes Senden** erfordern bei einer organisationsweiten Integration die Rolle `admin`, bei einer Team-Integration die Rolle `admin` oder eine Team-Admin-Mitgliedschaft. Ein organisationsweites API-Token läuft mit der Rolle der Person, die es erstellt hat; ein Token, das ein Organisations-Admin erstellt hat, erfüllt diese Hürde also. Eine Team-Admin-Mitgliedschaft gilt für ein Token nie.

## Integrationstypen und `config`

| `type` | Pflichtfelder in `config` | Optionale Felder in `config` | Nur schreibbare Felder |
|--------|--------------------------|--------------------------|-------------------|
| `slack` | `webhookUrl` | | `webhookUrl` |
| `teams` | `webhookUrl` | | `webhookUrl` |
| `discord` | `webhookUrl` | | `webhookUrl` |
| `jira` | `baseUrl` (muss mit `https://` beginnen), `email`, `apiToken`, `projectKey` | `issueType` (Standard `Task`) | `apiToken` |
| `pagerduty_v2` | `routingKey` | | `routingKey` |
| `opsgenie` | `apiKey` | `region` (`us` oder `eu`, Standard `eu`) | `apiKey` |
| `webhook` | `webhookUrl` | `bearerToken`, `headers` (Objekt aus Header-Name und Wert) | `webhookUrl`, `bearerToken`, `headers` |

**Nur schreibbare Felder werden verschlüsselt gespeichert und nie zurückgegeben.** Jede Antwort setzt sie auf `null` und ergänzt pro Feld ein Flag: `hasWebhookUrl`, `hasApiToken`, `hasRoutingKey`, `hasApiKey`, `hasBearerToken`, und bei `webhook` zusätzlich `hasHeaders` (das ganze `headers`-Objekt kommt als `null` zurück).

URLs werden beim Speichern nicht auf Erreichbarkeit geprüft. Vom Kunden angegebene Ziele werden beim Senden geprüft: Eine URL, die auf eine private oder lokale Adresse auflöst, lässt die Zustellung scheitern; sie erscheint dann als `failed` im [Zustellverlauf](#zustellverlauf).

## Das `filters`-Objekt

| Feld | Typ | Beschreibung |
|-------|------|-------------|
| `teams` | integer[] | Nur Ereignisse von Incidents dieser Team-IDs weiterleiten. |
| `severities` | string[] | Teilmenge von `sev1`, `sev2`, `sev3`, `sev4`. |
| `event_kinds` | string[] | Teilmenge von `incident_created`, `acknowledged`, `severity_changed`, `status_changed`, `comment`, `resolved`. |

Ein leeres Objekt `{}` leitet alles weiter. Bei `jira` wird ein weggelassenes `event_kinds` zu `["incident_created"]`, Jira bekommt also ein Ticket pro Incident.

## Integrationen auflisten

`GET /api/im/outbound`

Liefert alle Integrationen deiner Organisation, nach Name sortiert, Geheimnisse geschwärzt.

### Beispiel (cURL)

```bash
curl -X GET "$BASE_URL/api/im/outbound" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json"
```

### Antwort (Response)

`200 OK`

```json
[
  {
    "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"
  }
]
```

## Integration abrufen

`GET /api/im/outbound/:id`

Liefert eine Integration, in derselben Form wie ein Listeneintrag.

## Integration anlegen

`POST /api/im/outbound`

### Request Body

| Feld | Typ | Erforderlich | Beschreibung |
|-------|------|----------|-------------|
| `type` | string | Ja | Einer der Typen oben. Lässt sich später nicht ändern. |
| `name` | string | Ja | Getrimmt, maximal 200 Zeichen. |
| `config` | object | Ja | Siehe [Integrationstypen und `config`](#integrationstypen-und-config). |
| `teamId` | integer \| null | Nein | Team deiner Organisation, oder `null`/weggelassen für eine organisationsweite Integration. |
| `filters` | object | Nein | Siehe [das `filters`-Objekt](#das-filters-objekt). Standard `{}`. |

Neue Integrationen sind immer aktiv.

### Beispiel (cURL)

```bash
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"
    }
  }'
```

### Antwort (Response)

`200 OK`: die angelegte Integration, Geheimnisse geschwärzt.

```json
{
  "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"
}
```

## Integration ändern

`PATCH /api/im/outbound/:id`

Teilweise Aktualisierung von `name`, `teamId`, `filters`, `config` und `isActive` (Boolean, oder die Strings `"true"`/`"false"`). Andere Schlüssel, auch `type`, werden ignoriert. Du brauchst die Schreibhürde für den aktuellen Geltungsbereich der Integration und, wenn du `teamId` änderst, auch für den neuen (`null` macht sie organisationsweit).

- `filters` ersetzt die gespeicherten Filter vollständig.
- `config` wird mit der gespeicherten Konfiguration zusammengeführt, danach wird das Ergebnis gegen den Typ geprüft. Lass ein nur schreibbares Feld weg oder schick `null` oder `""`, um den gespeicherten Wert zu behalten.
- Bei `webhook` ersetzt ein mitgeschicktes `headers` alle gespeicherten Header samt Werten. Lass `headers` weg, um sie zu behalten.

```bash
curl -X PATCH "$BASE_URL/api/im/outbound/7" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "isActive": false }'
```

`200 OK`: die aktualisierte Integration, Geheimnisse geschwärzt.

## Integration löschen

`DELETE /api/im/outbound/:id`

Löscht auch den Zustellverlauf der Integration.

```bash
curl -X DELETE "$BASE_URL/api/im/outbound/7" \
  -H "Authorization: Bearer $TOKEN"
```

`200 OK`

```json
{ "ok": true }
```

## Zustellverlauf

`GET /api/im/outbound/:id/history`

Zustellversuche einer Integration, neueste zuerst. Zustellungen werden 90 Tage aufbewahrt.

### Query-Parameter

| Parameter | Typ | Beschreibung |
|-----------|------|-------------|
| `limit` | integer | Seitengröße, Standard `50`, begrenzt auf 1 bis 200. |
| `before` | integer | Cursor: liefert Zustellungen mit einer kleineren `id`. Übergib `nextCursor` der vorigen Seite. |

```bash
curl -X GET "$BASE_URL/api/im/outbound/6/history?limit=20" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json"
```

`200 OK`. `requestSummary` enthält nur den Typ, den Ursprung des Ziels (ohne Pfad) und die Methode; `responseSummary` enthält den Statuscode und einen gekürzten Body. Keins von beiden enthält Geheimnisse.

```json
{
  "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` ist `sent`, `failed` oder `retrying`.

## Zustellung erneut senden

`POST /api/im/outbound/:id/retry`

Stellt einen neuen Sendeauftrag für eine Zustellung dieser Integration in die Warteschlange. Gesendet wird der **aktuelle** Stand des Incidents (Titel, Severity, Status von jetzt), nicht der ursprüngliche Payload. Der Aufruf kehrt zurück, sobald der Auftrag eingereiht ist; das Ergebnis erscheint als neuer Eintrag im Zustellverlauf.

Begrenzt auf 10 Wiederholungen pro Minute und Integration. Die Grenze zählt jeden berechtigten Aufruf, auch einen, der danach an der Validierung scheitert.

| Feld | Typ | Erforderlich | Beschreibung |
|-------|------|----------|-------------|
| `deliveryId` | integer | Ja | `id` einer Zustellung aus dem Verlauf dieser Integration. |

```bash
curl -X POST "$BASE_URL/api/im/outbound/6/retry" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "deliveryId": 88412 }'
```

`200 OK`

```json
{ "ok": true }
```

## Häufige Fehler

- `401 Unauthorized` wenn du nicht authentifiziert bist
- `403 Forbidden` (`imAccessDenied`) bei Verwendung eines kunden-gescopten Tokens, oder wenn deine Session keine IM-berechtigte Rolle hat
- `403 Forbidden` (`imNotEnabled`) wenn Incident Management für die Organisation nicht aktiviert ist
- `403 Forbidden` (`imOutboundWriteDenied`) wenn du eine organisationsweite Integration ohne die Rolle `admin` schreibst
- `403 Forbidden` (`imTeamWriteDenied`) wenn du eine Team-Integration ohne die Schreibhürde für dieses Team schreibst
- `400 Bad Request` (`invalidRequestBody`) wenn `:id` keine positive Ganzzahl ist, `name` oder `type` fehlt oder ungültig ist, ein Pflichtfeld in `config` fehlt, `baseUrl` bei `jira` nicht `https` nutzt, ein Eintrag in `filters` ungültig ist, `teamId` oder `isActive` ungültig ist, oder `deliveryId` fehlt
- `404 Not Found` (`imOutboundNotFound`) wenn die Integration nicht existiert oder zu einer anderen Organisation gehört
- `404 Not Found` (`imOutboundDeliveryNotFound`) wenn `deliveryId` keine Zustellung dieser Integration ist
- `422 Unprocessable Entity` (`invalidTeamId`) wenn `teamId` nicht zu deiner Organisation gehört
- `422 Unprocessable Entity` (`imOutboundNotRetryable`) wenn der Typ der Integration nicht erneut gesendet werden kann
- `429 Too Many Requests` (`imOutboundRetryRateLimited`) wenn die Wiederholungsgrenze erreicht ist. `data.retryAfter` ist die Wartezeit in Sekunden
