Uptimeify Docs

Meldefälle (NIS-2)

Einen NIS-2-Meldefall führen: Kriterien, Entscheidung zur Meldepflicht, Zeitpunkt der Kenntnis, Fristen, Berichtsentwürfe und das Protokoll Ihrer Abgaben im BSI-Portal.

GET /api/im/report-cases · POST /api/im/report-cases · GET /api/im/report-cases/:id · PATCH /api/im/report-cases/:id

POST /api/im/report-cases/:id/aware-at · POST /api/im/report-cases/:id/criteria · POST /api/im/report-cases/:id/decision · POST /api/im/report-cases/:id/interim-request · POST /api/im/report-cases/:id/close

POST /api/im/report-cases/:id/incidents · DELETE /api/im/report-cases/:id/incidents/:incidentId · GET /api/im/report-cases/:id/draft · POST /api/im/report-cases/:id/submissions

Ein Meldefall sammelt, was Sie für eine NIS-2-Meldung brauchen (Art. 23 NIS-2-Richtlinie, § 32 BSIG): die verknüpften Vorfälle, je Erheblichkeitskriterium eine Entscheidung, die Entscheidung zur Meldepflicht, den Zeitpunkt der Kenntnis und die Fristen daraus (Frühwarnung 24 h, Meldung 72 h, Abschlussmeldung einen Monat nach der Meldung, Zwischenmeldung auf Anfrage). Jede Meldung geben Sie selbst im BSI-Portal ab. Uptimeify übermittelt nichts und stuft keinen Vorfall selbst ein. Kriterien, Abgaben und Zeitachse sind nur anfügbar: nachträglich lässt sich nichts ändern oder löschen. Geschlossene Fälle werden drei Jahre aufbewahrt.

:id ist die publicId des Falls oder seine numerische id. :incidentId ist die numerische ID eines IM-Vorfalls.

Authentifizierung

Nur mit Sitzung (Dashboard-Anmeldung); API-Tokens werden mit 403 (nis2OfficerRequired) abgewiesen. NIS-2-Meldefälle müssen eingeschaltet sein (NIS-2-Einstellungen). Organisations-Admins und eingetragene Meldeverantwortliche dürfen lesen und schreiben, alle anderen bekommen 403. Ein Fall einer anderen Organisation antwortet 404 (reportCaseNotFound), genau wie ein fehlender.

Meldefälle auflisten

GET /api/im/report-cases

QueryBeschreibung
statusKommagetrennter Statusfilter, z. B. reportable,early_warning_submitted.
pageStandard 1.
perPageStandard 50, höchstens 200.

Sortiert nach der nächsten offenen Frist; Fälle ohne Frist stehen am Ende, neueste zuerst.

curl "$BASE_URL/api/im/report-cases?status=reportable&perPage=20" -H "Cookie: $SESSION_COOKIE"

200 OK

{
  "items": [
    {
      "id": 12,
      "publicId": "0b6f9a52-7a43-4c8e-9f1e-5c2d8a1b3e77",
      "title": "Ausfall Kundenportal",
      "status": "reportable",
      "awareAt": "2026-10-21T07:00:00.000Z",
      "createdAt": "2026-10-21T07:05:00.000Z",
      "nextDeadline": { "stage": "early_warning", "dueAt": "2026-10-22T07:00:00.000Z", "met": false },
      "incidentCount": 1
    }
  ],
  "total": 1,
  "page": 1,
  "perPage": 20
}

nextDeadline ist { stage, dueAt, met } oder null.

Statuswerte: assessment, not_reportable, reportable, early_warning_submitted, notification_submitted, final_submitted, closed.

Meldefall öffnen

POST /api/im/report-cases

FeldTypPflichtBeschreibung
titlestringJa1 bis 200 Zeichen.
incidentIdsinteger[]NeinIM-Vorfälle Ihrer Organisation, höchstens 50, keine Test-Vorfälle.

awareAt beginnt mit dem Zeitpunkt der Anfrage; korrigieren mit aware-at. Jeder andere Schlüssel ist ein 400.

curl -X POST "$BASE_URL/api/im/report-cases" -H "Cookie: $SESSION_COOKIE" -H "Content-Type: application/json" \
  -d '{ "title": "Ausfall Kundenportal", "incidentIds": [4812] }'

201 Created: der ganze Fall, gleiche Form wie Meldefall lesen.

Meldefall lesen

GET /api/im/report-cases/:id

200 OK

{
  "id": 12,
  "publicId": "0b6f9a52-7a43-4c8e-9f1e-5c2d8a1b3e77",
  "organizationId": 2,
  "title": "Ausfall Kundenportal",
  "status": "reportable",
  "awareAt": "2026-10-21T07:00:00.000Z",
  "decision": "reportable",
  "decidedBy": "u_8f2c",
  "decidedAt": "2026-10-21T08:10:00.000Z",
  "decisionReason": null,
  "suspectedMalicious": true,
  "crossBorder": false,
  "severityAssessment": "high",
  "impact": "Kundenportal nicht erreichbar",
  "affectedUsers": null,
  "iocs": "203.0.113.7",
  "rootCause": null,
  "mitigation": null,
  "kritisFacility": null,
  "criticalService": null,
  "interimRequestedAt": null,
  "interimDueAt": null,
  "createdBy": "u_8f2c",
  "createdAt": "2026-10-21T07:05:00.000Z",
  "closedAt": null,
  "canDecide": true,
  "nextDeadline": { "stage": "early_warning", "dueAt": "2026-10-22T07:00:00.000Z", "met": false },
  "deadlines": [
    { "stage": "early_warning", "dueAt": "2026-10-22T07:00:00.000Z", "met": false },
    { "stage": "notification", "dueAt": "2026-10-24T07:00:00.000Z", "met": false }
  ],
  "incidents": [
    { "id": 4812, "title": "Portal weg", "severity": "sev1", "status": "resolved", "triggeredAt": "2026-10-21T06:40:00.000Z", "resolvedAt": "2026-10-21T07:27:00.000Z" }
  ],
  "criteria": [
    { "key": "total_outage_over_30min", "suggestible": true, "suggestedValue": true, "suggestionSource": "monitoring:47min", "decision": "confirmed", "value": true, "decidedBy": "u_8f2c", "decidedAt": "2026-10-21T08:05:00.000Z" }
  ],
  "submissions": [],
  "events": [
    { "id": 301, "type": "decided", "actorUserId": "u_8f2c", "payload": { "decision": "reportable", "reason": null, "previous": null }, "createdAt": "2026-10-21T08:10:00.000Z" }
  ]
}

Felder ohne Wert sind null, nie weggelassen.

criteria enthält immer alle zehn Kriterien (hier gekürzt): total_outage_over_30min, degraded_over_1h_5pct_users, financial_loss_500k_or_5pct, trade_secret_exfiltration, death_or_health_damage, malicious_unauthorized_access, iva_compromised_malicious, iva_compromised_5pct_users, third_party_damage, planned_maintenance. Nur das erste und das letzte bekommen einen Vorschlag aus den Monitoring-Daten; ein Vorschlag ist nie eine Entscheidung. deadlines bleibt leer, bis der Fall als reportable entschieden ist. events ist die nur anfügbare Zeitachse, neueste zuerst.

Inhaltsfelder ändern

PATCH /api/im/report-cases/:id

Merge-Patch über die Inhaltsfelder: suspectedMalicious, crossBorder (boolean oder null), severityAssessment, impact, affectedUsers, iocs, rootCause, mitigation, kritisFacility, criticalService (Text bis 10000 Zeichen oder null). Nicht genannte Felder bleiben, null leert, leerer Text wird null. Jeder andere Schlüssel ist ein 400. 200 OK: der ganze Fall.

Aktionen am Fall

Jede antwortet 200 OK mit dem ganzen Fall. Jeder andere Schlüssel im Body ist ein 400.

AktionBodyRegel
POST /api/im/report-cases/:id/aware-atawareAt (ISO-Zeitpunkt, Pflicht), reason (Text oder null)Früher ist frei; später braucht eine reason (reportCaseAwareAtReasonRequired).
POST /api/im/report-cases/:id/criteriakey (eines der zehn), decision (confirmed | rejected), value (boolean), alle PflichtFügt eine neue Entscheidung an; die jüngste gilt.
POST /api/im/report-cases/:id/decisiondecision (reportable | not_reportable, Pflicht), reasonErst wenn jedes Kriterium entschieden ist (reportCaseCriteriaOpen). not_reportable braucht eine reason und geht nach einer Abgabe nicht mehr.
POST /api/im/report-cases/:id/interim-requestrequestedAt, dueAt (ISO-Zeitpunkte, Pflicht)Das BSI hat eine Zwischenmeldung angefordert; dueAt muss nach requestedAt liegen.
POST /api/im/report-cases/:id/closekeinerErst nach der Abschlussmeldung oder bei not_reportable (reportCaseCannotClose). Ein geschlossener Fall nimmt keine Änderung mehr an.
curl -X POST "$BASE_URL/api/im/report-cases/0b6f9a52-7a43-4c8e-9f1e-5c2d8a1b3e77/decision" \
  -H "Cookie: $SESSION_COOKIE" -H "Content-Type: application/json" -d '{ "decision": "reportable" }'

Vorfälle verknüpfen und lösen

POST /api/im/report-cases/:id/incidents mit { "incidentId": 4813 } antwortet 201 Created:

{ "reportCaseId": 12, "reportCasePublicId": "0b6f9a52-7a43-4c8e-9f1e-5c2d8a1b3e77", "incidentId": 4813, "linkedAt": "2026-10-21T09:00:00.000Z" }

DELETE /api/im/report-cases/:id/incidents/:incidentId antwortet 204 No Content. Ein zweites DELETE antwortet 404 (reportCaseIncidentNotLinked). Verknüpfen und Lösen aktualisieren die Vorschläge aus dem Monitoring.

Berichtsentwurf

GET /api/im/report-cases/:id/draft?stage=early_warning&locale=de

stage (Pflicht): early_warning, notification, interim, progress oder final. locale: de (Standard) oder en. Fehlende Pflichtangaben erscheinen als [fehlt] / [missing].

200 OK

{
  "text": "# Frühwarnung (≤ 24 h)\n…",
  "draftToken": "v1.1761058800000.de.Qk9…"
}

text ist Markdown zum Kopieren ins BSI-Portal. draftToken ist die Signatur des Servers über genau diesen Text, diesen Fall, diese Stufe und diese Sprache. Behandeln Sie ihn als undurchsichtig, bewahren Sie ihn zusammen mit dem Text auf und schicken Sie beides beim Protokollieren der Abgabe zurück. Er gilt 90 Tage.

Abgabe protokollieren

POST /api/im/report-cases/:id/submissions

Der Ablauf: Entwurf kopieren (text und draftToken), text im BSI-Portal abgeben, danach hier die Abgabe mit demselben text und draftToken protokollieren. Der Server friert genau diesen Text ein. Weil der Token beweist, dass der Server ihn erzeugt hat, ist der eingefrorene Text zugleich das, was Sie wirklich abgegeben haben, und nichts, was Sie hätten erfinden können. Ein späteres Bearbeiten des Falls ändert den Beleg nicht: ein späterer Entwurf ist ein neuer Text mit neuem Token.

FeldTypPflichtBeschreibung
stagestringJaearly_warning, notification, interim, progress oder final. Muss die Stufe des Entwurfs sein.
submittedAtstring (ISO-Zeitpunkt)JaWann Sie im BSI-Portal abgegeben haben; nicht in der Zukunft.
referencestringJaAktenzeichen aus dem BSI-Portal, 1 bis 200 Zeichen.
textstringJaDer Entwurfstext genau so, wie GET .../draft ihn geliefert hat, bis 100000 Zeichen. Kein Zeichen darf abweichen.
draftTokenstringJaDer draftToken, der mit diesem Text kam. Die Sprache steckt im Token.

Gehören Text und Token nicht zusammen (anderer Text, Fall oder andere Stufe, oder der Token ist älter als 90 Tage), antwortet der Server 400 (reportCaseDraftInvalid): Entwurf neu holen und diesen abgeben. Nur bei Fällen, die als reportable entschieden sind. Die Stufen folgen der Reihenfolge des Gesetzes: einmal early_warning, dann notification (zählt auch als Frühwarnung, wenn keine erfasst ist), danach beliebig viele interim und progress, zuletzt final. Jede andere Reihenfolge antwortet 409 (reportCaseInvalidTransition). Jeder andere Schlüssel ist ein 400.

curl -X POST "$BASE_URL/api/im/report-cases/0b6f9a52-7a43-4c8e-9f1e-5c2d8a1b3e77/submissions" \
  -H "Cookie: $SESSION_COOKIE" -H "Content-Type: application/json" \
  -d '{ "stage": "early_warning", "submittedAt": "2026-10-21T15:00:00Z", "reference": "BSI-2026-4711", "text": "# Frühwarnung (≤ 24 h)\n…", "draftToken": "v1.1761058800000.de.Qk9…" }'

201 Created

{ "id": 41, "stage": "early_warning", "submittedAt": "2026-10-21T15:00:00.000Z", "submittedBy": "u_8f2c", "reference": "BSI-2026-4711", "frozenText": "# Frühwarnung (≤ 24 h)\n…", "createdAt": "2026-10-21T15:02:00.000Z" }

Häufige Fehler

  • 401 Unauthorized (unauthorized) ohne Anmeldung
  • 403 Forbidden (imAccessDenied) für einen Kunden-Login oder eine Rolle ohne Incident-Management-Zugang
  • 403 Forbidden (nis2NotEnabled), wenn Meldefälle ausgeschaltet sind
  • 403 Forbidden (nis2OfficerRequired) für ein API-Token oder eine Person, die weder Admin noch Meldeverantwortliche ist
  • 400 Bad Request (invalidIdentifier), wenn :id weder Zahl noch UUID ist
  • 404 Not Found (reportCaseNotFound), wenn der Fall nicht existiert oder einer anderen Organisation gehört
  • 400 Bad Request (invalidRequestBody) bei fehlendem Feld, falschem Typ, unbekanntem Schlüssel, leerer reference, ungültigem Query-Wert oder dueAt nicht nach requestedAt
  • 404 Not Found (imIncidentNotFound), wenn ein Vorfall nicht existiert oder einer anderen Organisation gehört
  • 400 Bad Request (reportCaseTestIncident), (reportCaseReasonRequired), (reportCaseAwareAtReasonRequired), (reportCaseSubmittedInFuture), (reportCaseDraftInvalid)
  • 409 Conflict (reportCaseClosed), (reportCaseCriteriaOpen), (reportCaseAlreadySubmitted), (reportCaseNotReportable), (reportCaseCannotClose), (reportCaseIncidentAlreadyLinked), (reportCaseInvalidTransition)
  • 404 Not Found (reportCaseIncidentNotLinked) beim Lösen eines Vorfalls, der nicht verknüpft ist

Auf dieser Seite