Alert-Sources-API
Alert-Sources im Incident Management anlegen, konfigurieren und testen: Ingest-Tokens, optionale HMAC- oder Bearer-Authentifizierung, Payload-Mapping, Wartungsfenster und Statistiken je Source.
Eine Alert-Source ist eine eingehende Integration. Jede Webhook-Source hat ihre eigene Ingest-URL (https://uptimeify.io/api/im/ingest/<token>) und ein Payload-Mapping, das das JSON des Anbieters in einen Alert übersetzt. Wie du Zabbix, Datadog, Grafana und andere auf diese URL zeigen lässt, steht in den Einrichtungsanleitungen. Diese Seite beschreibt die Endpunkte, mit denen du die Sources selbst verwaltest.
Authentifizierung
Jeder Endpunkt braucht IM-Zugriff: eine IM-berechtigte Rolle (admin, editor, responder) oder einen organisationsweiten API-Token. Incident Management muss für die Organisation aktiviert sein. Ein kunden-gescopter Token wird mit 403 Forbidden (customerScopedTokenForbidden) abgewiesen.
- Lese-Endpunkte (Liste, Detail, Presets, Payloads, Statistik, test-mapping) stehen jedem IM-berechtigten Aufrufer offen.
- Schreib-Endpunkte (Anlegen, Ändern, Löschen, Token- und Secret-Rotation, disable-auth, test-alert) verlangen zusätzlich die Rolle
adminoder eine Team-Admin-Mitgliedschaft im Team der Source. Ein API-Token trägt die Rolle der Person, die ihn erstellt hat. Nimm also einen Token, den ein Organisations-Admin erstellt hat.
Sources sind auf deine Organisation beschränkt. Eine Source einer anderen Organisation antwortet mit 404 Not Found (imSourceNotFound), nie mit 403.
Das Source-Objekt
Liste, Detail, Ändern und disable-auth liefern diese Form. Zugangsdaten tauchen darin nie auf: Ingest-Token, Bearer-Token und HMAC-Secret gibt es nur einmal, vom Endpunkt, der sie erzeugt.
| Feld | Typ | Beschreibung |
|---|---|---|
id | number | ID der Source. |
organizationId | number | Besitzende Organisation. |
teamId | number | Team, dessen Eskalation die Source speist. |
name | string | Anzeigename, bis zu 120 Zeichen. |
type | string | zabbix, datadog, grafana, alertmanager, sentry, custom, api, email oder uptimeify_monitoring (die eingebaute Brücke aus dem klassischen Monitoring, von der Plattform angelegt). |
authMode | object | { url_token?, hmac?: { enabled }, bearer?: { enabled } }. Siehe Optionale Absender-Authentifizierung. |
payloadMapping | object | Feld-Mapping, das auf jede eingehende Payload angewendet wird. |
autoResolve | boolean | Ob ein Resolve-Event den Incident automatisch schließt. Wird auf jeden neuen Incident übertragen. |
groupingWindowMinutes | number oder null | Gruppierungsfenster. null gruppiert nur nach Dedup-Key. |
maintenance | object oder null | Wiederkehrende Wartungsfenster, { windows: [...] }. |
snoozeUntil | ISO 8601 oder null | Stummgeschaltet bis zu diesem Zeitpunkt. |
secondaryExpiresAt | ISO 8601 oder null | Bis wann der vorige Ingest-Token nach einer Rotation noch gilt. |
createdAt, updatedAt | ISO 8601 | Zeitstempel. |
{
"id": 7,
"organizationId": 1,
"teamId": 3,
"name": "Zabbix production",
"type": "zabbix",
"secondaryExpiresAt": null,
"payloadMapping": {
"title": "trigger_name",
"severity": "event_severity",
"status": "trigger_status",
"host": "host_name",
"dedupKey": "event_id",
"severityMap": { "Disaster": "sev1", "High": "sev2", "Average": "sev3", "Warning": "sev4", "Information": "sev4", "Not classified": "sev4" },
"resolveValues": ["RESOLVED", "OK"]
},
"autoResolve": true,
"groupingWindowMinutes": null,
"maintenance": null,
"snoozeUntil": null,
"createdAt": "2026-10-01T08:00:00.000Z",
"updatedAt": "2026-10-01T08:00:00.000Z",
"authMode": {}
}Sources auflisten
GET /api/im/sources
Liefert alle Sources deiner Organisation als Array von Source-Objekten, sortiert nach Name.
curl "$BASE_URL/api/im/sources" \
-H "Authorization: Bearer $TOKEN"Source abrufen
GET /api/im/sources/:id
Liefert ein Source-Objekt.
Presets auflisten
GET /api/im/sources/presets
Liefert die eingebauten Presets, die du beim Anlegen als type übergeben kannst. docsSlug ist die Anleitung unter /de/api/incident-management/alert-sources/, vendorTemplate eine fertige Anbieter-Konfiguration (nur Zabbix bringt eine mit, ein Media-Type-YAML), samplePayload die Test-Payload, auf die test-mapping und test-alert zurückfallen.
[
{
"name": "zabbix",
"docsSlug": "zabbix",
"vendorTemplate": "zabbix_export:\n version: '7.4'\n ...",
"samplePayload": { "event_id": "6633487", "event_severity": "Disaster", "trigger_name": "Zabbix agent is not available on Zabbix server", "trigger_status": "PROBLEM", "host_name": "Zabbix server" }
},
{ "name": "datadog", "docsSlug": "datadog", "vendorTemplate": null, "samplePayload": { "alert_id": "1234567", "alert_title": "[Triggered] High CPU usage on host web-01" } }
]Die vollständige Liste ist zabbix, datadog, grafana, alertmanager, sentry, custom (Beispiel-Payloads oben gekürzt).
Source anlegen
POST /api/im/sources
Request Body
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
teamId | number | Ja | Team in deiner Organisation. |
name | string | Ja | 1 bis 120 Zeichen, Leerraum wird abgeschnitten. |
type | string | Ja | Ein Preset-Name, api (keine Ingest-URL, gespeist über Events-Ingest) oder email (eingehende Mail-Adresse). |
payloadMapping | object | Nein | Weglassen, um mit dem Mapping des Presets zu starten. Ein expliziter Wert, auch {}, ersetzt das Preset. Bei email bleibt nur der verschachtelte Schlüssel emailSelectors erhalten, siehe E-Mail-Anleitung. |
autoResolve | boolean | Nein | Standard true. |
groupingWindowMinutes | number oder null | Nein | Positive Ganzzahl oder null (Standard). |
authMode | object | Nein | Hier werden nur url_token und enabled: false angenommen. HMAC und Bearer schaltest du über ihre eigenen Endpunkte weiter unten ein. |
payloadMapping gibt es in zwei Formen. Die flache Form nutzt String-Selektoren title, severity, status, host, dedupKey, dazu severityMap (Rohwert auf sev1 .. sev4), resolveValues (Array von Strings) und custom (Objekt aus Selektoren). Die Attribut-Form ist { "version": 2, "attributes": [...] }, jedes Attribut { key, standard?, steps?, flags? } mit Schritten { kind: "extract", type: "path" | "jsonpath" | "constant", expr } oder { kind: "valueMap", entries: [{ from, to }], fallback? }. Beide Formen kennen defaultSeverity (sev1 .. sev4, Standard sev3), das greift, wenn die Payload keinen gültigen Schweregrad liefert.
Beispiel (cURL)
curl -X POST "$BASE_URL/api/im/sources" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "teamId": 3, "name": "Zabbix production", "type": "zabbix" }'Antwort (Response)
200 OK: das Source-Objekt plus ingestToken. Der Token steht nur in dieser Antwort. Gespeichert wird nur sein SHA-256-Hash, später lesen kannst du ihn nicht mehr. Die Webhook-URL lautet https://uptimeify.io/api/im/ingest/<ingestToken>. Bei type: "api" ist der Token null. Bei type: "email" enthält die Antwort zusätzlich inboundEmailAddress (<token>@alerts.uptimeify.io).
{
"id": 7,
"organizationId": 1,
"teamId": 3,
"name": "Zabbix production",
"type": "zabbix",
"secondaryExpiresAt": null,
"payloadMapping": { "title": "trigger_name", "dedupKey": "event_id" },
"autoResolve": true,
"groupingWindowMinutes": null,
"maintenance": null,
"snoozeUntil": null,
"createdAt": "2026-10-01T08:00:00.000Z",
"updatedAt": "2026-10-01T08:00:00.000Z",
"authMode": {},
"ingestToken": "Q2hR4mVx9LkP0sTz7bNw1cYe5uJa3fGd"
}Source ändern
PATCH /api/im/sources/:id
Schick nur die Felder, die du ändern willst. Die Schreibberechtigung wird am aktuellen Team geprüft, beim Verschieben zusätzlich am Zielteam.
| Feld | Typ | Beschreibung |
|---|---|---|
name | string | 1 bis 120 Zeichen. |
teamId | number | Verschiebt die Source in ein anderes Team derselben Organisation. |
payloadMapping | object | Ersetzt das ganze Mapping, es wird nicht zusammengeführt. null setzt ein leeres Mapping. |
authMode | object | Wird mit dem gespeicherten Wert zusammengeführt. hmac.enabled: true / bearer.enabled: true gehen erst, wenn ein Secret existiert (siehe unten). bearer.token_hash wird abgelehnt. |
autoResolve | boolean | |
groupingWindowMinutes | number oder null | null entfernt das Fenster. |
maintenance | object oder null | { "windows": [{ "dow": [1,2,3,4,5], "from": "22:00", "to": "23:30", "timezone": "Europe/Berlin" }] }. from/to als 24-h-HH:MM, dow ISO-Wochentage 1 (Montag) bis 7, timezone eine IANA-Zone, höchstens 20 Fenster. null entfernt die Wartung. Alerts innerhalb eines Fensters werden unterdrückt. |
snoozeUntil | ISO 8601 oder null | Schaltet die Source bis zu diesem Zeitpunkt stumm. Ein Wert in der Vergangenheit wird angenommen und wirkt nicht. null hebt es auf. |
Den Ingest-Token änderst du hier nicht, dafür gibt es rotate-token.
curl -X PATCH "$BASE_URL/api/im/sources/7" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "snoozeUntil": "2026-10-09T18:00:00Z", "groupingWindowMinutes": 15 }'200 OK: das geänderte Source-Objekt.
Source löschen
DELETE /api/im/sources/:id
Wird mit 409 abgelehnt, solange die Source noch offene Alerts hat oder primäre Source eines offenen Incidents ist (alles, was nicht resolved oder merged ist). Löse diese zuerst. Die Monitoring-Brücke (uptimeify_monitoring) lässt sich nicht löschen.
200 OK: { "success": true }.
Der Body der 409 (imSourceReferencesExist) nennt, was das Löschen blockiert (jede Liste höchstens 50 Einträge):
{
"statusCode": 409,
"statusMessage": "Source is still referenced by open alerts or incidents",
"data": {
"code": "imSourceReferencesExist",
"openAlertCount": 2,
"openIncidents": [{ "id": "42", "title": "Database connection pool exhausted" }]
}
}Ingest-Token rotieren
POST /api/im/sources/:id/rotate-token
Kein Request Body. Erzeugt einen neuen Ingest-Token. Der vorige Token gilt noch 7 Tage weiter (bis secondaryExpiresAt), so kannst du die Konfiguration beim Anbieter umstellen, ohne Alerts zu verlieren. Der neue Token steht nur in dieser Antwort. Bei einer E-Mail-Source lautet die neue Eingangsadresse <ingestToken>@alerts.uptimeify.io.
{
"ingestToken": "Vn8kT1qZ4xLb7RwE0mYc2sHa9uJd6fPg",
"secondaryExpiresAt": "2026-10-16T09:30:00.000Z"
}Optionale Absender-Authentifizierung
Der Token in der URL ist immer Pflicht. Zusätzlich kannst du eine Signatur oder einen Header verlangen:
- HMAC: Der Absender schickt
X-Uptimeify-Timestamp(Unix-Sekunden, höchstens 5 Minuten Abweichung) undX-Uptimeify-Signature, das hex-codierte HMAC-SHA256 über<timestamp>.<roher Body>mit dem Secret der Source. Ein Präfixsha256=wird akzeptiert. - Bearer: Der Absender schickt
Authorization: Bearer <token>.
Eine Anfrage, die eine dieser Prüfungen nicht besteht, wird genau wie ein unbekannter Token beantwortet (404). authMode.url_token wird gespeichert, hat aber keine Wirkung.
HMAC-Secret setzen
POST /api/im/sources/:id/set-hmac-secret
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
secret | string | Nein | Dein eigenes Secret, mindestens 16 Zeichen. Weglassen, um ein 256-Bit-Secret erzeugen zu lassen. |
Schaltet HMAC ein (authMode.hmac.enabled: true). Ein weiterer Aufruf ersetzt das Secret, das alte gilt sofort nicht mehr. Das Secret wird verschlüsselt gespeichert und nur in dieser Antwort zurückgegeben:
{ "secret": "c2VjcmV0LWV4YW1wbGUtbm90LXJlYWwtMTIzNDU2Nzg5MA", "hmacEnabled": true }Bearer-Token rotieren
POST /api/im/sources/:id/rotate-bearer-token
Kein Request Body. Erzeugt einen 256-Bit-Bearer-Token und schaltet Bearer-Authentifizierung ein. Ein vorheriger Bearer-Token gilt sofort nicht mehr. Gespeichert wird nur ein Hash, der Token steht nur in dieser Antwort:
{ "token": "b3V0LW9mLWJhbmQtZXhhbXBsZS10b2tlbi1ub3QtcmVhbA", "bearerEnabled": true }HMAC oder Bearer abschalten
POST /api/im/sources/:id/disable-auth
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
mode | string | Ja | hmac oder bearer. |
Setzt authMode.<mode>.enabled auf false und liefert das Source-Objekt. Das Secret bleibt gespeichert, daher schaltet PATCH mit { "authMode": { "hmac": { "enabled": true } } } es ohne neues Secret wieder ein. Einen schon abgeschalteten Modus abzuschalten gelingt ebenfalls.
Mapping testen
POST /api/im/sources/:id/test-mapping
Wendet das gespeicherte Mapping der Source auf eine Payload an, ohne etwas anzulegen. Reihenfolge der Payload: payload aus dem Body, sonst die zuletzt empfangene Payload der Source, sonst die Beispiel-Payload des Presets. Ein Mapping-Fehler ist kein HTTP-Fehler: Die Antwort ist 200 mit ok: false.
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
payload | object | Nein | Die JSON-Payload, die gemappt werden soll. |
curl -X POST "$BASE_URL/api/im/sources/7/test-mapping" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "payload": { "event_id": "6633487", "event_severity": "Disaster", "trigger_name": "Zabbix agent is not available on Zabbix server", "trigger_status": "PROBLEM", "host_name": "Zabbix server" } }'{
"ok": true,
"alert": {
"title": "Zabbix agent is not available on Zabbix server",
"severity": "sev1",
"status": "open",
"host": "Zabbix server",
"dedupKey": "6633487",
"custom": {}
},
"attributes": [
{ "key": "title", "standard": true, "value": "Zabbix agent is not available on Zabbix server", "resolved": true, "flags": {} },
{ "key": "severity", "standard": true, "value": "sev1", "resolved": true, "flags": {} },
{ "key": "status", "standard": true, "value": "PROBLEM", "resolved": true, "flags": {} },
{ "key": "host", "standard": true, "value": "Zabbix server", "resolved": true, "flags": {} },
{ "key": "correlationId", "standard": true, "value": "6633487", "resolved": true, "flags": { "grouped": true } }
]
}Bei einem Fehler: { "ok": false, "error": "title selector resolved to nothing on this payload" }.
alert.status ist nur dann resolved, wenn der gemappte Statuswert nach resolveValues gleich resolved ist (ohne Beachtung der Groß-/Kleinschreibung), alles andere ist open. Ohne dedupKey-Selektor ist der Dedup-Key der SHA-1 aus Titel plus Host.
Test-Alert auslösen
POST /api/im/sources/:id/test-alert
Stellt einen echten Alert in die normale Ingest-Pipeline. Meist öffnet das einen echten Incident und alarmiert, wer für das Team der Source On-Call ist. Payload: payload aus dem Body, sonst die Beispiel-Payload des Presets (kein Rückgriff auf zuletzt empfangene Payloads). Der Body ist wie bei der Ingest-URL auf 256 KB und 20 Verschachtelungsebenen begrenzt.
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
payload | beliebiges JSON | Nein | Die Payload, die eingespielt wird. Pflicht bei api- und email-Sources, die keine Beispiel-Payload haben. |
200 OK: { "enqueued": true }.
Letzte Payloads
GET /api/im/sources/:id/payloads
Bis zu 10 zuletzt empfangene Roh-Payloads der Source, neueste zuerst. Hilfreich beim Bauen eines Mappings. Sie liegen in einem kurzlebigen Cache, eine leere Liste ist bei einer ruhigen Source normal.
{ "payloads": [ { "event_id": "6633487", "trigger_status": "PROBLEM" } ] }Source-Statistik
GET /api/im/sources/:id/stats
Tageszähler der letzten 30 Tage mit Daten, älteste zuerst. day ist ein UTC-Kalendertag. alerts zählt eingehende Alerts, deduped die, die in einen schon offenen Alert eingeflossen sind, incidents die geöffneten Incidents.
{
"days": [
{ "day": "2026-10-07", "alerts": 41, "deduped": 33, "incidents": 3 },
{ "day": "2026-10-08", "alerts": 12, "deduped": 9, "incidents": 1 }
]
}Häufige Fehler
401 Unauthorized(unauthorized) wenn du nicht authentifiziert bist403 Forbidden(customerScopedTokenForbidden) bei einem kunden-gescopten Token403 Forbidden(imAccessDenied) wenn deine Session keine IM-berechtigte Rolle hat403 Forbidden(imNotEnabled) wenn Incident Management für die Organisation nicht aktiviert ist403 Forbidden(imTeamWriteDenied) bei einem Schreib-Endpunkt ohne Rolleadminoder Team-Admin-Mitgliedschaft400 Bad Request(invalidRequestBody) wenn:idkeine positive Ganzzahl ist,teamId/name/typefehlt oder ungültig ist, ein Mapping, eine Wartung odersnoozeUntilfehlerhaft ist,authMode.bearer.token_hashmitgeschickt wird,modenichthmac/bearerist oder ein mitgegebenes HMAC-Secret kürzer als 16 Zeichen ist404 Not Found(imSourceNotFound) wenn die Source nicht existiert oder zu einer anderen Organisation gehört422 Unprocessable Entity(invalidTeamId) wennteamIdkein Team der Organisation ist422 Unprocessable Entity(imAuthModeNotProvisionable) wenn HMAC oder Bearer per Anlegen/PATCHeingeschaltet wird, bevor ein Secret existiert409 Conflict(imSourceReferencesExist) beim Löschen einer Source mit offenen Alerts oder Incidents409 Conflict(imSourceProtected) beim Löschen der Monitoring-Brücke400 Bad Request(imSourceHasNoIngestToken) beim Rotieren des Tokens einerapi-Source400 Bad Request(imNoTestPayloadAvailable) wenn test-mapping oder test-alert keine Payload zur Verfügung hat413 Payload Too Large(payloadTooLarge),422 Unprocessable Entity(invalidJson,payloadTooDeep) bei test-alert503 Service Unavailable(unavailable) wenn test-alert den Alert nicht einreihen kann, sicher wiederholbar
Incident bestätigen / Status ändern
Bestätigt einen Incident-Management-Incident oder bewegt ihn zwischen den offenen Status (acknowledged, investigating, identified, monitoring).
Analytics
Incident-Volumen, MTTA und MTTR pro Tag, das Verhältnis von Alerts zu Incidents und die On-Call-Last pro Person für einen Zeitraum, in einem Aufruf.