[
  {
    "id": "index",
    "path": "/de",
    "area": "",
    "areaTitle": "Entwickeln mit Szenera",
    "page": "index",
    "pageTitle": "Entwickeln mit Szenera",
    "section": "Entwickeln mit Szenera",
    "anchor": "",
    "text": "API, CLI, Apps-SDK und Webhooks von Szenera: Schlüssel erzeugen, Aufrufe absetzen, Ereignisse empfangen — und die Regeln, die auf jeder Route gelten."
  },
  {
    "id": "index#was-es-gibt",
    "path": "/de",
    "area": "",
    "areaTitle": "Entwickeln mit Szenera",
    "page": "index",
    "pageTitle": "Entwickeln mit Szenera",
    "section": "Was es gibt",
    "anchor": "was-es-gibt",
    "text": "API. Die HTTP-API unter /v1 ist die Grundlage von allem: OpenAPI 3.1, 19 Tags in sieben Gruppen, die Referenz unter /api/reference und die Spezifikation unter /api/openapi.json . Daneben stehen die Wege, die ein fremder Standard vorgibt: der delegierte Zugang über OAuth 2.1 und die Provisionierung über SCIM. CLI. Die Kommandozeile szenera bündelt 136 Befehle für Organisationen mit JSON-Ausgabe und Exit-Codes; jeder Befehl ist ein API-Aufruf, und einen öffentlichen Bezugsweg gibt es heute nicht — wer den Quellbaum der Plattform hat, baut sie daraus. Apps-SDK. Das SDK legt fest, wie eine App gebaut ist — Manifest, Widget, Server-Teil, Signatur — und wie sie in den Katalog kommt; es ist heute nicht veröffentlicht, die Seiten beschreiben den Vertrag, wie er im Quellbaum der Plattform gilt. Webhooks. Webhooks tragen Ereignisse Ihrer Organisation an Ihre Systeme: Endpunkt anlegen, HMAC-Signatur prüfen, Wiederholungen verstehen, den Ereigniskatalog abrufen. Änderungen. Was an der API stabil ist, wo Änderungen erscheinen und wie Sie die Spezifikation gegen Ihren Stand vergleichen."
  },
  {
    "id": "index#tldr-in-fünf-befehlen",
    "path": "/de",
    "area": "",
    "areaTitle": "Entwickeln mit Szenera",
    "page": "index",
    "pageTitle": "Entwickeln mit Szenera",
    "section": "tl;dr in fünf Befehlen",
    "anchor": "tldr-in-fünf-befehlen",
    "text": "Die CLI wird heute nicht als eigenständiges Paket ausgeliefert; wie Sie sie aus dem Quellbaum bauen, steht unter Die CLI beziehen . Jeder Befehl ist ein API-Aufruf, den die Seite Der erste Aufruf mit curl auch mit curl zeigt."
  },
  {
    "id": "index#anfangen",
    "path": "/de",
    "area": "",
    "areaTitle": "Entwickeln mit Szenera",
    "page": "index",
    "pageTitle": "Entwickeln mit Szenera",
    "section": "Anfangen",
    "anchor": "anfangen",
    "text": "Bereich Was Sie dort finden Erste Schritte Vom API-Schlüssel zum ersten Aufruf: Schlüssel erzeugen, mit curl anfragen, Antworten lesen und Wiederholungen von Anfang an absichern."
  },
  {
    "id": "index#schnittstellen",
    "path": "/de",
    "area": "",
    "areaTitle": "Entwickeln mit Szenera",
    "page": "index",
    "pageTitle": "Entwickeln mit Szenera",
    "section": "Schnittstellen",
    "anchor": "schnittstellen",
    "text": "Bereich Was Sie dort finden API Die Regeln, die auf jeder Route gelten — Adresse, Anmeldung, Fehler, Sperren, Blättern, Stabilität —, die Zugänge für fremde Anwendungen, Verzeichnisdienste und Assistenten und der Wegweiser durch die Referenz. CLI Die Kommandozeile szenera: Profile, Ausweis, 136 Befehle, JSON-Ausgabe und Exit-Codes — und was heute beim Bezug gilt. Webhooks Ereignisse Ihrer Organisation an Ihre Systeme: Endpunkt anlegen, Signatur prüfen, Wiederholungen verstehen, Ereignistypen nachschlagen und den Katalog abrufen."
  },
  {
    "id": "index#erweitern",
    "path": "/de",
    "area": "",
    "areaTitle": "Entwickeln mit Szenera",
    "page": "index",
    "pageTitle": "Entwickeln mit Szenera",
    "section": "Erweitern",
    "anchor": "erweitern",
    "text": "Bereich Was Sie dort finden Apps-SDK Wie eine App gebaut ist — Manifest, Widget, Server-Teil, Datenzugriff, Signatur — und wie sie in den Katalog und auf den Bildschirm kommt. Der ganze Weg einer neuen App steht unter Von der Vorlage bis zur Freigabe ."
  },
  {
    "id": "index#verfolgen",
    "path": "/de",
    "area": "",
    "areaTitle": "Entwickeln mit Szenera",
    "page": "index",
    "pageTitle": "Entwickeln mit Szenera",
    "section": "Verfolgen",
    "anchor": "verfolgen",
    "text": "Bereich Was Sie dort finden Änderungen Wo Änderungen an der API entstehen, was ein Bruch ist und wie Sie die Spezifikation gegen Ihren Stand vergleichen — ohne Versprechen, die es nicht gibt."
  },
  {
    "id": "getting-started/index",
    "path": "/de/getting-started",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "index",
    "pageTitle": "Erste Schritte",
    "section": "Erste Schritte",
    "anchor": "",
    "text": "Vom API-Schlüssel zum ersten Aufruf: Schlüssel erzeugen, mit curl anfragen, Antworten lesen und Wiederholungen von Anfang an absichern. Die Portalseite Einstellungen › Organisation › API-Schlüssel: links die Bereichsliste, rechts die Schaltfläche Schlüssel erstellen und die Tabelle der ausgestellten Schlüssel mit Name und Rechten, Klasse, Ersteller, Erstellungszeitpunkt, Gültigkeit und letzter Benutzung Hier beginnt jede Anbindung: Der Schlüssel entsteht im Portal, alles Weitere läuft über die API."
  },
  {
    "id": "getting-started/index#orientierung",
    "path": "/de/getting-started",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "index",
    "pageTitle": "Erste Schritte",
    "section": "Orientierung",
    "anchor": "orientierung",
    "text": "Seite Wann Sie es brauchen Wie läuft ein Aufruf durch die Plattform? Von der Bearer-Kopfzeile über die Rechteprüfung bis zur Antwort mit Trace-Kennung: die Stationen, die jeder Aufruf durchläuft."
  },
  {
    "id": "getting-started/index#der-erste-aufruf",
    "path": "/de/getting-started",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "index",
    "pageTitle": "Erste Schritte",
    "section": "Der erste Aufruf",
    "anchor": "der-erste-aufruf",
    "text": "Seite Wann Sie es brauchen Einen API-Schlüssel erzeugen Im Portal unter Einstellungen › Organisation › API-Schlüssel einen Schlüssel mit Klasse, Rechten und Ablauf anlegen — der Klartext erscheint genau einmal. Der erste Aufruf mit curl Mit Schlüssel und curl GET /v1/me und GET /v1/playlists aufrufen, die Antwort und ihre Kopfzeilen ansehen — in fünf Minuten. Was eine Antwort enthält Status, Kopfzeilen, Listenform, Kennungen mit Präfix und der Fehlerrumpf — alles, was in einer Antwort der API steckt, in Tabellen. Idempotenz von Anfang an Verändernde Aufrufe mit Idempotency-Key absichern, damit eine Wiederholung nach Netzabbruch keinen zweiten Vorgang erzeugt."
  },
  {
    "id": "getting-started/index#wenn-es-hakt",
    "path": "/de/getting-started",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "index",
    "pageTitle": "Erste Schritte",
    "section": "Wenn es hakt",
    "anchor": "wenn-es-hakt",
    "text": "Seite Wann Sie es brauchen Wenn der erste Aufruf scheitert 401, 403, 404, 422, 429 und Netzfehler beim ersten Aufruf: was der Code bedeutet und welche Seite den Weg zurück zeigt."
  },
  {
    "id": "getting-started/about-request-flow",
    "path": "/de/getting-started/about-request-flow",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "about-request-flow",
    "pageTitle": "Wie läuft ein Aufruf durch die Plattform?",
    "section": "Wie läuft ein Aufruf durch die Plattform?",
    "anchor": "",
    "text": "Jeder Aufruf durchläuft dieselben drei Stationen: Die Plattform erkennt den Ausweis, wählt die Operation unter /v1 und prüft deren Recht, dann antwortet sie mit JSON und den Kopfzeilen, die überall gelten. Wer diese Stationen kennt, liest jede Fehlerantwort auf den ersten Blick."
  },
  {
    "id": "getting-started/about-request-flow#station-1-der-ausweis-in-der-bearer-kopfzeile",
    "path": "/de/getting-started/about-request-flow",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "about-request-flow",
    "pageTitle": "Wie läuft ein Aufruf durch die Plattform?",
    "section": "Station 1: Der Ausweis in der Bearer-Kopfzeile",
    "anchor": "station-1-der-ausweis-in-der-bearer-kopfzeile",
    "text": "Die Plattform erkennt am Präfix des Werts in Authorization: Bearer … , welche Art Ausweis vorliegt — ohne weitere Angabe von Ihnen: Präfix Ausweis Für wen ak_ API-Schlüssel einer Organisation Maschinen: Skripte, Dienste, Ihre Anbindung eyJ Sitzungstoken aus POST /v1/auth/login Menschen im Portal; 15 Minuten gültig, danach Auffrischung oat_ Zugriffstoken eines delegierten Zugangs eine fremde Anwendung im Namen eines Menschen opk_ Schlüssel des Plattformbetriebs nur der Betreiber der Plattform rdr_ Renderer-Token der Abspielprozess eines gekoppelten Geräts Am Rand begegnen Ihnen sct_ (ein Verzeichnisdienst unter /scim/v2 , sonst nirgends), inv_ (Einladung vor der ersten Anmeldung) und dev_ (Erst-Registrierung eines Geräts); Geräte im Betrieb weisen sich mit ihrem Zertifikat aus. Für Ihre Anbindung ist ak_… der Regelweg: kein Ablauf nach einer Viertelstunde, keine Auffrischung. Ein Wert mit unbekanntem Präfix gilt ausdrücklich nicht als anonym: Er wird als unbrauchbarer Ausweis mit 401 Auth.Required abgewiesen — ein Tippfehler im Präfix wird so zu einer 401, nicht zu einer irreführenden 404. Dieselbe Antwort bekommt ein Aufruf ohne Kopfzeile; ein Schlüssel mit richtigem Präfix, aber abgeschnittenem Wert bekommt 401 Auth.TokenMalformed ."
  },
  {
    "id": "getting-started/about-request-flow#station-2-route-und-rechteprüfung",
    "path": "/de/getting-started/about-request-flow",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "about-request-flow",
    "pageTitle": "Wie läuft ein Aufruf durch die Plattform?",
    "section": "Station 2: Route und Rechteprüfung",
    "anchor": "station-2-route-und-rechteprüfung",
    "text": "Jede Route, über die Szenera selbst entscheidet, liegt unter /v1 , und der Pfad ist Teil des Vertrags; nur wo ein fremder Standard seine Adresse vorschreibt, fehlt das Präfix ( /oauth/… , /.well-known/… , /scim/v2/… ). Dann prüft die Plattform je Operation das Recht, das die Referenz bei ihr nennt (zum Beispiel playlist.list für GET /v1/playlists ). Ein API-Schlüssel trägt die feste Liste von Rechten aus dem Anlegen; Geräte, das Koppeln und Löschen von Displays und die Schlüsselverwaltung bleiben Benutzerkonten vorbehalten. Fehlt das Recht: 403 Permission.Denied . Routen des Plattformbetriebs antworten einem Schlüssel einer Organisation mit 403 Permission.OperatorOnly . Ein Aufruf läuft vom Client über Ausweis-Erkennung, Route und Rechteprüfung zur Antwort; unbekannter Ausweis und fehlendes Recht zweigen in den Fehlerrumpf ab Der Fehlerzweig trägt denselben Rumpf wie jeder Fehler: code, message, traceId."
  },
  {
    "id": "getting-started/about-request-flow#station-3-die-antwort-und-ihre-kopfzeilen",
    "path": "/de/getting-started/about-request-flow",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "about-request-flow",
    "pageTitle": "Wie läuft ein Aufruf durch die Plattform?",
    "section": "Station 3: Die Antwort und ihre Kopfzeilen",
    "anchor": "station-3-die-antwort-und-ihre-kopfzeilen",
    "text": "Die Plattform spricht JSON. Vier Kopfzeilen prägen den Austausch — drei in der Antwort, eine in der Anfrage — überall dort, wo ihre Bedingung erfüllt ist: Kopfzeile Wann X-Trace-Id immer — die Spur-Kennung dieses Aufrufs If-Match (Anfrage) bei Änderungen versionierter Ressourcen (Szenen, Playlisten, Programme, Rollen, Displays): die gelesene version geht als If-Match mit, dieselbe Zahl steht als ETag in der Antwort Idempotent-Replay: true wenn eine Wiederholung mit demselben Idempotency-Key die gespeicherte Antwort erhielt X-RateLimit-Limit , X-RateLimit-Remaining , X-RateLimit-Reset nur auf gedrosselten Routen — vor allem Anmeldung, Registrierung, Passwort-Zurücksetzen Ein Fehler kommt immer im selben Rumpf: code in der Form Bereich.Ursache , message als Kurztext und traceId mit dem Präfix trc_ . Der HTTP-Status folgt dem Bereich des Codes: Auth.* → 401, Permission.* → 403, *.NotFound → 404, Conflict.* → 409, Validation.* → 422, RateLimit.* → 429."
  },
  {
    "id": "getting-started/about-request-flow#was-daraus-folgt",
    "path": "/de/getting-started/about-request-flow",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "about-request-flow",
    "pageTitle": "Wie läuft ein Aufruf durch die Plattform?",
    "section": "Was daraus folgt",
    "anchor": "was-daraus-folgt",
    "text": "Schlüssel statt Sitzung für Maschinen. Ein Sitzungstoken läuft nach 15 Minuten ab; ein Schlüssel ak_… nicht. Schalten Sie auf code, nie auf message. Der Code ist stabil und Teil des Vertrags; der Text darf sich ändern. Protokollieren Sie die Trace-Kennung. X-Trace-Id und traceId sind derselbe Wert — das, was Sie bei einer Rückfrage nennen. Geben Sie dem Schlüssel nur die Rechte, die Ihre Anbindung braucht. Ein zu enger Schlüssel fällt an einer 403 auf, ein zu weiter an gar nichts."
  },
  {
    "id": "getting-started/about-request-flow#weiterführendes",
    "path": "/de/getting-started/about-request-flow",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "about-request-flow",
    "pageTitle": "Wie läuft ein Aufruf durch die Plattform?",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Einen API-Schlüssel erzeugen Anmeldung Fehlerformat"
  },
  {
    "id": "getting-started/create-api-key",
    "path": "/de/getting-started/create-api-key",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "create-api-key",
    "pageTitle": "Einen API-Schlüssel erzeugen",
    "section": "Einen API-Schlüssel erzeugen",
    "anchor": "",
    "text": "Nach dieser Anleitung besitzen Sie einen Schlüssel ak_… mit genau den Rechten, die Ihre Anbindung braucht, und einem Ablaufdatum. Der Klartext erscheint genau einmal — beim Erstellen."
  },
  {
    "id": "getting-started/create-api-key#schritte",
    "path": "/de/getting-started/create-api-key",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "create-api-key",
    "pageTitle": "Einen API-Schlüssel erzeugen",
    "section": "Schritte",
    "anchor": "schritte",
    "text": "Öffnen Sie Einstellungen › Organisation › API-Schlüssel und klicken Sie auf Schlüssel erstellen . Der Dialog API-Schlüssel erstellen öffnet sich: „Legen Sie Name und Rechte fest. Der Klartext-Schlüssel erscheint danach genau einmal.“ Tragen Sie unter Name (1) ein, wofür der Schlüssel da ist — das Portal schlägt „CI-Import“ als Beispiel vor. Der Name steht später in der Liste und in Protokollen. Wählen Sie unter Rechte (2) eine Klasse — vorgewählt ist Nur Lesen : Klasse Was der Schlüssel darf Nur Lesen Inhalte, Medien, Zeitpläne, Standorte, Displays, Werbung und Nachweise abrufen — nichts verändern. Medien-Automation Medien hochladen und Ordner verwalten. Inhalte-Pflege Szenen und Vorlagen anlegen und ändern. Ausspielsteuerung Playlisten und Zeitpläne verwalten. Vollzugriff Alles, was Ihr Konto über die API darf. Individuell Rechte einzeln, mit Erklärung je Recht. Unter den Karten steht, wie viele Rechte die Wahl enthält — bei Nur Lesen mit den vollen Rechten der Rolle Inhaber „Enthält 19 Rechte.“; die Zahl ist auf Ihre eigenen Rechte beschnitten. Mit Einzelne Rechte anpassen (3) öffnen Sie die Auswahl aus dem Rechte-Katalog ( Recht suchen … , Recht hinzufügen ); bei Individuell ist sie sofort offen. Wählen Sie unter Gültig bis (4) die Laufzeit: In 30 Tagen , In 90 Tagen (Vorgabe), In einem Jahr oder Unbefristet . Unbefristete Schlüssel markiert die Liste eigens. Wählen Sie unter Alter Schlüssel gültig bis (5), wie lange bei einer späteren Rotation der alte Wert neben dem neue"
  },
  {
    "id": "getting-started/create-api-key#optionen",
    "path": "/de/getting-started/create-api-key",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "create-api-key",
    "pageTitle": "Einen API-Schlüssel erzeugen",
    "section": "Optionen",
    "anchor": "optionen",
    "text": "Rotieren. Über Aktionen › Rotieren öffnet sich Schlüssel rotieren : Der Schlüssel „bekommt einen Nachfolger mit denselben Rechten. Der neue Schlüssel wird Ihnen einmalig angezeigt.“ Sie wählen, wie lange der alte daneben gilt ( Sofort ungültig bis 3 Tage ); die Meldung „Schlüssel rotiert — der alte gilt noch bis …“ nennt den Zeitpunkt. Widerrufen. Über Aktionen › Widerrufen und die Rückfrage Schlüssel widerrufen? wird der Schlüssel sofort und endgültig ungültig; laufende Integrationen verlieren den Zugang. Über die API. POST /v1/me/api-keys legt denselben Schlüssel an (Recht identity.api-key.create ). Ein Schlüssel kann keinen weiteren ausstellen; die Route erwartet ein Sitzungstoken ( eyJ… ) aus POST /v1/auth/login . Rumpf: name , scope (Liste der Rechte, mindestens eines), expiresAt (Pflicht; null heißt unbefristet), optional originClass (nur die Herkunft zur Anzeige — die Rechte selbst müssen in scope stehen; Vorgabe custom ) und rotationGraceHours (0–72, Vorgabe 24); die Antwort 201 trägt den Klartext einmalig unter plainKey . Felder und Lebenszyklus: Anmeldung: Schlüssel und Sitzungen . Über die Kommandozeile. szenera api-key issue --name=\"Mediathek-Roboter\" --permissions=asset.list,asset.read ; --list-allowed zeigt die freigegebenen Rechte. Der Befehl braucht ein Profil mit Kennwort-Anmeldung, kein Schlüssel-Profil (siehe Profile und Ausweis ); alle Flaggen: Befehle für Ihre Organisation ."
  },
  {
    "id": "getting-started/create-api-key#ergebnis-prüfen",
    "path": "/de/getting-started/create-api-key",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "create-api-key",
    "pageTitle": "Einen API-Schlüssel erzeugen",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "Der Aufruf GET /v1/me mit der Kopfzeile Authorization: Bearer ak_… antwortet mit 200 und Ihrem Konto (siehe Der erste Aufruf mit curl ). Die Liste unter Einstellungen › Organisation › API-Schlüssel zeigt den neuen Eintrag mit Name / Rechte , Klasse , Erstellt von , Gültig bis und Zuletzt benutzt — zuerst „Nie benutzt“, nach dem ersten Aufruf mit Zeitpunkt. GET /v1/me/api-keys liefert Ihre eigenen Schlüssel — auch mit dem neuen Schlüssel selbst aufgerufen: die Kennung akm_… und den Präfix ak_ plus neun Zeichen, nie den Klartext. Alle Schlüssel der Organisation zeigt GET /v1/api-keys (Recht identity.api-key.list )."
  },
  {
    "id": "getting-started/create-api-key#wenn-etwas-nicht-klappt",
    "path": "/de/getting-started/create-api-key",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "create-api-key",
    "pageTitle": "Einen API-Schlüssel erzeugen",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung Schlüssel erstellen ist gesperrt; darüber steht „Bestehende Schlüssel arbeiten weiter und lassen sich jederzeit widerrufen. Neue auszustellen oder zu erneuern, geht ab dem Paket …“ Das Paket der Organisation enthält den API-Zugang nicht. Paket ab Premium wählen oder die Freischaltung vertraglich vereinbaren; bestehende Schlüssel arbeiten weiter. Die API antwortet auf POST /v1/me/api-keys mit 402 Billing.AddonRequired Dasselbe über die API: Die Fähigkeit API-Zugang fehlt. wie oben Eine Klasse enthält weniger Rechte als erwartet Die Klasse ist auf Ihre eigenen Rechte beschnitten. Ein Konto mit den fehlenden Rechten legt den Schlüssel an, oder Ihre Rolle wird erweitert. 403 Permission.Denied auf POST /v1/me/api-keys mit einem Schlüssel ak_… Ein Schlüssel kann keinen Schlüssel ausstellen. Sitzungstoken verwenden oder den Weg über das Portal nehmen. 403 ApiKey.ScopeEscalationBlocked Der angeforderte Umfang übersteigt Ihre eigenen Rechte. Umfang verkleinern. 422 ApiKey.ScopeNotKeyCapable Ein gewähltes Recht ist für Schlüssel nicht vorgesehen; details nennt es. Recht entfernen; szenera api-key issue --list-allowed zeigt die zulässigen. Sie sehen nur Ihre eigenen Schlüssel („Sie sehen hier Ihre eigenen Schlüssel. Die Schlüssel anderer Mitglieder sind Konten mit Verwaltungsrecht vorbehalten.“) Ihnen fehlt identity.api-key.list . Ein Konto mit diesem Recht sieht alle Schlüssel der Organisation."
  },
  {
    "id": "getting-started/create-api-key#weiterführendes",
    "path": "/de/getting-started/create-api-key",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "create-api-key",
    "pageTitle": "Einen API-Schlüssel erzeugen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Der erste Aufruf mit curl Anmeldung Bereich Entwickler im Handbuch"
  },
  {
    "id": "getting-started/first-call",
    "path": "/de/getting-started/first-call",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "first-call",
    "pageTitle": "Der erste Aufruf mit curl",
    "section": "Der erste Aufruf mit curl",
    "anchor": "",
    "text": "Nach dieser Anleitung haben Sie mit Ihrem Schlüssel zwei Routen aufgerufen — Ihr eigenes Konto und Ihre Playlisten — und die Kopfzeilen einer Antwort gesehen. Fünf Minuten, keine Installation außer curl."
  },
  {
    "id": "getting-started/first-call#schritte",
    "path": "/de/getting-started/first-call",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "first-call",
    "pageTitle": "Der erste Aufruf mit curl",
    "section": "Schritte",
    "anchor": "schritte",
    "text": "Legen Sie Adresse und Schlüssel in Umgebungsvariablen ab. Der Schlüssel gehört in eine Kopfzeile, nie in die Adresse — Adressen landen in Protokollen, Kopfzeilen nicht. Rufen Sie Ihr eigenes Konto ab. GET /v1/me liest das Benutzerprofil, dem der Schlüssel gehört — die Antwort hier auf die Felder gekürzt, die zählen: tenantId ist die Kennung Ihrer Organisation. Steht dort eine andere als erwartet, zeigt der Schlüssel woandershin, als gedacht. Rufen Sie die Liste Ihrer Playlisten ab. GET /v1/playlists braucht das Recht playlist.list , das die Klassen Nur Lesen , Ausspielsteuerung und Vollzugriff enthalten — ein Schlüssel der Klasse Medien-Automation oder Inhalte-Pflege bekommt hier eine 403 . Auch diese Antwort ist auf die wichtigsten Felder gekürzt: Diese Liste nimmt limit zwischen 1 und 200 (Vorgabe 50) und blättert mit cursor weiter; nextCursor fehlt auf der letzten Seite. Die Displays ( GET /v1/players ) erreichen Sie mit einem Schlüssel genauso — fleet.player.list ist schlüsselfähig. Die Geräte dahinter ( GET /v1/devices ) nicht: identity.device.list ist nur für Benutzerkonten vorgesehen. Ein Display ist die Fläche, die etwas zeigt; ein Gerät die Maschine mit Zertifikat und Kopplung — und die bleibt Menschen vorbehalten. Sehen Sie sich die Kopfzeilen an. Mit -i zeigt curl den Status und die Kopfzeilen vor dem Rumpf: Zwei Zeilen zählen: Content-Type: application/json und X-Trace-Id: trc_… . Die Trace-Kennung ist die Spur dieses einen Aufrufs; notieren Sie sie, wenn etwas ni"
  },
  {
    "id": "getting-started/first-call#derselbe-aufruf-mit-der-kommandozeile",
    "path": "/de/getting-started/first-call",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "first-call",
    "pageTitle": "Der erste Aufruf mit curl",
    "section": "Derselbe Aufruf mit der Kommandozeile",
    "anchor": "derselbe-aufruf-mit-der-kommandozeile",
    "text": "Die Kommandozeile szenera nimmt Adresse und Schlüssel aus Umgebungsvariablen und spricht dieselben Routen; ist die Standardausgabe kein Terminal, gibt sie JSON aus. Den Schlüssel liest sie aus demselben SZENERA_API_KEY wie in Schritt 1, die Adresse aus SZENERA_BASE_URL : Wie Sie an die Kommandozeile kommen, steht unter Die CLI beziehen ."
  },
  {
    "id": "getting-started/first-call#ergebnis-prüfen",
    "path": "/de/getting-started/first-call",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "first-call",
    "pageTitle": "Der erste Aufruf mit curl",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "Beide Aufrufe antworten mit 200 und einem JSON-Rumpf; Fehler kämen als Rumpf mit code , message und traceId . Die Liste der Playlisten trägt items und, solange weitere Seiten folgen, nextCursor . Im Portal steht der Schlüssel unter Einstellungen › Organisation › API-Schlüssel jetzt mit einem Zeitpunkt in Zuletzt benutzt ."
  },
  {
    "id": "getting-started/first-call#wenn-etwas-nicht-klappt",
    "path": "/de/getting-started/first-call",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "first-call",
    "pageTitle": "Der erste Aufruf mit curl",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung 401 mit Auth.Required Kopfzeile fehlt, Bearer fehlt, oder der Wert hat kein bekanntes Präfix (Tippfehler im Präfix). Kopfzeile und Variable prüfen; siehe Wenn der erste Aufruf scheitert . 401 mit Auth.TokenMalformed Das Präfix ak_ stimmt, aber der Schlüssel ist abgeschnitten oder enthält ungültige Zeichen. Den Wert vollständig aus dem Geheimnis-Speicher übernehmen: ak_ plus 26 Zeichen. 401 mit Auth.ApiKeyUnknown , Auth.ApiKeyRevoked oder Auth.ApiKeyExpired Der Schlüssel existiert nicht, wurde widerrufen oder ist abgelaufen. Neuen Schlüssel erzeugen. 403 mit Permission.Denied Der Schlüssel trägt das Recht der Operation nicht — oder die Route verlangt ein Recht, das Schlüssel nie tragen (Geräte, Koppeln und Löschen von Displays, Schlüsselverwaltung). Klasse oder Rechte beim Erzeugen anpassen; für diese Routen ein Sitzungstoken verwenden. 404 mit Resource.NotFound Der Pfad trifft keine Route — meist fehlt /v1 , oder die Basis-Adresse ist falsch. Adresse und Pfad prüfen. 422 mit Validation.Failed Ein Parameter ist ungültig, etwa limit=500 . details.issues lesen und den Wert korrigieren."
  },
  {
    "id": "getting-started/first-call#weiterführendes",
    "path": "/de/getting-started/first-call",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "first-call",
    "pageTitle": "Der erste Aufruf mit curl",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Was eine Antwort enthält Wenn der erste Aufruf scheitert Paginierung"
  },
  {
    "id": "getting-started/response-contents",
    "path": "/de/getting-started/response-contents",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "response-contents",
    "pageTitle": "Was eine Antwort enthält",
    "section": "Was eine Antwort enthält",
    "anchor": "",
    "text": "Diese Seite ist eine Nachschlagetafel: Suchen Sie in der Tabelle, was Ihnen in einer Antwort begegnet — Status, Kopfzeile, Listenfeld, Kennung oder Fehlerfeld —, und lesen Sie daneben, was es bedeutet."
  },
  {
    "id": "getting-started/response-contents#statusklassen",
    "path": "/de/getting-started/response-contents",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "response-contents",
    "pageTitle": "Was eine Antwort enthält",
    "section": "Statusklassen",
    "anchor": "statusklassen",
    "text": "Status Bedeutung Was Sie tun 200 , 201 Erfolg; 201 beim Anlegen einer Ressource. Rumpf auswerten. 302 Nur auf den Anmeldewegen über einen Anbieter, bei Cloud-Anbindungen (Rundlauf durch den Browser) und beim Abholen eines App-Bündels (Weiterleitung zur Ablage). Für gewöhnliche Aufrufe mit Schlüssel nicht relevant; curl folgt mit -L . 4xx Ihr Aufruf: Ausweis, Recht, Pfad, Rumpf oder Zustand passen nicht. Immer mit Fehlerrumpf. code auswerten, Aufruf korrigieren. 5xx Die Plattform oder ein nachgelagerter Dienst ( 502 ). Ebenfalls mit Fehlerrumpf. traceId melden; bei 502 mit Retry-After später wiederholen."
  },
  {
    "id": "getting-started/response-contents#kopfzeilen",
    "path": "/de/getting-started/response-contents",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "response-contents",
    "pageTitle": "Was eine Antwort enthält",
    "section": "Kopfzeilen",
    "anchor": "kopfzeilen",
    "text": "Bis auf Idempotent-Replay sind alle hier genannten Kopfzeilen auch für Browser-Clients auf einem anderen Ursprung lesbar. Kopfzeile Wann Bedeutung Content-Type immer application/json ; mit Accept: application/problem+json bei Fehlern application/problem+json X-Trace-Id immer Spur-Kennung dieses Aufrufs, trc_… ; identisch mit traceId im Fehlerrumpf X-Correlation-Id wenn der Aufruf eine Korrelations-Kennung führt Kennung eines zusammenhängenden Vorgangs, cor_… ETag auf den Lese- und Änderungsrouten versionierter Ressourcen (Szenen, Playlisten, Programme, Rollen, Displays) die version der Ressource als Kennzeichen, etwa \"7\" ; beim Ändern als If-Match zurückschicken (siehe ETag und If-Match ) Idempotent-Replay bei einer Wiederholung mit demselben Idempotency-Key true : Die gespeicherte Antwort wurde geliefert, die Aktion lief nicht erneut X-RateLimit-Limit nur auf gedrosselten Routen erlaubte Anfragen im Fenster X-RateLimit-Remaining nur auf gedrosselten Routen davon noch offen X-RateLimit-Reset nur auf gedrosselten Routen Beginn des nächsten Fensters in Unix-Sekunden Retry-After bei 429 und bei 502 mit Wartezeit Sekunden bis zum nächsten erlaubten Versuch Server-Timing wenn die Plattform Zeitmessungen mitgibt Dauer der Bearbeitung auf dem Server"
  },
  {
    "id": "getting-started/response-contents#listen",
    "path": "/de/getting-started/response-contents",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "response-contents",
    "pageTitle": "Was eine Antwort enthält",
    "section": "Listen",
    "anchor": "listen",
    "text": "Geblätterte Listen haben immer dieselbe Form: Feld Bedeutung items die Datensätze dieser Seite nextCursor die Marke für die nächste Seite; fehlt auf der letzten Seite total die Gesamtzahl, wo die Route sie kennt — sonst fehlt das Feld Sie blättern mit den Abfrageparametern limit (Seitengröße) und cursor (Wert von nextCursor ). Bei geblätterten Listen liegt limit zwischen 1 und 200 mit Vorgabe 50; die gefilterten Listen der Verwaltung (Geräte, Konten, Rollen, Schlüssel) deckeln bei 100 mit Vorgabe 25. Kleine, nicht blätterbare Bestände antworten mit einer einfachen Liste — die Form steht am Endpunkt in der Referenz."
  },
  {
    "id": "getting-started/response-contents#kennungen",
    "path": "/de/getting-started/response-contents",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "response-contents",
    "pageTitle": "Was eine Antwort enthält",
    "section": "Kennungen",
    "anchor": "kennungen",
    "text": "Jede Kennung besteht aus einem Präfix und 26 Zeichen im Crockford-Base32-Alphabet (Ziffern und Großbuchstaben ohne I, L, O und U), zum Beispiel scn_01KJPX8NH0SZENE00000000001 . Das Präfix sagt, was die Kennung bezeichnet: Präfix Bezeichnet tnt_ Organisation usr_ Benutzerkonto dev_ Gerät ply_ Display (in der API playerId ) scn_ Szene pll_ Playlist sch_ Programm (in der API scheduleId ) ast_ Medium fld_ Ordner der Medien rol_ Rolle whk_ Webhook-Endpunkt whd_ Webhook-Zustellung akm_ API-Schlüssel (der Eintrag in Listen) adv_ Werbepartner acp_ Werbekampagne bkl_ Buchung einer Werbekampagne crv_ Motiv einer Buchung ppr_ Planfassung eines Displays pss_ Bildschirmaufnahme eines Displays pbt_ Stapel von Ausspielungen, den ein Gerät gemeldet hat pcl_ Abschluss einer Buchung über einen Zeitraum prf_ Nachweis über einen Abschluss pfi_ Datei eines Nachweises (PDF, Protokoll, Manifest, Bildschirmaufnahme) pdy_ Zustellung eines Nachweises an einen Ansprechpartner des Werbepartners shl_ Verweis, mit dem ein Werbepartner seine Nachweise ohne Konto öffnet rvs_ Erlösaufstellung eines Standorts für einen Monat trc_ Spur-Kennung eines Aufrufs cor_ Korrelations-Kennung eines Vorgangs Der Schlüssel selbst beginnt mit ak_ und ist ein Geheimwert: Er erscheint nur einmal bei der Ausstellung. In Listen steht seine Kennung akm_… und der Präfix ak_ plus neun Zeichen zum Wiedererkennen."
  },
  {
    "id": "getting-started/response-contents#fehlerrumpf",
    "path": "/de/getting-started/response-contents",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "response-contents",
    "pageTitle": "Was eine Antwort enthält",
    "section": "Fehlerrumpf",
    "anchor": "fehlerrumpf",
    "text": "Feld Wann Bedeutung code immer stabiler Code in der Form Bereich.Ursache , etwa Auth.Required oder Conflict.OptimisticLock — darauf schalten Sie message immer Kurztext für Menschen; nicht auswerten traceId immer, nie leer Spur-Kennung trc_… für Rückfragen details wenn der Fehler Zusatzangaben trägt etwa issues bei Validation.Failed , expected / actual bei Conflict.OptimisticLock ; fehlt sonst ganz correlationId wenn der Aufruf eine Korrelations-Kennung führt cor_… retryAfterSeconds , externalService nur bei Integrationsfehlern ( 502 ) Wartezeit (auch als Retry-After ) und der betroffene nachgelagerte Dienst stack nie im Produktivbetrieb nur in Entwicklungsumgebungen bei internen Fehlern Der HTTP-Status folgt dem Bereich des Codes: Validation.* → 422, Auth.* → 401, Permission.* → 403, *.NotFound → 404, Conflict.* → 409, RateLimit.* → 429, Payload.* → 413, Integration.* → 502. Einzelne Codes weichen ausdrücklich ab; die Seiten der Fehlercodes nennen für jeden Code den tatsächlich gesendeten Status."
  },
  {
    "id": "getting-started/response-contents#fehler-als-problem-details-rfc-9457",
    "path": "/de/getting-started/response-contents",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "response-contents",
    "pageTitle": "Was eine Antwort enthält",
    "section": "Fehler als Problem Details (RFC 9457)",
    "anchor": "fehler-als-problem-details-rfc-9457",
    "text": "Senden Sie Accept: application/problem+json , kommt derselbe Fehler als ProblemDetails -Dokument: Feld Wann Bedeutung type immer Adressform des Codes, klein geschrieben mit Schrägstrichen title immer Kurztitel des Codes status immer der HTTP-Status detail immer der Text von message code immer derselbe Code wie im Standard-Rumpf instance nach Lage der angefragte Pfad errorDetails nach Lage dieselben Zusatzangaben wie details traceId , correlationId , retryAfterSeconds nach Lage wie oben Ein stack gibt es in dieser Form nie."
  },
  {
    "id": "getting-started/response-contents#zeitangaben",
    "path": "/de/getting-started/response-contents",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "response-contents",
    "pageTitle": "Was eine Antwort enthält",
    "section": "Zeitangaben",
    "anchor": "zeitangaben",
    "text": "Alle Zeitpunkte sind Zeichenketten nach ISO 8601 mit Zeitzonen-Angabe, zum Beispiel \"createdAt\": \"2026-05-04T07:00:00.000Z\" . Ein fehlender Zeitpunkt steht als null (etwa \"expiresAt\": null für einen unbefristeten Schlüssel)."
  },
  {
    "id": "getting-started/response-contents#weiterführendes",
    "path": "/de/getting-started/response-contents",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "response-contents",
    "pageTitle": "Was eine Antwort enthält",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Fehlerformat Paginierung ETag und If-Match"
  },
  {
    "id": "getting-started/idempotency",
    "path": "/de/getting-started/idempotency",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "idempotency",
    "pageTitle": "Idempotenz von Anfang an",
    "section": "Idempotenz von Anfang an",
    "anchor": "",
    "text": "Nach dieser Anleitung erzeugt eine Wiederholung nach Netzabbruch keinen zweiten Vorgang mehr: Ihr Client schickt zu jedem verändernden Aufruf einen Idempotency-Key , und die Plattform liefert bei einer Wiederholung die gespeicherte Antwort statt einer neuen Ressource."
  },
  {
    "id": "getting-started/idempotency#schritte",
    "path": "/de/getting-started/idempotency",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "idempotency",
    "pageTitle": "Idempotenz von Anfang an",
    "section": "Schritte",
    "anchor": "schritte",
    "text": "Erzeugen Sie je Vorgang einen Schlüssel von mindestens 8 Zeichen — am besten einen, den Ihr System ohnehin kennt, etwa eine Auftrags- oder Importnummer. Er muss nur innerhalb Ihrer Organisation für diese Methode und diesen Pfad eindeutig sein. Senden Sie den Schlüssel als Kopfzeile Idempotency-Key mit dem verändernden Aufruf: Die Plattform speichert Status, Rumpf und die Kopfzeilen ETag , Location und X-Trace-Id dieser ersten Antwort für 24 Stunden. Bricht die Verbindung ab, bevor die Antwort bei Ihnen ankommt, wiederholen Sie denselben Aufruf mit demselben Schlüssel und demselben Rumpf. Es entsteht kein zweiter Ordner. Erkennen Sie die Wiederholung an der Kopfzeile Idempotent-Replay: true : Sie bekamen die gespeicherte Antwort, die Aktion lief nicht erneut."
  },
  {
    "id": "getting-started/idempotency#regeln-die-dabei-gelten",
    "path": "/de/getting-started/idempotency",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "idempotency",
    "pageTitle": "Idempotenz von Anfang an",
    "section": "Regeln, die dabei gelten",
    "anchor": "regeln-die-dabei-gelten",
    "text": "Der Zwischenspeicher ist getrennt je Organisation, Methode und Pfad. Derselbe Schlüssel auf einem anderen Pfad oder mit einer anderen Methode trifft einen anderen Eintrag. Kürzer als 8 Zeichen: 422 Validation.Failed . Derselbe Schlüssel mit einem anderen Rumpf ist keine Wiederholung, sondern ein zweiter Vorgang unter geborgtem Namen: 422 Validation.IdempotencyKeyReused . Weder die alte Antwort noch ein neuer Vorgang kommt zurück. Auf Routen, die den Schlüssel erzwingen, fehlt er: 409 Validation.IdempotencyKeyRequired . Nach 24 Stunden ist der Eintrag verfallen; derselbe Schlüssel führt den Vorgang dann erneut aus. Für Änderungen an versionierten Ressourcen (Szenen, Playlisten, Programme, Rollen, Displays) kommt If-Match mit der gelesenen version aus dem Rumpf hinzu — der Idempotency-Key schützt vor der doppelten Ausführung, If-Match vor dem Überschreiben fremder Änderungen. Siehe ETag und If-Match ."
  },
  {
    "id": "getting-started/idempotency#über-die-kommandozeile",
    "path": "/de/getting-started/idempotency",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "idempotency",
    "pageTitle": "Idempotenz von Anfang an",
    "section": "Über die Kommandozeile",
    "anchor": "über-die-kommandozeile",
    "text": "szenera api post und die übrigen schreibenden Befehle erzeugen ohne Angabe je Aufruf einen frischen Schlüssel. Nur zum Fortsetzen einer abgebrochenen Wiederholung setzen Sie ihn selbst:"
  },
  {
    "id": "getting-started/idempotency#ergebnis-prüfen",
    "path": "/de/getting-started/idempotency",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "idempotency",
    "pageTitle": "Idempotenz von Anfang an",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "Der zweite Aufruf liefert denselben Status und denselben Rumpf wie der erste — dieselbe Kennung fld_… — und zusätzlich Idempotent-Replay: true . Die Ordnerliste enthält den Ordner genau einmal."
  },
  {
    "id": "getting-started/idempotency#wenn-etwas-nicht-klappt",
    "path": "/de/getting-started/idempotency",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "idempotency",
    "pageTitle": "Idempotenz von Anfang an",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung Der zweite Aufruf legt doch eine zweite Ressource an Anderer Pfad, andere Methode, anderer Schlüssel — oder der Eintrag ist nach 24 Stunden verfallen. Schlüssel, Pfad und Methode der Wiederholung mit dem ersten Versuch vergleichen; Wiederholungen innerhalb von 24 Stunden abschließen. 422 Validation.Failed mit „Idempotency-Key too short“ Der Schlüssel hat weniger als 8 Zeichen. Längeren Schlüssel wählen. 422 Validation.IdempotencyKeyReused Derselbe Schlüssel mit einem anderen Rumpf. Für einen neuen Vorgang einen neuen Schlüssel vergeben. 409 Validation.IdempotencyKeyRequired Die Route verlangt den Schlüssel. Kopfzeile immer mitsenden."
  },
  {
    "id": "getting-started/idempotency#weiterführendes",
    "path": "/de/getting-started/idempotency",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "idempotency",
    "pageTitle": "Idempotenz von Anfang an",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "ETag und If-Match Fehlerformat Skripte und CI"
  },
  {
    "id": "getting-started/troubleshooting",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "Wenn der erste Aufruf scheitert",
    "anchor": "",
    "text": "Die Symptome stehen in der Reihenfolge, in der sie beim ersten Aufruf am häufigsten auftreten. Lesen Sie zuerst den Status, dann das Feld code im Fehlerrumpf — und heben Sie die traceId auf, falls Sie nachfragen müssen."
  },
  {
    "id": "getting-started/troubleshooting#symptome-nach-häufigkeit",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "Symptome nach Häufigkeit",
    "anchor": "symptome-nach-häufigkeit",
    "text": ""
  },
  {
    "id": "getting-started/troubleshooting#401-mit-authrequired",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "401 mit Auth.Required",
    "anchor": "401-mit-authrequired",
    "text": "Bedeutung: Die Kopfzeile Authorization fehlt, das Wort Bearer davor fehlt, oder der Wert hat kein bekanntes Präfix (Tippfehler im Präfix). Die Plattform behandelt einen unbrauchbaren Ausweis nie als anonym — auch ein Wert mit unbekanntem Präfix landet hier und nicht bei einer 404. Lösung: Prüfen Sie, dass die Kopfzeile genau Authorization: Bearer ak_… lautet und die Umgebungsvariable gefüllt ist ( echo \"$SZENERA_API_KEY\" | cut -c1-3 muss ak_ zeigen). Kommandozeile: Exit-Code 3. Siehe Der erste Aufruf mit curl ."
  },
  {
    "id": "getting-started/troubleshooting#401-mit-authtokenmalformed",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "401 mit Auth.TokenMalformed",
    "anchor": "401-mit-authtokenmalformed",
    "text": "Bedeutung: Das Präfix ak_ stimmt, aber der Rest entspricht nicht dem Muster eines Schlüssels: genau 26 Zeichen aus Ziffern und Großbuchstaben ohne I, L, O und U. Typisch ist ein beim Kopieren abgeschnittener Schlüssel oder ein Wert mit fremden Zeichen — Leerzeichen, Anführungszeichen, Kleinbuchstaben. Lösung: Übernehmen Sie den Wert vollständig aus Ihrem Geheimnis-Speicher und prüfen Sie die Länge ( echo -n \"$SZENERA_API_KEY\" | wc -c muss 29 ergeben). Kommandozeile: Exit-Code 3. Siehe Der erste Aufruf mit curl ."
  },
  {
    "id": "getting-started/troubleshooting#401-mit-authapikeyunknown-authapikeyrevoked-oder-authapikeyexpired",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "401 mit Auth.ApiKeyUnknown, Auth.ApiKeyRevoked oder Auth.ApiKeyExpired",
    "anchor": "401-mit-authapikeyunknown-authapikeyrevoked-oder-authapikeyexpired",
    "text": "Bedeutung: Präfix und Muster stimmen, aber der Schlüssel existiert nicht (Tippfehler im Wert, der die 26 Zeichen noch erfüllt), wurde widerrufen oder hat sein Datum unter Gültig bis überschritten. Lösung: Erzeugen Sie einen neuen Schlüssel und tauschen Sie den Wert in Ihrem Geheimnis-Speicher. Ein widerrufener Schlüssel kommt nicht zurück. Siehe Einen API-Schlüssel erzeugen ."
  },
  {
    "id": "getting-started/troubleshooting#401-mit-authtokenexpired",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "401 mit Auth.TokenExpired",
    "anchor": "401-mit-authtokenexpired",
    "text": "Bedeutung: Sie senden ein Sitzungstoken ( eyJ… ) aus der Anmeldung; es ist nach 15 Minuten abgelaufen. Lösung: Für Maschinen einen Schlüssel ak_… verwenden. Wer die Anmeldewege braucht, findet die Auffrischung unter Anmeldung ."
  },
  {
    "id": "getting-started/troubleshooting#403-mit-permissiondenied",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "403 mit Permission.Denied",
    "anchor": "403-mit-permissiondenied",
    "text": "Bedeutung: Der Ausweis gilt, trägt aber das Recht der Operation nicht. Jede Operation nennt ihr Recht in der Referenz (etwa identity.device.list ); ein Schlüssel hat nur die Rechte, die beim Anlegen gewählt wurden. Einige Rechte tragen Schlüssel nie — etwa identity.device.list (Geräte), fleet.playback-history.read (die Tages-Rückschau eines Displays) und identity.api-key.create ; diese Routen erreichen Sie nur mit einem Sitzungstoken. Lösung: Prüfen Sie im Portal unter Einstellungen › Organisation › API-Schlüssel die Spalten Name / Rechte und Klasse des Schlüssels; fehlt das Recht, erzeugen Sie einen Schlüssel mit passender Klasse oder ergänzen es über Einzelne Rechte anpassen . Kommandozeile: Exit-Code 4. Siehe Einen API-Schlüssel erzeugen ."
  },
  {
    "id": "getting-started/troubleshooting#403-mit-permissionoperatoronly",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "403 mit Permission.OperatorOnly",
    "anchor": "403-mit-permissionoperatoronly",
    "text": "Bedeutung: Die Route unter /v1/platform/… steht nur dem Plattformbetrieb offen. Ein Schlüssel einer Organisation kommt dort nie hinein; hier hilft kein neuer Ausweis, sondern eine andere Route. Lösung: Die Route der Organisation verwenden (etwa GET /v1/playlists statt einer Route unter /v1/platform ). Siehe Bereiche der Referenz ."
  },
  {
    "id": "getting-started/troubleshooting#402-mit-billingaddonrequired",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "402 mit Billing.AddonRequired",
    "anchor": "402-mit-billingaddonrequired",
    "text": "Bedeutung: Beim Anlegen oder Rotieren eines Schlüssels: Das Paket der Organisation enthält den API-Zugang nicht. Bestehende Schlüssel arbeiten weiter, nur Neue verlangen die Freischaltung. Lösung: Paket ab Premium („API und Webhooks“) oder die vertragliche Freischaltung des API-Zugangs. Siehe Einen API-Schlüssel erzeugen ."
  },
  {
    "id": "getting-started/troubleshooting#404-mit-resourcenotfound",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "404 mit Resource.NotFound",
    "anchor": "404-mit-resourcenotfound",
    "text": "Bedeutung: Der Pfad trifft keine Route. Beim ersten Aufruf fast immer: /v1 fehlt ( /me statt /v1/me ), ein Tippfehler im Pfad, oder die Basis-Adresse zeigt auf das Portal statt auf die API. Lösung: Adresse und Pfad prüfen; jede Route beginnt mit /v1 . Kommandozeile: Exit-Code 5. Siehe Basis-URL, /v1 und Stabilität ."
  },
  {
    "id": "getting-started/troubleshooting#404-mit-ressourcenotfound-etwa-scenenotfound-devicenotfound",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "404 mit <Ressource>.NotFound (etwa Scene.NotFound, Device.NotFound)",
    "anchor": "404-mit-ressourcenotfound-etwa-scenenotfound-devicenotfound",
    "text": "Bedeutung: Die Route stimmt, aber die Kennung gehört nicht zu Ihrer Organisation oder existiert nicht mehr. Eine fremde Kennung ist von einer erfundenen nicht zu unterscheiden — beides ist dieselbe 404. Lösung: Kennung aus einer Liste Ihrer Organisation übernehmen ( GET /v1/scenes , GET /v1/playlists , …) und das Präfix mit der Ressource abgleichen ( scn_ für Szenen, pll_ für Playlisten). Siehe Was eine Antwort enthält ."
  },
  {
    "id": "getting-started/troubleshooting#422-mit-validationfailed",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "422 mit Validation.Failed",
    "anchor": "422-mit-validationfailed",
    "text": "Bedeutung: Ein Parameter oder ein Feld im Rumpf verletzt den Vertrag — etwa limit=500 , wo höchstens 200 gilt, ein fehlendes Pflichtfeld oder ein falscher Typ. Lösung: Lesen Sie details.issues : Jeder Eintrag nennt den Feldpfad und den Verstoß. Kommandozeile: Exit-Code 7; die Kommandozeile prüft einen selbst geschriebenen Rumpf schon vor dem Senden und meldet den Feldpfad mit Exit-Code 1. Siehe Fehlerformat ."
  },
  {
    "id": "getting-started/troubleshooting#429-mit-ratelimitexceeded",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "429 mit RateLimit.Exceeded",
    "anchor": "429-mit-ratelimitexceeded",
    "text": "Bedeutung: Zu viele Anfragen in einem Fenster. Gedrosselt sind vor allem die missbrauchsnahen Routen — Anmeldung, Registrierung, Passwort-Zurücksetzen; einzelne weitere Routen nennen es in der Referenz. Ihr erster Aufruf mit Schlüssel trifft sie nicht; wer die Anmeldung in einer Schleife wiederholt, schon. Lösung: Die Kopfzeile Retry-After nennt die Sekunden bis zum nächsten erlaubten Versuch; X-RateLimit-Reset den Beginn des nächsten Fensters. Warten, dann wiederholen — und für Maschinen einen Schlüssel statt der Anmeldung verwenden. Kommandozeile: Exit-Code 8. Siehe Drosselung und Rate-Limit-Kopfzeilen ."
  },
  {
    "id": "getting-started/troubleshooting#keine-http-antwort-verbindung-verweigert-zeitüberschreitung-tls-fehler",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "Keine HTTP-Antwort: Verbindung verweigert, Zeitüberschreitung, TLS-Fehler",
    "anchor": "keine-http-antwort-verbindung-verweigert-zeitüberschreitung-tls-fehler",
    "text": "Bedeutung: Der Aufruf hat die Plattform nicht erreicht. Die Basis-Adresse ist falsch, der Port fehlt oder ist falsch, das Zertifikat passt nicht zum Namen, oder ein Proxy blockiert. Lösung: Erst die Adresse ohne Ausweis prüfen — szenera health fragt die öffentliche Bereitschaftsroute Ihrer Umgebung ab und scheitert nicht an der Anmeldung; so sieht ein falscher Port nicht aus wie ein falscher Schlüssel. Kommandozeile: Exit-Code 10. Danach Schritt 1 unter Der erste Aufruf mit curl ."
  },
  {
    "id": "getting-started/troubleshooting#die-trace-kennung-aufheben",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "Die Trace-Kennung aufheben",
    "anchor": "die-trace-kennung-aufheben",
    "text": "Jede Fehlerantwort trägt traceId im Rumpf und X-Trace-Id in der Kopfzeile — derselbe Wert trc_… . Er ist die Spur genau dieses Aufrufs auf der Plattform. Wenn Sie nachfragen, nennen Sie ihn; ohne ihn lässt sich ein einzelner Aufruf nicht wiederfinden."
  },
  {
    "id": "getting-started/troubleshooting#weiterführendes",
    "path": "/de/getting-started/troubleshooting",
    "area": "getting-started",
    "areaTitle": "Erste Schritte",
    "page": "troubleshooting",
    "pageTitle": "Wenn der erste Aufruf scheitert",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Einen API-Schlüssel erzeugen Der erste Aufruf mit curl Fehlercodes: Allgemein"
  },
  {
    "id": "api/index",
    "path": "/de/api",
    "area": "api",
    "areaTitle": "API",
    "page": "index",
    "pageTitle": "API",
    "section": "API",
    "anchor": "",
    "text": "Die Regeln, die auf jeder Route gelten — Adresse, Anmeldung, Fehler, Sperren, Blättern, Stabilität — und der Wegweiser durch die Referenz. Der Portal-Dialog „API-Schlüssel erstellen“ mit ausgefülltem Namensfeld, den sechs Rechte-Klassen — gewählt ist „Nur Lesen“ mit 12 Rechten — und den Auswahlfeldern für die Gültigkeit des neuen und des alten Schlüssels Die Regeln dieses Bereichs gelten für Aufrufe mit einem Schlüssel ak_…; ausgestellt, rotiert und widerrufen werden die Schlüssel im Portal unter Einstellungen › API-Schlüssel."
  },
  {
    "id": "api/index#grundlagen",
    "path": "/de/api",
    "area": "api",
    "areaTitle": "API",
    "page": "index",
    "pageTitle": "API",
    "section": "Grundlagen",
    "anchor": "grundlagen",
    "text": "Seite Wann Sie es brauchen Basis-URL, /v1 und Stabilität Woher die Basis-Adresse kommt, warum fast jede Route /v1 trägt, welche Pfade außerhalb liegen und was x-stability je Operation zusagt. Anmeldung: Schlüssel und Sitzungen Bearer-Kopfzeile, Schlüssel-Präfixe, Rechteumfang, Ablauf und Rotation eines Schlüssels — und wann eine Sitzung statt eines Schlüssels sinnvoll ist. Das Fehlerformat Der Fehlerrumpf mit code, message und traceId, die RFC-9457-Form und die Regel, nach der aus einem Code der HTTP-Status wird. Gleichzeitige Änderungen: ETag und If-Match Eine versionierte Ressource lesen, die Version als If-Match zurückschicken und einen 409-Konflikt sauber auflösen. Drosselung und Rate-Limit-Kopfzeilen Welche Routen eine eigene Grenze haben, welches Kontingent an einem Schlüssel hängt, was die vier Kopfzeilen bedeuten und wie ein Client auf 429 reagiert. Blättern: limit, cursor, items, nextCursor Cursor-Blättern auf allen Listen, die Grenzen 50/200 und 25/100, die vier Routen mit page — und wie die CLI mit --all allen Seiten folgt."
  },
  {
    "id": "api/index#zugänge-und-gegenstände",
    "path": "/de/api",
    "area": "api",
    "areaTitle": "API",
    "page": "index",
    "pageTitle": "API",
    "section": "Zugänge und Gegenstände",
    "anchor": "zugänge-und-gegenstände",
    "text": "Seite Wann Sie es brauchen Delegierter Zugang mit OAuth 2.1 Ihre Anwendung läuft bei fremden Kunden: registrieren, mit PKCE einwilligen lassen, Token oat_ tauschen, erneuern, widerrufen. Provisionierung mit SCIM 2.0 Ein Verzeichnisdienst soll Konten anlegen, sperren und Rollen setzen: Zugang sct_, Endpunkte unter /scim/v2, Abbildung und Fehler. Der MCP-Dienst für KI-Assistenten Ein Assistent soll mit Ihrem Bestand arbeiten: Adresse, Anmeldung, Werkzeugliste, Bestätigungspflicht, Grenzen und Fehler. Standorte Eine Filialstruktur spiegeln: Felder eines Standorts, seine Routen, die Zuordnung eines Displays und die Auswertung nach Orten. Web-Displays Ein Display anlegen, hinter dem kein Gerät steht: Routen, Anschluss-Adresse, die beiden Zustände, Löschen und die Grenzen gegenüber einem Gerät."
  },
  {
    "id": "api/index#fehlercodes",
    "path": "/de/api",
    "area": "api",
    "areaTitle": "API",
    "page": "index",
    "pageTitle": "API",
    "section": "Fehlercodes",
    "anchor": "fehlercodes",
    "text": "Seite Wann Sie es brauchen Fehlercodes: auf jeder Route möglich Die plattformweiten Codes für Anmeldung, Rechte, Validierung, Konflikte, Sperren, Nutzlast, HTTP, Ereignisse, Zwischenspeicher, Jobs und Ressourcen. Fehlercodes: Konten und Sitzungen Die Codes der Bereiche Identity und Sessions: Benutzerkonten, Schlüssel, Einladungen, Anmeldung, zweiter Faktor, Sitzungen. Fehlercodes: Organisation, Zugang, Beitritt Die Codes der Bereiche Tenancy, Access, Onboarding und Cloud-Connections: Stammdaten, Domain, Rollen, Erteilungen, Registrierung, Speicher-Verbindungen. Fehlercodes: Abrechnung Die Codes des Bereichs Billing: Paket, Lizenzen, Zubuchungen, Zahlungsmittel, Rechnungen — und warum hier 402 steht. Fehlercodes: Medien, Szenen, Programme, Apps Die Codes der Bereiche Media-Library, Composition, Scheduling, App-Catalog, Marketplace, App-Runtime und Advertising. Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb Die Codes der Bereiche Fleet, Playback, Realtime, Notifications, Analytics, Audit und Ops-Console — letztere nur auf Betreiberrouten. Fehlercodes: Serverfehler (5xx) Die 119 Codes, auf die ein Client nicht fachlich reagieren kann: Invarianten, Start, Datenbank, nachgelagerte Dienste — wiederholen oder melden."
  },
  {
    "id": "api/index#referenz-wegweiser",
    "path": "/de/api",
    "area": "api",
    "areaTitle": "API",
    "page": "index",
    "pageTitle": "API",
    "section": "Referenz-Wegweiser",
    "anchor": "referenz-wegweiser",
    "text": "Seite Wann Sie es brauchen Die Referenz lesen Unter /api/reference eine Operation finden, Berechtigung, Sicherheitsverfahren, Beispiel und Stabilität ablesen und die Spezifikation herunterladen. Die Bereiche der Referenz Die 19 Tags der Spezifikation in ihren 7 Gruppen mit je einem Satz Zweck — und warum die Betreiber-Fläche nicht dazugehört. Einen eigenen Client aus der Spezifikation erzeugen Es gibt heute keinen veröffentlichten Client: So erzeugen Sie aus openapi.json Ihren eigenen und bilden die Konventionen der API darin ab."
  },
  {
    "id": "api/base-url-and-stability",
    "path": "/de/api/base-url-and-stability",
    "area": "api",
    "areaTitle": "API",
    "page": "base-url-and-stability",
    "pageTitle": "Basis-URL, /v1 und Stabilität",
    "section": "Basis-URL, /v1 und Stabilität",
    "anchor": "",
    "text": "Woher die Basis-Adresse kommt, warum fast jede Route /v1 trägt, was die Spezifikation über sich selbst sagt und was x-stability je Operation zusichert. Lesen Sie die Tabellen als Vertrag: Was hier steht, ändert sich nicht ohne Ankündigung."
  },
  {
    "id": "api/base-url-and-stability#basis-adresse",
    "path": "/de/api/base-url-and-stability",
    "area": "api",
    "areaTitle": "API",
    "page": "base-url-and-stability",
    "pageTitle": "Basis-URL, /v1 und Stabilität",
    "section": "Basis-Adresse",
    "anchor": "basis-adresse",
    "text": "Die API hat keine feste öffentliche Adresse. Sie sprechen den Ursprung Ihrer Umgebung an. Die Spezifikation auf dieser Seite nennt ihn als ersten Server unter dem Namen „Die Schnittstelle Ihrer Umgebung“ — die Adresse setzt der Betreiber beim Start ein, deshalb steht in einer selbst gebauten Fassung an ihrer Stelle noch der Platzhalter __SZENERA_API_BASE_URL__ . Als zweiten Server führt sie http://localhost:41000 für eine lokale Entwicklungsumgebung. Die Fassung an Ihrer Umgebung ( GET /v1/openapi.json ) nennt stattdessen / — dort ist die eigene Herkunft die richtige Antwort, hier wäre es diese Doku-Seite. Was Wert Basis-Adresse der Ursprung Ihrer Umgebung, z. B. https://api.<ihre-domain> Pfad-Präfix /v1 auf jeder Route Transport HTTPS; Rumpf und Antwort als JSON Beispiel GET $SZENERA_API/v1/me In allen Beispielen dieser Seiten steht die Basis-Adresse in der Umgebungsvariablen SZENERA_API : Die Anmeldung hat, wo eingerichtet, einen zweiten Wirt: https://auth.<ihre-domain> . Er bedient dieselbe Schnittstelle für /v1/auth/… , OAuth, SCIM und die Metadaten, alle anderen Routen liegen weiter an der Basis-Adresse. Die Endpunkte der Anmeldung nennen künftig die Metadaten von auth.<ihre-domain> , die Adressen an der Basis-Adresse bleiben bis auf Weiteres erreichbar."
  },
  {
    "id": "api/base-url-and-stability#das-präfix-v1",
    "path": "/de/api/base-url-and-stability",
    "area": "api",
    "areaTitle": "API",
    "page": "base-url-and-stability",
    "pageTitle": "Basis-URL, /v1 und Stabilität",
    "section": "Das Präfix /v1",
    "anchor": "das-präfix-v1",
    "text": "Jede Route, über die Szenera selbst entscheidet, liegt unter /v1 , und der Pfad ist Teil des Vertrags: GET /v1/scenes/{id} , POST /v1/auth/login , GET /v1/devices . Außerhalb des Präfixes liegen vier Gruppen, und keine davon ist eine Nachlässigkeit. Pfad Warum ohne /v1 /scim/v2/… Der Pfad gehört dem fremden Protokoll (RFC 7644), das seine eigene Version trägt. Ein Verzeichnisdienst prüft seine Angaben gegen die Adresse, unter der er spricht — ein Präfix davor bräche das. Siehe Provisionierung mit SCIM 2.0 . /.well-known/oauth-authorization-server , /.well-known/oauth-protected-resource RFC 8414 und RFC 9728 legen diese Adressen wörtlich fest; ein fremder Client sucht dort und nirgends sonst. /oauth/register , /oauth/authorize , /oauth/token , /oauth/revoke die Endpunkte des Standards, siehe Delegierter Zugang mit OAuth 2.1 GET /health/live , GET /health/ready , GET /health/version , GET /metrics anmeldefreie Betriebsrouten; sie beschreiben den Zustand einer Umgebung, nicht Ihren Bestand, und stehen deshalb auch nicht in der öffentlichen Spezifikation Alles, was hinter der Einwilligung liegt — die Einwilligung selbst, die verbundenen Anwendungen unter GET /v1/me/connected-apps —, steht regulär unter /v1 : Diese Routen sind der Vertrag dieser API und nicht der eines fremden Standards."
  },
  {
    "id": "api/base-url-and-stability#die-spezifikation",
    "path": "/de/api/base-url-and-stability",
    "area": "api",
    "areaTitle": "API",
    "page": "base-url-and-stability",
    "pageTitle": "Basis-URL, /v1 und Stabilität",
    "section": "Die Spezifikation",
    "anchor": "die-spezifikation",
    "text": "Angabe Wert Format OpenAPI 3.1.0 info.title Szenera API info.version 1.0.0 Operationen 443 auf 346 Pfaden Bereiche (Tags) 19 in 7 Gruppen — siehe Die Bereiche der Referenz Schemata mit Beispiel 741 von 741 Zwei Bezugsquellen: Quelle Stand Zugriff /api/openapi.json auf dieser Seite das aktuelle Release, ohne die als internal geführten Operationen ohne Anmeldung GET /v1/openapi.json an Ihrer Umgebung dieselbe öffentliche Fassung für den Stand, der dort tatsächlich läuft ohne Anmeldung; Cache-Control: public, max-age=300 — fünf Minuten zwischenspeicherbar Beide Quellen zeigen dieselbe Auswahl: die Operationen der Klassen stable und beta . Was internal trägt, fällt heraus, mitsamt den Schemata, die nur dort gebraucht wurden — heute 337 der 780 Operationen, darunter die gesamte Betreiber-Fläche unter /v1/platform/… . Ein daraus erzeugter Client bekommt damit nur, was Ihre Organisation auch aufrufen darf. Die vollständige Fassung steht dem Betreiber der Plattform hinter seiner eigenen Anmeldung zur Verfügung. Auf dieser Seite erscheint sie nicht."
  },
  {
    "id": "api/base-url-and-stability#stabilität-je-operation",
    "path": "/de/api/base-url-and-stability",
    "area": "api",
    "areaTitle": "API",
    "page": "base-url-and-stability",
    "pageTitle": "Basis-URL, /v1 und Stabilität",
    "section": "Stabilität je Operation",
    "anchor": "stabilität-je-operation",
    "text": "Jede Operation trägt die Erweiterung x-stability mit einem von drei Werten: Wert Bedeutung stable Pfad, Pflichtfelder und Fehlercodes ändern sich nicht ohne neue Version. beta Öffentlich, aber jung: Die Form kann sich noch bewegen, bis sie einen Zyklus ohne Bruch überstanden hat. internal Nicht Teil der öffentlichen Fläche; erscheint nur in der Fassung des Betreibers und damit in keiner Datei dieser Seite. Stand des aktuellen Release: 372 der 443 Operationen sind stable , 71 sind beta — die 31 Routen der Werbung unter /v1/advertising/… (siehe Die Bereiche der Referenz ), GET /v1/webhooks/event-types (siehe Den Ereigniskatalog über die API abrufen ), der Trockenlauf POST /v1/scenes/validate (siehe Der MCP-Dienst für KI-Assistenten ), die drei Wege des Render-Auftrags einer Szenen-Version ( POST /v1/scenes/{id}/versions/{vid}/render , GET /v1/scenes/{id}/versions/{vid}/render/{jobId} und GET /v1/scenes/{id}/versions/{vid}/render/{jobId}/image ), die fünf Routen der Web-Displays , deren Form sich mit dieser Welle bereits einmal verschoben hat, die zwei Wege der Bildschirmaufnahmen eines Displays ( GET /v1/players/{playerId}/screenshots und GET /v1/players/{playerId}/screenshots/{screenshotId} ), die neun Routen der neuen Auswertung unter /v1/analytics/ — die freie Auswertung mit Katalog und Ausgabe, der Überblick, Tag und Protokoll eines Displays, die Inhalte und die Lieferung einer Werbekampagne —, die acht Routen der gespeicherten Ansichten unter /v1/analytics/reports , die mi"
  },
  {
    "id": "api/base-url-and-stability#was-es-nicht-gibt",
    "path": "/de/api/base-url-and-stability",
    "area": "api",
    "areaTitle": "API",
    "page": "base-url-and-stability",
    "pageTitle": "Basis-URL, /v1 und Stabilität",
    "section": "Was es nicht gibt",
    "anchor": "was-es-nicht-gibt",
    "text": "Keine Versionswahl über Kopfzeilen oder Abfrageparameter; die Version steht im Pfad. Keine eigene Basis-Adresse je Bereich: Alle Bereiche liegen unter demselben Ursprung, auch die Pfade ohne /v1 . Die Anmeldung ist zusätzlich unter dem Anmelde-Wirt erreichbar, mit denselben Pfaden. Kein zweiter Vertrag neben dieser Spezifikation: Was die Kommandozeile, der Assistent oder ein Portal kann, kann diese API auch. Läuft eine Operation aus, trägt sie deprecated: true und x-sunset mit dem Abschalt-Tag, und ihre Antworten tragen Deprecation und Sunset . Zwischen Ankündigung und Abschaltung liegen zwölf Monate — die Einzelheiten stehen unter Stabilitätsklassen und Auslauf . Wie Änderungen an der API erscheinen und wie Sie zwei Stände der Spezifikation vergleichen, steht unter Release-Züge, Notizen und Abkündigungen ."
  },
  {
    "id": "api/base-url-and-stability#weiterführendes",
    "path": "/de/api/base-url-and-stability",
    "area": "api",
    "areaTitle": "API",
    "page": "base-url-and-stability",
    "pageTitle": "Basis-URL, /v1 und Stabilität",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Die Referenz lesen Der erste Aufruf mit curl Stabilitätsklassen und Auslauf"
  },
  {
    "id": "api/authentication",
    "path": "/de/api/authentication",
    "area": "api",
    "areaTitle": "API",
    "page": "authentication",
    "pageTitle": "Anmeldung: Schlüssel und Sitzungen",
    "section": "Anmeldung: Schlüssel und Sitzungen",
    "anchor": "",
    "text": "Jeder Aufruf weist sich mit der Kopfzeile Authorization: Bearer <wert> aus; die API erkennt die Art des Ausweises am Präfix des Werts. Für Integrationen ist der API-Schlüssel der Regelweg. Diese Seite nennt die Präfixe, die Regeln für Schlüssel, ihren Lebenszyklus und wann eine Sitzung die bessere Wahl ist."
  },
  {
    "id": "api/authentication#die-präfixe",
    "path": "/de/api/authentication",
    "area": "api",
    "areaTitle": "API",
    "page": "authentication",
    "pageTitle": "Anmeldung: Schlüssel und Sitzungen",
    "section": "Die Präfixe",
    "anchor": "die-präfixe",
    "text": "Präfix Ausweis Für Integrationen ak_ API-Schlüssel einer Organisation ja — der Regelweg für den eigenen Server oat_ Zugriffstoken eines delegierten Zugangs ja — wenn ein Mensch Ihrer Anwendung Rechte gibt, siehe Delegierter Zugang mit OAuth 2.1 eyJ Zugriffstoken einer Sitzung für Oberflächen und die Kommandozeile mit Kennwort sct_ SCIM-Zugang einer Organisation nur für /scim/v2 , siehe Provisionierung mit SCIM 2.0 opk_ Betreiber-Schlüssel nur Plattformbetrieb rdr_ Abspieler-Token eines gekoppelten Geräts nein inv_ Einladungs-Token vor der ersten Anmeldung nein dev_ Geräteausweis bei der Erst-Registrierung nein ak_ und opk_ sind eigenständige Präfixe: opk_ beginnt nicht mit ak_ . Ein Schlüssel sagt damit selbst, welcher Sorte er angehört. Nicht zu verwechseln sind die Geheimwerte mit den Kennungen ihrer Datensätze ( akm_ , sctm_ , oac_ ): Die stehen in Listen und Protokollen, der Geheimwert selbst nur ein einziges Mal in der Antwort auf seine Ausstellung. Ein sct_ kommt auf keine andere Route als /scim/v2 — überall sonst wird er mit 403 abgewiesen. Umgekehrt erreicht kein ak_ und kein oat_ die SCIM-Fläche. Ein Wert mit unbekanntem Präfix gilt als unbekannt , nicht als anonym: Die Antwort ist 401 , nicht 404 . Ohne Kopfzeile erreichen Sie nur öffentliche Routen — etwa GET /v1/openapi.json , GET /health/ready oder POST /v1/auth/login . In der Referenz erkennen Sie sie an einer leeren security -Liste und dem Satz „Öffentlich — keine Anmeldung erforderlich“. Genau 46 der 443 Opera"
  },
  {
    "id": "api/authentication#der-api-schlüssel",
    "path": "/de/api/authentication",
    "area": "api",
    "areaTitle": "API",
    "page": "authentication",
    "pageTitle": "Anmeldung: Schlüssel und Sitzungen",
    "section": "Der API-Schlüssel",
    "anchor": "der-api-schlüssel",
    "text": "Regel Wert Form ak_ und 26 Zeichen, z. B. ak_01KQRWJRC0APKEY00000000001 Klartext erscheint genau einmal — in der Antwort auf POST /v1/me/api-keys (Feld plainKey ) Wiedererkennung Listen zeigen nur keyPrefix ( ak_ und 9 Zeichen) und die Kennung akm_… Lebensdauer läuft nicht nach 15 Minuten ab; expiresAt setzt ein Ablaufdatum, null heißt unbefristet Rechte eine Liste konkreter Rechte ( scope ); eine Klasse ( read-only , media-automation , content-authoring , playout-control , full ) wählt diese Liste nur aus, custom ist die freie Auswahl. Gesendet wird immer scope ; die Liste wird danach nicht nachgeführt Obergrenze ein Schlüssel kann nie mehr als sein Ersteller ( 403 ApiKey.ScopeEscalationBlocked ) Schlüsselfähigkeit nur Rechte, die der Katalog für Schlüssel vorsieht ( 422 ApiKey.ScopeNotKeyCapable ) Organisation ein Schlüssel gehört genau einer Organisation Welche Rechte ein Schlüssel tragen kann, sagt GET /v1/permission-catalog — ohne besonderes Recht, nur mit Anmeldung. Jeder Eintrag trägt appliesTo ; nur Rechte mit apiKey in dieser Liste sind schlüsselfähig. Im Kern-Katalog sind das heute 56 Rechte: Medien ( asset.* ), Szenen und Layout-Vorlagen ( scene.* , layout-template.* ), Playlisten und Programme ( playlist.* , schedule.* ), Standorte ( location.* ), die Displays ( fleet.player.list , fleet.player.read , fleet.player.update , fleet.player.command.issue ), die Web-Displays ( fleet.web-display.manage ), die Werbung (alle acht advertising.* ), die Nachweise ( analytics."
  },
  {
    "id": "api/authentication#lebenszyklus-eines-schlüssels",
    "path": "/de/api/authentication",
    "area": "api",
    "areaTitle": "API",
    "page": "authentication",
    "pageTitle": "Anmeldung: Schlüssel und Sitzungen",
    "section": "Lebenszyklus eines Schlüssels",
    "anchor": "lebenszyklus-eines-schlüssels",
    "text": "Vorgang Route Recht Bemerkung Anlegen POST /v1/me/api-keys identity.api-key.create Rumpf: name , scope (Pflicht, Liste schlüsselfähiger Rechte), expiresAt (Pflicht, null = unbefristet); optional originClass (Etikett der gewählten Klasse, Vorgabe custom ), rotationGraceHours (0–72, Vorgabe 24) und rateLimit (eigenes Kontingent in der Form 600/1m , null = die Vorgabe der Umgebung, siehe Drosselung ). Die Klasse wählt nur die Rechte-Liste aus, gesendet wird immer scope Eigene Schlüssel lesen GET /v1/me/api-keys Sitzung nur selbst erstellte; mit einem Schlüssel antwortet die Route 401 Alle Schlüssel der Organisation GET /v1/api-keys identity.api-key.list auch die ausgeschiedener Mitglieder; includeRevoked , createdById , search , sort , order Rotieren POST /v1/me/api-keys/{id}/rotate identity.api-key.create Rumpf graceHours 0–72; der Nachfolger übernimmt Name, Rechte und Kontingent, der alte Schlüssel gilt bis oldRevokesAt Widerrufen DELETE /v1/me/api-keys/{id} identity.api-key.revoke Rumpf reason : owner-rotation (nicht mehr gebraucht oder ersetzt) oder compromised (vermutlich in fremden Händen); sofort und nicht umkehrbar Die Antwort — beide Schlüsselobjekte hier auf die Felder gekürzt, die eine Integration braucht: Im Portal finden Sie dieselben Vorgänge unter Einstellungen › Organisation › API-Schlüssel mit den Schaltflächen Schlüssel erstellen , Rotieren und Widerrufen ; die Gnadenfrist heißt dort Alter Schlüssel gültig bis mit den Stufen Sofort ungültig , 1 Stunde , 24 Stun"
  },
  {
    "id": "api/authentication#die-sitzung",
    "path": "/de/api/authentication",
    "area": "api",
    "areaTitle": "API",
    "page": "authentication",
    "pageTitle": "Anmeldung: Schlüssel und Sitzungen",
    "section": "Die Sitzung",
    "anchor": "die-sitzung",
    "text": "Eine Sitzung ist der Weg für Oberflächen und für Menschen an der Kommandozeile — dort heißt sie Ausweisart password . Sie braucht Auffrischung, ein Schlüssel nicht. Schritt Route Rumpf Ergebnis Anmelden POST /v1/auth/login email , password , audience ( tenant ), deviceFingerprint 201 mit outcome : session , mfa-required oder organization-choice Zweiter Faktor POST /v1/auth/mfa/verify mfaSessionId , code outcome: session Auffrischen POST /v1/auth/refresh kein Rumpf; das Cookie reist mit neues Zugriffstoken Bestandteil Regel Zugriffstoken accessToken im Rumpf, expiresAt nennt den Ablauf; Lebensdauer 15 Minuten; reist als Authorization: Bearer eyJ… Auffrisch-Cookie refresh_token , httpOnly , Pfad /v1/auth , SameSite=Lax , 7 Tage; nur POST /v1/auth/refresh liest es und setzt es neu Cookie-Regel Ein Cookie wird nur gesetzt, wenn die Antwort tatsächlich eine Sitzung ist ( outcome: session ) Andere Ursprünge Browser-Clients senden credentials: 'include' , sonst bleibt das Cookie zu Hause Wirt des Cookies Das Cookie trägt kein Domain -Attribut und gehört dem Wirt, der es setzt: Anmelden und Auffrischen gehen an denselben Wirt. Hat Ihre Umgebung einen Anmelde-Wirt ( auth.<ihre-domain> ), ist es dieser Fachliche Anfragen werden nicht über Cookies authentisiert — der Bearer-Kopf ist immer der Ausweis. Gehört ein Konto zu mehreren Organisationen, endet die Anmeldung in organization-choice ; ein Schlüssel kennt diese Frage nicht, er gehört genau einer Organisation. Die Unternehmens-Anmeld"
  },
  {
    "id": "api/authentication#schlüssel-oder-sitzung",
    "path": "/de/api/authentication",
    "area": "api",
    "areaTitle": "API",
    "page": "authentication",
    "pageTitle": "Anmeldung: Schlüssel und Sitzungen",
    "section": "Schlüssel oder Sitzung?",
    "anchor": "schlüssel-oder-sitzung",
    "text": "Situation Wahl Server-zu-Server, Skripte, CI Schlüssel Rechte aus Medien, Szenen, Playlisten, Programmen, Standorten, Werbung und Nachweisen Schlüssel Geräte steuern, Konten oder Webhooks verwalten Sitzung — diese Rechte trägt kein Schlüssel Kurz etwas nachsehen, ohne einen Schlüssel anzulegen Sitzung über die Kommandozeile mit Ausweisart password Ihre Anwendung läuft bei fremden Kunden und soll deren Bestand sehen delegierter Zugang oat_… — ein Mensch willigt ein, statt einen Schlüssel herauszugeben Konten aus einem Personalverzeichnis pflegen SCIM-Zugang sct_…"
  },
  {
    "id": "api/authentication#weiterführendes",
    "path": "/de/api/authentication",
    "area": "api",
    "areaTitle": "API",
    "page": "authentication",
    "pageTitle": "Anmeldung: Schlüssel und Sitzungen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Einen API-Schlüssel erzeugen Delegierter Zugang mit OAuth 2.1 Provisionierung mit SCIM 2.0 Fehlercodes: Konten und Sitzungen Handbuch: Organisation"
  },
  {
    "id": "api/error-format",
    "path": "/de/api/error-format",
    "area": "api",
    "areaTitle": "API",
    "page": "error-format",
    "pageTitle": "Das Fehlerformat",
    "section": "Das Fehlerformat",
    "anchor": "",
    "text": "Jeder Fehler der API kommt in demselben Rumpf zurück: code , message und traceId immer, weitere Felder nach Lage. Diese Seite nennt die Felder, die Regel, nach der aus einem Code der HTTP-Status wird, die sieben zentralen Antworten der Referenz und die RFC-9457-Form."
  },
  {
    "id": "api/error-format#der-fehlerrumpf",
    "path": "/de/api/error-format",
    "area": "api",
    "areaTitle": "API",
    "page": "error-format",
    "pageTitle": "Das Fehlerformat",
    "section": "Der Fehlerrumpf",
    "anchor": "der-fehlerrumpf",
    "text": "Feld Wann Inhalt code immer der stabile Fehlercode in der Form Bereich.Ursache , Muster ^[A-Z][a-zA-Z]+\\.[A-Z][a-zA-Z0-9]+$ message immer Kurzbeschreibung für Menschen, 1–1000 Zeichen, kann sich ändern traceId immer die Spur dieses Aufrufs: trc_ und 26 Zeichen, nie leer und identisch mit der Kopfzeile X-Trace-Id details nach Lage Zusatzangaben ohne personenbezogene Daten: issues bei Eingabefehlern, expectedVersion und actualVersion bei Versionskonflikten. Fehlt ganz, wenn es nichts gibt correlationId nach Lage Kennung eines zusammenhängenden Vorgangs, cor_… , bis 128 Zeichen retryAfterSeconds bei nachgelagerten Diensten ganze Sekunden bis zum nächsten Versuch. Geht zusätzlich als Kopfzeile Retry-After hinaus externalService bei nachgelagerten Diensten Name des Dienstes, der nicht verwertbar geantwortet hat stack nie im Produktivbetrieb nur in Entwicklungsumgebungen bei Serverfehlern Zwei Regeln für jeden Client: Schalten Sie auf code , nie auf message . Und nennen Sie bei Rückfragen die traceId — sie ist der Schlüssel, mit dem der Betrieb den Aufruf wiederfindet."
  },
  {
    "id": "api/error-format#wie-aus-dem-code-der-http-status-wird",
    "path": "/de/api/error-format",
    "area": "api",
    "areaTitle": "API",
    "page": "error-format",
    "pageTitle": "Das Fehlerformat",
    "section": "Wie aus dem Code der HTTP-Status wird",
    "anchor": "wie-aus-dem-code-der-http-status-wird",
    "text": "Der Status ist nicht frei gesetzt, sondern deterministisch aus dem Code abgeleitet. Die Auflösung prüft in fester Reihenfolge, und der erste Treffer gewinnt. Stufe Regel Beispiel 1 Exakte Zuordnung des einzelnen Codes Validation.IdempotencyKeyRequired → 409, Http.BadRequest → 400 2 Zuordnung des Fachbereichs Billing.LicensesExhausted → 402, Tenant.Frozen → 403, Signup.TicketInvalid → 401 3 Konvention des Namensraums Validation.* → 422, Auth.* → 401, Permission.* → 403, *.NotFound → 404, Conflict.* → 409, RateLimit.* → 429, Payload.* → 413, Integration.* → 502 4 Sonst 500 Deshalb tragen die meisten Serverfehler keine eigene Zuordnung: Sie fallen bewusst auf 500 durch. Wo ein Fachcode von der Konvention abweicht, ist das Absicht und auf der jeweiligen Code-Seite begründet."
  },
  {
    "id": "api/error-format#die-sieben-zentralen-antworten",
    "path": "/de/api/error-format",
    "area": "api",
    "areaTitle": "API",
    "page": "error-format",
    "pageTitle": "Das Fehlerformat",
    "section": "Die sieben zentralen Antworten",
    "anchor": "die-sieben-zentralen-antworten",
    "text": "Die Referenz beschreibt sieben Antworten zentral. Sie bedeuten auf jeder Operation dasselbe. Antwort Status Bedeutung Unauthorized 401 nicht oder ungültig angemeldet PermissionDenied 403 angemeldet, aber ohne das nötige Recht NotFound 404 die Ressource existiert nicht oder ist für Sie nicht sichtbar Conflict 409 gleichzeitige Änderung oder Verstoß gegen eine Eindeutigkeit ValidationFailed 422 die Anfrage verletzt das Schema oder eine fachliche Regel RateLimit 429 zu viele Anfragen, Retry-After nennt die Wartezeit IntegrationError 502 ein nachgelagerter Dienst antwortete nicht verwertbar"
  },
  {
    "id": "api/error-format#rfc-9457-problem-details",
    "path": "/de/api/error-format",
    "area": "api",
    "areaTitle": "API",
    "page": "error-format",
    "pageTitle": "Das Fehlerformat",
    "section": "RFC 9457: Problem Details",
    "anchor": "rfc-9457-problem-details",
    "text": "Senden Sie Accept: application/problem+json , antwortet die API mit Content-Type: application/problem+json und einem ProblemDetails -Dokument statt des Standardrumpfs. Feld Wann Inhalt type immer die Adressform des Codes title immer Kurztitel, bis 200 Zeichen status immer der HTTP-Status als Zahl detail immer die Beschreibung, bis 1000 Zeichen code immer gesetzt derselbe Fehlercode wie im Standardrumpf instance nach Lage der angefragte Pfad errorDetails nach Lage dieselben Zusatzangaben wie details traceId , correlationId , retryAfterSeconds nach Lage wie im Standardrumpf Einen stack gibt es in dieser Form nicht."
  },
  {
    "id": "api/error-format#wo-die-codes-stehen",
    "path": "/de/api/error-format",
    "area": "api",
    "areaTitle": "API",
    "page": "error-format",
    "pageTitle": "Das Fehlerformat",
    "section": "Wo die Codes stehen",
    "anchor": "wo-die-codes-stehen",
    "text": "Der Katalog umfasst 612 Codes, verteilt auf sieben Seiten: Seite Bereiche Auf jeder Route möglich Anmeldung, Rechte, Eingabeprüfung, Konflikte, Drosselung, Nutzlast, HTTP, Betreiberrouten Konten und Sitzungen Konten, Schlüssel, Geräte, Anmeldung, zweiter Faktor Organisation, Zugang, Beitritt Stammdaten, Rollen, Einladungen, Registrierung, Speicher-Verbindungen Abrechnung Paket, Lizenzen, Zahlungsmittel, Rechnungen Medien, Szenen, Programme, Apps Medien, Szenen, Playlisten, Programme, App-Katalog, Marktplatz, App-Laufzeit, Nachrichten-Quellen, Werbung Geräte, Wiedergabe, Meldungen, Betrieb Flotte, Wiedergabe, Echtzeit, Webhooks, Auswertung, Prüfprotokoll, Betriebskonsole Serverfehler (5xx) Invarianten, Start, Datenbank, nachgelagerte Dienste"
  },
  {
    "id": "api/error-format#weiterführendes",
    "path": "/de/api/error-format",
    "area": "api",
    "areaTitle": "API",
    "page": "error-format",
    "pageTitle": "Das Fehlerformat",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Fehlercodes: auf jeder Route möglich Was eine Antwort enthält Gleichzeitige Änderungen: ETag und If-Match"
  },
  {
    "id": "api/etag-and-if-match",
    "path": "/de/api/etag-and-if-match",
    "area": "api",
    "areaTitle": "API",
    "page": "etag-and-if-match",
    "pageTitle": "Gleichzeitige Änderungen: ETag und If-Match",
    "section": "Gleichzeitige Änderungen: ETag und If-Match",
    "anchor": "",
    "text": "Nach dieser Anleitung ändern Sie eine versionierte Ressource so, dass eine gleichzeitige Änderung eines anderen Clients nicht verloren geht: Sie lesen das Kennzeichen aus der Kopfzeile ETag , schicken es als If-Match zurück und lösen einen 409 -Konflikt sauber auf."
  },
  {
    "id": "api/etag-and-if-match#wie-die-versionsprüfung-funktioniert",
    "path": "/de/api/etag-and-if-match",
    "area": "api",
    "areaTitle": "API",
    "page": "etag-and-if-match",
    "pageTitle": "Gleichzeitige Änderungen: ETag und If-Match",
    "section": "Wie die Versionsprüfung funktioniert",
    "anchor": "wie-die-versionsprüfung-funktioniert",
    "text": "Fünf Ressourcen führen ein Feld version : Szenen, Playlisten, Programme (Zeitpläne), Rollen und Displays. Bei einer Änderung senden Sie den gelesenen Wert als Kopfzeile If-Match . Hat inzwischen jemand anderes geschrieben, antwortet die API mit 409 Conflict.OptimisticLock , und Sie lesen neu. Die Version ist der ETag. Sie steht zweimal in der Antwort: als Feld version im Rumpf und als Kopfzeile ETag: \"7\" — ein starkes Kennzeichen, kein W/ . Beide Auftritte tragen denselben Wert; welchen Sie lesen, ist Ihre Wahl. Die Kopfzeile ist auch für Browser-Clients auf anderen Ursprüngen freigegeben. ETag senden die Lese- und die Änderungsroute derselben Ressourcen: GET und PATCH auf /v1/scenes/{id} , /v1/playlists/{id} , /v1/schedules/{id} , /v1/roles/{id} und /v1/players/{id} sowie POST /v1/scenes/{id}/versions/{vid}/revert . Trägt eine Antwort ausnahmsweise keine Fassung, bleibt die Kopfzeile weg, statt eine Null zu behaupten."
  },
  {
    "id": "api/etag-and-if-match#schritte",
    "path": "/de/api/etag-and-if-match",
    "area": "api",
    "areaTitle": "API",
    "page": "etag-and-if-match",
    "pageTitle": "Gleichzeitige Änderungen: ETag und If-Match",
    "section": "Schritte",
    "anchor": "schritte",
    "text": "Lesen Sie die Ressource und merken Sie sich die Fassung — aus der Kopfzeile oder aus dem Rumpf: Senden Sie die Änderung mit If-Match und genau diesem Wert: Die Antwort ist die Szene mit der neuen Version (hier auf vier Felder gekürzt): Ein inhaltsgleicher Patch lässt die Version stehen. Kommt 409 zurück, hat jemand zwischen Schritt 1 und 2 geschrieben: Lesen Sie die Ressource neu (Schritt 1), wenden Sie Ihre Änderung auf den frischen Stand an und senden Sie erneut mit der neuen Version. Wiederholen Sie den Patch nicht blind mit der alten Version — das endet wieder in 409 ."
  },
  {
    "id": "api/etag-and-if-match#optionen",
    "path": "/de/api/etag-and-if-match",
    "area": "api",
    "areaTitle": "API",
    "page": "etag-and-if-match",
    "pageTitle": "Gleichzeitige Änderungen: ETag und If-Match",
    "section": "Optionen",
    "anchor": "optionen",
    "text": "If-Match nimmt drei Schreibweisen desselben Werts an: Kopfzeile Wirkung If-Match: \"7\" die Regelform: das Kennzeichen, wie die Antwort es gesendet hat If-Match: W/\"7\" die schwache Form, ebenfalls angenommen If-Match: 7 die nackte Zahl aus dem Rumpf — der Bestand, damit kein laufender Client bricht If-Match: 0 oder kein Zahlenwert 422 Validation.Failed („If-Match must be a positive resource version“) kein If-Match auf Routen, die die Kopfzeile erzwingen: 422 Validation.Failed („If-Match header required for this operation“) Displays fehlende Kopfzeile endet in 409 Conflict.OptimisticLock (Versionsvergleich gegen 0), nie in 422 Fünf Operationen erzwingen die Kopfzeile und führen sie in der Referenz als Pflicht-Parameter: PATCH /v1/scenes/{id} , POST /v1/scenes/{id}/versions/{vid}/revert , PATCH /v1/playlists/{id} , PATCH /v1/schedules/{id} und PATCH /v1/roles/{id} . PATCH /v1/players/{id} nimmt sie an, verlangt sie aber nicht: Ohne Kopfzeile prüft die Route gegen Fassung 0 und läuft damit in denselben Konflikt wie eine veraltete Angabe. Bei Programmen liegt version unter schedule.version , bei Rollen unter role.version , bei Displays unter player.version . Was details bei einem Konflikt trägt, hängt von der Ressource ab: Szenen nennen sceneId und expectedVersion ; Rollen resource , resourceId , expectedVersion und actualVersion ; Displays playerId , expectedVersion und actualVersion ; Playlisten und Programme entity , id und expectedVersion . Kommandozeile. szenera scene update <"
  },
  {
    "id": "api/etag-and-if-match#ergebnis-prüfen",
    "path": "/de/api/etag-and-if-match",
    "area": "api",
    "areaTitle": "API",
    "page": "etag-and-if-match",
    "pageTitle": "Gleichzeitige Änderungen: ETag und If-Match",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "Die Antwort auf den Patch ist 200 mit der Ressource, und version ist um eins gestiegen (oder unverändert, wenn der Patch nichts geändert hat). Ein zweiter Patch mit dem alten If-Match antwortet 409 Conflict.OptimisticLock ."
  },
  {
    "id": "api/etag-and-if-match#wenn-etwas-nicht-klappt",
    "path": "/de/api/etag-and-if-match",
    "area": "api",
    "areaTitle": "API",
    "page": "etag-and-if-match",
    "pageTitle": "Gleichzeitige Änderungen: ETag und If-Match",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung 409 Conflict.OptimisticLock Ein anderer Client hat zwischen Lesen und Schreiben geändert. Neu lesen, Änderung erneut anwenden, mit neuer Version senden. 422 Validation.Failed mit „If-Match header required for this operation“ Eine der fünf Routen oben erzwingt die Kopfzeile. If-Match mit dem gelesenen Kennzeichen mitsenden. 422 Validation.Failed mit „If-Match must be a positive resource version“ Der Wert ist keine Zahl ab 1 — etwa ein selbst gebildetes Kennzeichen aus mehreren Teilen. Das Kennzeichen aus der Antwort senden, \"7\" , W/\"7\" oder 7 . 409 Conflict.OptimisticLock bei einem Display, obwohl niemand geschrieben hat Die Kopfzeile fehlte; die Route vergleicht dann gegen Fassung 0. If-Match mit dem gelesenen Kennzeichen mitsenden. Sie erwarten 412 Es gibt keinen 412 — der Konflikt ist immer 409 . Auf 409 und den Code Conflict.OptimisticLock schalten. Kein ETag -Kopf in der Antwort Die Route ist nicht versioniert, oder die Antwort trug ausnahmsweise keine Fassung. version aus dem Rumpf lesen; fehlt auch die, kennt die Ressource keine Fassung."
  },
  {
    "id": "api/etag-and-if-match#weiterführendes",
    "path": "/de/api/etag-and-if-match",
    "area": "api",
    "areaTitle": "API",
    "page": "etag-and-if-match",
    "pageTitle": "Gleichzeitige Änderungen: ETag und If-Match",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Idempotenz von Anfang an Das Fehlerformat Befehle für Ihre Organisation"
  },
  {
    "id": "api/rate-limits",
    "path": "/de/api/rate-limits",
    "area": "api",
    "areaTitle": "API",
    "page": "rate-limits",
    "pageTitle": "Drosselung und Rate-Limit-Kopfzeilen",
    "section": "Drosselung und Rate-Limit-Kopfzeilen",
    "anchor": "",
    "text": "Zwei Dinge werden gedrosselt, aus zwei Gründen: die missbrauchsnahen Routen — Anmeldung, Anmelde-Link, Registrierung, Passwort-Zurücksetzen und die Rundläufe der Anbieter-Anmeldung — tragen jeweils eine eigene Grenze, und jeder Aufruf mit einem API-Schlüssel zählt zusätzlich gegen ein Kontingent, das dem Schlüssel gehört. Diese Seite nennt die Kopfzeilen, beide Grenzen und die richtige Reaktion auf 429 ."
  },
  {
    "id": "api/rate-limits#die-kopfzeilen",
    "path": "/de/api/rate-limits",
    "area": "api",
    "areaTitle": "API",
    "page": "rate-limits",
    "pageTitle": "Drosselung und Rate-Limit-Kopfzeilen",
    "section": "Die Kopfzeilen",
    "anchor": "die-kopfzeilen",
    "text": "Jede Antwort, auf die eine Grenze wirkt, liefert den Stand des Fensters mit: Kopfzeile Bedeutung X-RateLimit-Limit erlaubte Anfragen im Fenster insgesamt X-RateLimit-Remaining davon noch offen X-RateLimit-Reset Zeitpunkt, an dem das Fenster neu beginnt, in Unix-Sekunden Retry-After Sekunden bis zum nächsten erlaubten Versuch — nur bei Überschreitung ( 429 ) Alle vier sind für Browser-Clients auf anderen Ursprüngen lesbar, ebenso X-Trace-Id , X-Correlation-Id und Server-Timing ."
  },
  {
    "id": "api/rate-limits#die-antwort-bei-überschreitung",
    "path": "/de/api/rate-limits",
    "area": "api",
    "areaTitle": "API",
    "page": "rate-limits",
    "pageTitle": "Drosselung und Rate-Limit-Kopfzeilen",
    "section": "Die Antwort bei Überschreitung",
    "anchor": "die-antwort-bei-überschreitung",
    "text": "Status 429 , dazu Retry-After . Ein zweiter Code desselben Bereichs, RateLimit.GlobalCircuitBreaker (ebenfalls 429 ), zeigt eine plattformweite Überlast-Sicherung an; die Reaktion ist dieselbe."
  },
  {
    "id": "api/rate-limits#die-grenzen-der-anmelde--und-registrierungsrouten",
    "path": "/de/api/rate-limits",
    "area": "api",
    "areaTitle": "API",
    "page": "rate-limits",
    "pageTitle": "Drosselung und Rate-Limit-Kopfzeilen",
    "section": "Die Grenzen der Anmelde- und Registrierungsrouten",
    "anchor": "die-grenzen-der-anmelde--und-registrierungsrouten",
    "text": "Die Grenzen gelten je Fenster und je Zählmerkmal: „IP“ zählt je Absenderadresse, „IP+Adresse“ je Absenderadresse und E-Mail-Adresse im Rumpf. Route Grenze POST /v1/auth/identify 10 je Minute (IP+Adresse) und 40 je 10 Minuten (IP) POST /v1/auth/login 5 je Minute (IP+Adresse) POST /v1/auth/login/choose-organization 10 je Minute (IP) POST /v1/auth/mfa/verify , POST /v1/auth/mfa/recovery-code 5 je Minute (IP) POST /v1/auth/login/magic-link 5 je Minute (IP+Adresse) POST /v1/auth/login/magic-link/consume 5 je Minute (IP) POST /v1/auth/password-reset/request 5 je Minute (IP+Adresse) POST /v1/auth/password-reset/consume 5 je Minute (IP) POST /v1/auth/onboarding/consume 5 je Minute (IP) GET /v1/auth/login/sso/{providerId} 20 je Minute (IP) GET|POST /v1/auth/login/sso/callback 30 je Minute (IP) GET /v1/auth/login/social 60 je Minute (IP) GET /v1/auth/login/social/{provider} 20 je Minute (IP) GET|POST /v1/auth/login/social/callback 30 je Minute (IP) POST /v1/auth/login/exchange 10 je Minute (IP) POST /v1/auth/sso-recovery/consume 5 je Minute (IP) GET /v1/auth/signup 60 je Minute (IP) POST /v1/auth/signup/start 3 je 10 Minuten (IP+Adresse) und 10 je 24 Stunden (IP) POST /v1/auth/signup/verify , POST /v1/auth/signup/social 10 je Minute (IP) POST /v1/auth/signup/complete , POST /v1/auth/signup/join 5 je Minute (IP) POST /v1/tenants/me/sso/{providerId}/domains/{domain}/verify 10 je Minute (IP) Weitere öffentliche und sitzungsgebundene Routen haben eigene Fenster — etwa die Einladungs-Annahm"
  },
  {
    "id": "api/rate-limits#das-kontingent-je-schlüssel",
    "path": "/de/api/rate-limits",
    "area": "api",
    "areaTitle": "API",
    "page": "rate-limits",
    "pageTitle": "Drosselung und Rate-Limit-Kopfzeilen",
    "section": "Das Kontingent je Schlüssel",
    "anchor": "das-kontingent-je-schlüssel",
    "text": "Ein Aufruf mit einem Schlüssel ak_… zählt gegen ein Kontingent, das dem Schlüssel gehört — auf jeder Route, auch auf den lesenden ohne eigene Grenze. Der Grund ist die Bauart eines Automaten: Ein Mensch klickt, ein Skript fragt in einer Schleife, und gerade die billigen Lese-Wege legen einen Stapel am schnellsten lahm. Was Wert Vorgabe ein Wert der Umgebung in der Form <Anzahl>/<Fenster> , etwa 600/1m ; das Fenster zählt in s , m oder h Je Schlüssel beim Anlegen überschreibbar (Feld rateLimit in POST /v1/me/api-keys ; null heißt „die Vorgabe der Umgebung“) — eine Rotation übernimmt den Wert Gezählt wird je Schlüssel, nicht je Absenderadresse und nicht je Organisation Kopfzeilen dieselben vier wie oben, an jeder Antwort Bei Überschreitung 429 RateLimit.Exceeded mit Retry-After Nicht betroffen sind Aufrufe aus einer angemeldeten Sitzung, Geräte, Abspieler und Betreiber-Schlüssel: Sie haben eigene Grenzen. Wirken auf einen Aufruf mehrere Regeln — etwa die Grenze der Anmelderoute und das Kontingent des Schlüssels —, zeigen die Kopfzeilen die knappste, und die zuerst gerissene Regel bestimmt den Fehlercode. Was ein Client daraus macht: X-RateLimit-Remaining mitlesen und die eigene Schleife bremsen, bevor die Null erreicht ist. Ein Import, der zehntausend Medien anlegt, läuft mit einer kleinen Pause je Aufruf schneller durch als einer, der gegen die Wand fährt und wartet."
  },
  {
    "id": "api/rate-limits#die-richtige-reaktion",
    "path": "/de/api/rate-limits",
    "area": "api",
    "areaTitle": "API",
    "page": "rate-limits",
    "pageTitle": "Drosselung und Rate-Limit-Kopfzeilen",
    "section": "Die richtige Reaktion",
    "anchor": "die-richtige-reaktion",
    "text": "Regel Warum Retry-After abwarten, dann genau einmal wiederholen Jeder Versuch davor zählt ins Fenster und verlängert die Sperre. X-RateLimit-Remaining beobachten und vor der Null bremsen Ein Client, der das Fenster kennt, sieht nie einen 429 . Nicht hämmern Wiederholungen im Sekundentakt füllen das Fenster, statt es zu leeren. Die Kommandozeile beendet einen Aufruf mit 429 mit Exit-Code 8 ( rate-limited ) — siehe Ausgabe als JSON und Exit-Codes ."
  },
  {
    "id": "api/rate-limits#weiterführendes",
    "path": "/de/api/rate-limits",
    "area": "api",
    "areaTitle": "API",
    "page": "rate-limits",
    "pageTitle": "Drosselung und Rate-Limit-Kopfzeilen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Das Fehlerformat Ausgabe als JSON und Exit-Codes"
  },
  {
    "id": "api/pagination",
    "path": "/de/api/pagination",
    "area": "api",
    "areaTitle": "API",
    "page": "pagination",
    "pageTitle": "Blättern: limit, cursor, items, nextCursor",
    "section": "Blättern: limit, cursor, items, nextCursor",
    "anchor": "",
    "text": "Listen der API werden über einen Cursor geblättert: limit begrenzt die Seite, cursor setzt sie fort, die Antwort führt items und — solange weitere Datensätze folgen — nextCursor . Diese Seite nennt die Grenzen, die vier Ausnahmen mit page und die Schleife, die alle Seiten einsammelt."
  },
  {
    "id": "api/pagination#cursor-blättern",
    "path": "/de/api/pagination",
    "area": "api",
    "areaTitle": "API",
    "page": "pagination",
    "pageTitle": "Blättern: limit, cursor, items, nextCursor",
    "section": "Cursor-Blättern",
    "anchor": "cursor-blättern",
    "text": "Teil Regel limit (Abfrageparameter) ganze Zahl 1–200, Vorgabe 50 cursor (Abfrageparameter) der Wert von nextCursor der vorigen Seite; beim ersten Aufruf weglassen items (Antwort) die Datensätze dieser Seite nextCursor (Antwort) die Marke der nächsten Seite; fehlt auf der letzten Seite total (Antwort) die Gesamtzahl, wo die Route sie kennt; sonst fehlt das Feld Der Cursor ist stabil bei Einfügungen: Ein Datensatz, der während des Blätterns dazukommt, verschiebt keine Seite. 29 Operationen der Referenz nehmen cursor , 34 nehmen limit . Die Antwort, je Szene auf vier Felder gekürzt:"
  },
  {
    "id": "api/pagination#gefilterte-listen-25-und-100",
    "path": "/de/api/pagination",
    "area": "api",
    "areaTitle": "API",
    "page": "pagination",
    "pageTitle": "Blättern: limit, cursor, items, nextCursor",
    "section": "Gefilterte Listen: 25 und 100",
    "anchor": "gefilterte-listen-25-und-100",
    "text": "Die gefilterten Listen der Verwaltung — Organisationen, Konten, Geräte, Rollen, Erteilungen, Schlüssel — deckeln niedriger, weil jede Zeile mehr trägt: Vorgabe 25, höchstens 100. Ein limit darüber antwortet 422 Validation.Failed . Die Referenz nennt an limit heute keine Grenze; es gelten 200/50 für Cursor-Listen und 100/25 für die gefilterten Listen der Verwaltung (Organisationen, Konten, Geräte, Rollen, Erteilungen, Schlüssel)."
  },
  {
    "id": "api/pagination#die-ausnahme-page",
    "path": "/de/api/pagination",
    "area": "api",
    "areaTitle": "API",
    "page": "pagination",
    "pageTitle": "Blättern: limit, cursor, items, nextCursor",
    "section": "Die Ausnahme: page",
    "anchor": "die-ausnahme-page",
    "text": "Vier Listen blättern mit Seitenzahlen statt mit einem Cursor, weil eine Seitenleiste („Seite 3 von 12“) die Gesamtzahl braucht: Route Inhalt GET /v1/devices/{id}/commands Befehle eines Geräts GET /v1/devices/{id}/events Betriebsprotokoll eines Geräts GET /v1/players/{id}/commands Befehle eines Displays GET /v1/players/{id}/events Betriebsprotokoll eines Displays Teil Regel page ganze Zahl ab 1, Vorgabe 1 limit 1–200, Vorgabe 50 Antwort items , total , page , pageSize — alle vier Pflicht page und pageSize reisen zurück, obwohl Sie sie geschickt haben: Sie sind die Zahlen, gegen die total gilt."
  },
  {
    "id": "api/pagination#kleine-bestände",
    "path": "/de/api/pagination",
    "area": "api",
    "areaTitle": "API",
    "page": "pagination",
    "pageTitle": "Blättern: limit, cursor, items, nextCursor",
    "section": "Kleine Bestände",
    "anchor": "kleine-bestände",
    "text": "Kleine, nicht blätterbare Bestände antworten mit einer einfachen Liste ohne items -Hülle. Die Antwortform steht am Endpunkt in der Referenz; ein Client, der auf items prüft, erkennt den Unterschied am Aufbau."
  },
  {
    "id": "api/pagination#alle-seiten-einsammeln",
    "path": "/de/api/pagination",
    "area": "api",
    "areaTitle": "API",
    "page": "pagination",
    "pageTitle": "Blättern: limit, cursor, items, nextCursor",
    "section": "Alle Seiten einsammeln",
    "anchor": "alle-seiten-einsammeln",
    "text": "Die Schleife endet, sobald nextCursor fehlt. Prüfen Sie nicht auf einen leeren items -Block als Ende — die letzte Seite kann voll sein."
  },
  {
    "id": "api/pagination#kommandozeile",
    "path": "/de/api/pagination",
    "area": "api",
    "areaTitle": "API",
    "page": "pagination",
    "pageTitle": "Blättern: limit, cursor, items, nextCursor",
    "section": "Kommandozeile",
    "anchor": "kommandozeile",
    "text": "Jeder Listen-Befehl von szenera kennt dieselben drei Flaggen: Flagge Wirkung --limit=<anzahl> höchstens so viele Zeilen je Seite; ein Wert außerhalb von 1 bis zum Deckel der Route wird lokal abgewiesen (Exit 1), bevor ein Aufruf hinausgeht --cursor=<marke> weiterlesen ab dieser Marke; die Kopfzeile eines Ergebnisses nennt sie: „weiter mit --cursor=…“ --all allen Seiten folgen, bis keine mehr kommt; die zusammengehängte Ausgabe trägt kein nextCursor Wie Sie daraus ein Skript bauen, steht unter Skripte und CI ."
  },
  {
    "id": "api/pagination#weiterführendes",
    "path": "/de/api/pagination",
    "area": "api",
    "areaTitle": "API",
    "page": "pagination",
    "pageTitle": "Blättern: limit, cursor, items, nextCursor",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Was eine Antwort enthält Skripte und CI"
  },
  {
    "id": "api/oauth-delegated-access",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Delegierter Zugang mit OAuth 2.1",
    "anchor": "",
    "text": "Nach dieser Seite lässt Ihre Anwendung einen Menschen entscheiden, statt einen Schlüssel zu verlangen: Sie registriert sich selbst, schickt die Person zur Einwilligung, tauscht den Code mit PKCE gegen ein Zugriffstoken oat_… und erneuert oder widerruft es. Das Passwort der Person sieht sie dabei nie."
  },
  {
    "id": "api/oauth-delegated-access#wann-dieser-weg-der-richtige-ist",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Wann dieser Weg der richtige ist",
    "anchor": "wann-dieser-weg-der-richtige-ist",
    "text": "Situation Wahl Ihr eigener Server spricht mit Szenera ein API-Schlüssel ak_… — er gehört der Organisation und hängt an keinem Konto Ihre Anwendung läuft bei fremden Kunden und soll deren Bestand sehen dieser Weg: Ein Mensch dort willigt ein, und Ihre Anwendung bekommt ein Token Ein KI-Assistent soll in Ihrem Namen arbeiten dieser Weg, meist über den MCP-Dienst Ein delegierter Zugang trägt immer zwei Seiten: die Anwendung, die handelt, und das Konto, in dessen Auftrag. Zur Laufzeit gilt die Schnittmenge aus beidem. Verliert die Person ein Recht, verliert Ihre Anwendung es im selben Augenblick mit."
  },
  {
    "id": "api/oauth-delegated-access#die-adressen",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Die Adressen",
    "anchor": "die-adressen",
    "text": "Ihre Anwendung muss genau eine Adresse fest kennen: die Wegbeschreibung. Alles Übrige steht darin, und zwar absolut. Aussteller und Endpunkte nennen künftig die Metadaten von auth.<ihre-domain> , wenn Ihre Umgebung diesen Wirt hat. Tragen Sie darum keinen Endpunkt von Hand ein, sondern lesen Sie ihn aus der Beschreibung, die Sie dort abrufen. Die Adressen an der Basis-Adresse der API bleiben bis auf Weiteres erreichbar. Route Zweck GET /.well-known/oauth-authorization-server Wegbeschreibung des Autorisierungs-Servers (RFC 8414) GET /.well-known/oauth-protected-resource Wegbeschreibung der geschützten Ressource (RFC 9728) POST /oauth/register Registrierung eines öffentlichen Clients (RFC 7591) GET /oauth/authorize prüft die Anfrage und führt zur Einwilligungsseite POST /oauth/token Code oder Erneuerungs-Token gegen eine Ausgabe tauschen POST /oauth/revoke ein Token zurücknehmen (RFC 7009) GET /v1/me/connected-apps die verbundenen Anwendungen des eigenen Kontos DELETE /v1/me/connected-apps/{clientId} einer Anwendung alles nehmen, was dieses Konto ihr gab Die ersten sechs Adressen tragen kein /v1 : Ihre Pfade schreiben die Standards wörtlich vor, und ein fremder Client sucht dort und nirgends sonst. Was hinter der Einwilligung liegt, steht regulär unter /v1 — siehe Basis-URL, /v1 und Stabilität ."
  },
  {
    "id": "api/oauth-delegated-access#schritte",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Schritte",
    "anchor": "schritte",
    "text": "Holen Sie die Wegbeschreibung. Sie nennt die Endpunkte, die unterstützten Verfahren und unter scopes_supported jedes Recht, das ein delegierter Zugang tragen kann. Registrieren Sie Ihren Client — einmalig, ohne Anmeldung, ohne Geheimnis. Es gibt nur öffentliche Clients: token_endpoint_auth_method ist immer none . Die Registrierung ist auf 10 Aufrufe je Stunde und Absenderadresse begrenzt. Legen Sie die Kennung ab und registrieren Sie nicht bei jedem Start neu. Erzeugen Sie ein Prüfwort und seine Prüfsumme. Das Verfahren ist ausschließlich S256 ; plain gibt es nicht. Schicken Sie die Person in den Browser. Sie landet auf der Einwilligungsseite des Portals und sieht dort den Namen Ihrer Anwendung, das Rücksprung-Ziel und die erbetenen Rechte — gebündelt zu Bereichen mit je einem Satz und einem Schalter. Prüfen Sie beim Rücksprung state gegen den gesendeten Wert, bevor Sie den Code einlösen. Tauschen Sie den Code. Der Rumpf ist formularkodiert , nicht JSON. Arbeiten Sie damit wie mit jedem anderen Bearer: Erneuern Sie rechtzeitig. Der Erneuerungs-Token rotiert : Nach dem Tausch ist der alte verbraucht, und die Antwort trägt einen neuen. Geben Sie zurück, was Sie nicht mehr brauchen. Die Rücknahme antwortet immer 200 , auch für einen unbekannten Wert."
  },
  {
    "id": "api/oauth-delegated-access#optionen",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Optionen",
    "anchor": "optionen",
    "text": ""
  },
  {
    "id": "api/oauth-delegated-access#lebensdauern",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Lebensdauern",
    "anchor": "lebensdauern",
    "text": "Gegenstand Gültig Zugriffstoken oat_… eine Stunde Erneuerungs-Token ort_… dreißig Tage, rotiert bei jedem Tausch Autorisierungs-Code ocd_… zwei Minuten, genau eine Einlösung offene Einwilligungs-Anfrage zehn Minuten"
  },
  {
    "id": "api/oauth-delegated-access#rechte",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Rechte",
    "anchor": "rechte",
    "text": "Es gibt keinen eigenen Katalog: Ein Scope ist ein Rechte-Schlüssel, denselben, den auch ein API-Schlüssel tragen darf — scene.read , playlist.write und so fort. Die vollständige Liste steht als scopes_supported in beiden Wegbeschreibungen; welche Rechte für Maschinen freigegeben sind, sagt auch GET /v1/permission-catalog über das Feld appliesTo . Beim Einwilligen greifen zwei Schnitte: Ein Recht, das Maschinen nie tragen, ist nicht erteilbar; ein Recht, das die einwilligende Person selbst nicht hält, ebenfalls nicht. Beides steht mit seinem Grund in seinem Bereich. Erbitten Sie deshalb nur, was Sie wirklich brauchen: Ein zu breiter Wunsch macht die Entscheidung schwerer und wird abgewiesen, statt stillschweigend gekürzt zu werden."
  },
  {
    "id": "api/oauth-delegated-access#bereiche-und-teilweise-einwilligung",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Bereiche und teilweise Einwilligung",
    "anchor": "bereiche-und-teilweise-einwilligung",
    "text": "Die Einwilligungsseite zeigt die Rechte nicht als Liste, sondern gebündelt nach der Kategorie des Rechte-Katalogs — „Szenen“, „Playlisten und Zeitpläne“, „Medien“, „Displays und Standorte“, „Berichte“. Jeder Bereich trägt einen Satz, was Ihre Anwendung darin tun kann, einen Schalter und aufklappbar die einzelnen Rechte. Alle angefragten Bereiche sind zu Beginn eingeschaltet; dieselbe Bündelung steht später in der Karte Verbundene Anwendungen , eingeklappt als Bilanz („5 Bereiche, 18 Rechte“). Rechnen Sie mit einer Teilmenge. Der Mensch darf einzelne Bereiche abschalten, und POST /v1/oauth/authorizations/{id}/approve nimmt jede Teilmenge des Erbetenen an. Das Zugriffstoken trägt dann nur die erteilten Rechte, und die Token-Antwort nennt sie unter scope : Vergleichen Sie scope mit dem, worum Sie gebeten haben, statt es vorauszusetzen. Was fehlt, beantwortet die Plattform beim Aufruf mit 403 — nicht beim Tausch. Brauchen Sie einen Bereich wirklich, sagen Sie es der Person im Klartext und schicken Sie sie ein zweites Mal durch den Rundlauf; einen Weg, eine bestehende Einwilligung nachträglich zu erweitern, gibt es bewusst nicht."
  },
  {
    "id": "api/oauth-delegated-access#grenzen",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Grenzen",
    "anchor": "grenzen",
    "text": "GET /oauth/authorize , POST /oauth/token und POST /oauth/revoke sind auf 60 Aufrufe je Minute und Absenderadresse begrenzt, die beiden Wegbeschreibungen auf 120, die Registrierung auf 10 je Stunde. Der Echtzeit-Kanal bleibt einem delegierten Zugang verschlossen; Ereignisse holen Sie über Webhooks . Der Betreiber der Plattform kann einen Client sperren. Dann beginnt kein neuer Rundlauf mehr, und eine laufende Erneuerung scheitert; bestehende Zugriffstoken laufen aus, statt zurückgenommen zu werden. Ebenso kann eine Organisation den delegierten Zugang ganz abschalten: Die Einwilligung scheitert dann mit OAuthAuthorization.DelegationDisabled — dort, wo der Mensch entscheidet, und nicht erst beim Tausch."
  },
  {
    "id": "api/oauth-delegated-access#ergebnis-prüfen",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "GET /v1/me mit dem Token antwortet 200 und nennt das Konto und die Organisation, in deren Auftrag Sie handeln. Die Token-Antwort nennt unter scope die tatsächlich erteilten Rechte — sie können weniger sein als die erbetenen. Die Person sieht Ihre Anwendung im Portal unter Einstellungen › Mein Konto › Sicherheit in der Karte Verbundene Anwendungen , aufklappbar nach denselben Bereichen, und kann sie dort mit einem Klick widerrufen. Nach einem Widerruf antwortet derselbe Aufruf 401 ."
  },
  {
    "id": "api/oauth-delegated-access#wenn-etwas-nicht-klappt",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Die Endpunkte des Standards antworten im Format des Standards ( { \"error\": …, \"error_description\": … } ) und nicht im Fehlerformat dieser API . Die Routen unter /v1 antworten regulär. Symptom Bedeutung Lösung 400 invalid_grant beim Tausch Das Prüfwort passt nicht zur Prüfsumme, oder der Code ist abgelaufen. Rundlauf neu beginnen; Prüfwort und Prüfsumme müssen aus demselben Lauf stammen. 400 invalid_grant , und alle Token sind weg Ein Code oder ein Erneuerungs-Token wurde ein zweites Mal eingelöst. Die ganze Token-Familie erlischt: Zwei Inhaber desselben Geheimnisses lassen sich nicht auseinanderhalten. Den alten Token verwerfen, neu einwilligen lassen — und nie zwei Vorgänge auf demselben Token laufen lassen. 400 ohne Weiterleitung redirect_uri steht nicht in der Liste der Registrierung. Gerade diese Adresse ist das, was nicht bestätigt werden konnte. Die Adresse bei der Registrierung eintragen und beim Tausch wortgleich mitsenden. 410 OAuthAuthorization.Expired beim Öffnen der Einwilligungsseite Die Anfrage ist älter als zehn Minuten. Ausdrücklich nicht „nicht gefunden“. Die Person erneut hinschicken. 422 mit der Nennung von Rechte-Schlüsseln Es wurde ein Scope bestätigt, den die Person nicht halten kann oder der für Anwendungen nicht vorgesehen ist. Den genannten Schlüssel aus der Anfrage nehmen. 401 mitten im Betrieb Das Zugriffstoken ist älter als eine Stunde, oder die Person hat widerrufen. Einmal erneuern; scheitert auch das, neu einwilligen lassen. Die Codes OAuthClien"
  },
  {
    "id": "api/oauth-delegated-access#weiterführendes",
    "path": "/de/api/oauth-delegated-access",
    "area": "api",
    "areaTitle": "API",
    "page": "oauth-delegated-access",
    "pageTitle": "Delegierter Zugang mit OAuth 2.1",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Anmeldung: Schlüssel und Sitzungen Der MCP-Dienst für KI-Assistenten Handbuch: Verbundene Anwendungen erlauben und widerrufen"
  },
  {
    "id": "api/scim-provisioning",
    "path": "/de/api/scim-provisioning",
    "area": "api",
    "areaTitle": "API",
    "page": "scim-provisioning",
    "pageTitle": "Provisionierung mit SCIM 2.0",
    "section": "Provisionierung mit SCIM 2.0",
    "anchor": "",
    "text": "Nach dieser Seite bindet ein Verzeichnisdienst die Konten einer Organisation an Szenera an: Eintritt legt ein Konto an, Austritt sperrt es, ein Gruppenwechsel setzt die Rolle. Sie erfahren, wie der Zugang aussieht, welche Endpunkte es gibt, was aufeinander abgebildet wird und was bei Konflikten passiert."
  },
  {
    "id": "api/scim-provisioning#der-zugang",
    "path": "/de/api/scim-provisioning",
    "area": "api",
    "areaTitle": "API",
    "page": "scim-provisioning",
    "pageTitle": "Provisionierung mit SCIM 2.0",
    "section": "Der Zugang",
    "anchor": "der-zugang",
    "text": "Die Schnittstelle liegt unter /scim/v2 und nicht unter /v1 : Der Pfad gehört dem fremden Protokoll, und ein Verzeichnisdienst prüft seine eigenen Angaben gegen die Adresse, unter der er spricht. Was Wert Basis-Adresse $SZENERA_API/scim/v2 Ausweis Authorization: Bearer sct_… Inhaltstyp application/scim+json in beide Richtungen Reichweite genau eine Organisation je Zugang Hat Ihre Umgebung einen Anmelde-Wirt, nennt das Portal als Basis-Adresse künftig https://auth.<ihre-domain>/scim/v2 , und genau diese steht auch in meta.location der Antworten. Neue Einrichtungen tragen sie von Anfang an ein, eine bestehende an der Basis-Adresse der API bleibt bis auf Weiteres erreichbar. Der Zugang entsteht im Portal unter Einstellungen › Organisation › Unternehmens-Anmeldung , Abschnitt Provisionierung (SCIM) . Sein Wert erscheint dort ein einziges Mal. Er kommt auf keine andere Route der Plattform: Überall sonst wird er mit 403 abgewiesen, und umgekehrt erreicht kein API-Schlüssel /scim/v2 . Der Anmeldeweg ist Bedingung, nicht Empfehlung. Ein so angelegtes Konto bekommt kein Passwort und meldet sich über den Anbieter der Organisation an. Ohne aktiven Anbieter lässt sich kein Zugang ausstellen."
  },
  {
    "id": "api/scim-provisioning#die-endpunkte",
    "path": "/de/api/scim-provisioning",
    "area": "api",
    "areaTitle": "API",
    "page": "scim-provisioning",
    "pageTitle": "Provisionierung mit SCIM 2.0",
    "section": "Die Endpunkte",
    "anchor": "die-endpunkte",
    "text": "Route Methoden /scim/v2/ServiceProviderConfig GET — Funktionsumfang: Teiländerung ja, Filter ja, Stapel nein, Sortierung nein /scim/v2/ResourceTypes , /scim/v2/ResourceTypes/{id} GET /scim/v2/Schemas , /scim/v2/Schemas/{id} GET /scim/v2/Users GET (Liste und Filter), POST /scim/v2/Users/{id} GET , PUT , PATCH , DELETE /scim/v2/Groups GET (Liste und Filter), POST /scim/v2/Groups/{id} GET , PUT , PATCH , DELETE (wird abgewiesen, siehe unten) Ein Verzeichnisdienst beginnt mit der ersten Zeile: Ein Konto anlegen — wiederholbar, dieselbe Adresse liefert dasselbe Konto: Ein Konto sperren — die Teiländerung ist der Regelweg:"
  },
  {
    "id": "api/scim-provisioning#blättern-und-filtern",
    "path": "/de/api/scim-provisioning",
    "area": "api",
    "areaTitle": "API",
    "page": "scim-provisioning",
    "pageTitle": "Provisionierung mit SCIM 2.0",
    "section": "Blättern und Filtern",
    "anchor": "blättern-und-filtern",
    "text": "startIndex zählt ab eins , count ist auf 200 gedeckelt. Als Filter wird genau die Form attribut eq \"wert\" angenommen: userName eq und externalId eq auf Users , displayName eq auf Groups . Alles andere ist 400 mit scimType: \"invalidFilter\" — ein still ignorierter Filter lieferte sonst den ganzen Bestand."
  },
  {
    "id": "api/scim-provisioning#die-abbildung",
    "path": "/de/api/scim-provisioning",
    "area": "api",
    "areaTitle": "API",
    "page": "scim-provisioning",
    "pageTitle": "Provisionierung mit SCIM 2.0",
    "section": "Die Abbildung",
    "anchor": "die-abbildung",
    "text": "SCIM Szenera userName die E-Mail-Adresse; sie ist je Organisation eindeutig und damit der gemeinsame Schlüssel externalId wird gespeichert, ist je Organisation eindeutig und über externalId eq auffindbar name.givenName , name.familyName , displayName der Anzeigename des Profils active: false das Konto sperren; active: true reaktiviert es DELETE /Users/{id} sperrt , löscht nicht Group eine Rolle der Organisation; displayName ist ihr Name members die Konten, die diese Rolle tragen Felder, die Szenera nicht führt ( title , phoneNumbers , Erweiterungs-Schemas), werden still verworfen. Ihre Zuordnung im Verzeichnisdienst muss dafür nicht ausgedünnt werden. Vier Eigenheiten, die eine Anbindung kennen muss: Gelöscht wird nie. An einem Konto hängen Protokoll-Einträge, hochgeladene Medien und angelegte Programme. Das endgültige Löschen bleibt ein bewusster Schritt eines Menschen im Portal. Ein Konto trägt genau eine Rolle, nie keine. Ein add weist die Rolle zu und ersetzt die bisherige; ein remove setzt auf die Basis-Rolle zurück; ein PUT ist beides zusammen, denn wer in der Liste fehlt, fällt zurück. Gruppen entstehen nicht über SCIM. Die Substanz einer Rolle sind ihre Rechte, und die trägt das Protokoll nicht mit. POST /Groups trifft deshalb eine bestehende Rolle über ihren displayName ; ein unbekannter Name bekommt 400 mit scimType: \"invalidValue\" . Legen Sie die Rollen zuerst im Portal an und benennen Sie die Gruppen im Verzeichnis genauso. Jede Änderung ist wiederholbar. Ein Verz"
  },
  {
    "id": "api/scim-provisioning#fehler",
    "path": "/de/api/scim-provisioning",
    "area": "api",
    "areaTitle": "API",
    "page": "scim-provisioning",
    "pageTitle": "Provisionierung mit SCIM 2.0",
    "section": "Fehler",
    "anchor": "fehler",
    "text": "Unter /scim/v2 gilt das Fehlerformat des Protokolls, nicht das dieser API : Ein Verzeichnisdienst wertet scimType maschinell aus und entscheidet danach, ob er es erneut versucht, den Eintrag überspringt oder anhält. status ist eine Zeichenkette, so schreibt es das Protokoll vor. Fall Antwort Der Ausweis fehlt oder gilt nicht 401 Der Ausweis ist ein anderer als sct_… 403 Die Adresse gibt es in dieser Organisation schon 409 mit scimType: \"uniqueness\" Der Filter hat eine andere Form als attribut eq \"wert\" 400 mit scimType: \"invalidFilter\" POST /Groups nennt eine Rolle, die es nicht gibt 400 mit scimType: \"invalidValue\" DELETE /Groups/{id} 400 mit scimType: \"mutability\" — Rollen werden im Portal gelöscht"
  },
  {
    "id": "api/scim-provisioning#weiterführendes",
    "path": "/de/api/scim-provisioning",
    "area": "api",
    "areaTitle": "API",
    "page": "scim-provisioning",
    "pageTitle": "Provisionierung mit SCIM 2.0",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Anmeldung: Schlüssel und Sitzungen Basis-URL, /v1 und Stabilität Handbuch: Konten aus dem Personalverzeichnis (SCIM)"
  },
  {
    "id": "api/mcp-service",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Der MCP-Dienst für KI-Assistenten",
    "anchor": "",
    "text": "Der MCP-Dienst gibt einem KI-Assistenten eine kuratierte Auswahl an Werkzeugen auf Ihren Bestand: Szenen und Medien ansehen, Displays und Standorte prüfen, Entwürfe vorschlagen und, nach ausdrücklicher Bestätigung, ein Programm setzen. Diese Seite nennt die Adresse, die Anmeldung, die Werkzeuge, die Grenzen und die Fehler."
  },
  {
    "id": "api/mcp-service#was-der-dienst-ist",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Was der Dienst ist",
    "anchor": "was-der-dienst-ist",
    "text": "Er ist ein dünner Adapter vor derselben API, die auch das Portal und die Kommandozeile benutzen. Er hält keine Daten, kennt kein Geheimnis und prüft keinen Ausweis: Ihr Bearer reist unverändert an die Plattform weiter, und die Rechteauflösung bleibt die der API. Es gibt dort keine Route, kein Feld und keine Regel, die es nur dort gäbe. Was er beiträgt, sind vier Dinge: eine Auswahl statt einer Abbildung der ganzen API, eine Bestätigung vor jeder Wirkung, Grenzen für Ergebnisgröße und Laufzeit — und die Zusage, dass ein Werkzeug nur eines löscht: ein Display, das im Browser läuft. Der Dienst spricht Englisch. Werkzeugnamen, Eingabefelder, Antwortfelder, Statuswerte, Fehlerarten, Beschreibungen und Hinweise sind englisch, damit ein Assistent in jeder Sprache dieselben Namen liest. Diese Seite bleibt deutsch und zitiert die Antworten des Dienstes wörtlich."
  },
  {
    "id": "api/mcp-service#verbinden",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Verbinden",
    "anchor": "verbinden",
    "text": "Was Wert Transport Streamable HTTP nach der MCP-Spezifikation Adresse https://mcp.<ihre-domain>/mcp Anmeldung Authorization: Bearer oat_… oder Authorization: Bearer ak_… Beschreibung der Ressource GET https://mcp.<ihre-domain>/.well-known/oauth-protected-resource (RFC 9728) Lebenszeichen GET /health/live , GET /health/ready Ein Assistent, der OAuth beherrscht, braucht nur die Adresse: Ohne Ausweis antwortet /mcp mit 401 und der Kopfzeile WWW-Authenticate , die auf die Beschreibung der Ressource zeigt. Von dort findet der Client den Autorisierungs-Server und beginnt den Rundlauf aus Delegierter Zugang mit OAuth 2.1 . Ein Werkzeug ohne diesen Weg trägt stattdessen einen API-Schlüssel ein. Schon beim Handschlag prüft der Dienst den Ausweis einmal gegen GET /v1/me . Wird er abgelehnt, antwortet er 401 mit derselben Aufforderung — der Client beginnt dann die Anmeldung, statt in ein Gespräch zu geraten, in dem jedes Werkzeug scheitert. Der Ausweis gehört an jede Anfrage, auch innerhalb einer Sitzung. Das gilt ebenso für GET /mcp , das den Ereignisstrom einer Sitzung öffnet, und für DELETE /mcp , das sie beendet. Beide prüft der Dienst bei jeder Anfrage gegen GET /v1/me , bevor er die Sitzung überhaupt ansieht. Ohne Ausweis antworten sie mit 401 und error=\"invalid_request\" , mit einem abgelehnten Ausweis mit 401 und error=\"invalid_token\" . Die Sitzungskennung allein öffnet also nichts. Die Sitzung, die der Handschlag eröffnet, lebt 24 Stunden gleitend : Jeder Aufruf setzt die Frist "
  },
  {
    "id": "api/mcp-service#die-rechte-um-die-der-client-bittet",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Die Rechte, um die der Client bittet",
    "anchor": "die-rechte-um-die-der-client-bittet",
    "text": "Die Beschreibung der Ressource nennt unter scopes_supported die Rechte, die die Werkzeuge dieses Dienstes brauchen — genau sie, keine weiteren. Dieselbe Liste steht als scope in der Kopfzeile WWW-Authenticate , damit auch ein Client sie hat, der die Beschreibung nicht abruft. Ein Client übernimmt sie unverändert in seine Anmeldung. Ohne scope weist der Autorisierungs-Server die Anfrage ab. Auf der Einwilligung stehen diese Rechte danach gebündelt zu Bereichen — „Szenen“, „Playlisten und Zeitpläne“, „Medien“, „Displays und Standorte“, „Berichte“ —, jeder mit einem Satz und einem Schalter. Alle angefragten Bereiche sind zu Beginn eingeschaltet, und ein Mensch darf einzelne abschalten: Die Einwilligung darf eine Teilmenge sein. Ihr Zugriffstoken trägt dann nur die erteilten Rechte, und die Token-Antwort nennt sie unter scope . Fehlt einem Werkzeug ein Recht, antwortet es permission_denied und nennt den Bereich , den die Einwilligung tragen müsste — mit den Worten des Einwilligungs-Bildschirms, in der englischen Fassung („Displays & locations“; auf einem deutschen Bildschirm steht dafür „Displays und Standorte“): Damit kann ein Assistent seinem Menschen sagen, welchen Schalter er beim nächsten Rundlauf einschalten muss. Nachträglich erweitern lässt sich eine Einwilligung nicht — der Weg führt über Widerruf und erneutes Erlauben."
  },
  {
    "id": "api/mcp-service#neue-rechte-neue-einwilligung",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Neue Rechte, neue Einwilligung",
    "anchor": "neue-rechte-neue-einwilligung",
    "text": "Kommt ein Werkzeug dazu, das ein Recht braucht, das bisher keines brauchte, wächst diese Liste — und ein bestehendes Zugriffstoken wächst nicht mit . Es trägt die Rechte, die zum Zeitpunkt seiner Einwilligung erteilt wurden. Die neuen sind darin nicht enthalten, und die Werkzeuge dahinter antworten mit permission_denied . whoami sagt das, bevor es jemand ausprobiert: Es vergleicht die Rechte Ihres Ausweises mit denen, die die Werkzeuge dieses Dienstes brauchen, und nennt die fehlenden unter missingPermissions . Der zweite Satz ist kein Beiwerk: Ein delegierter Zugang bekommt nie mehr, als das Konto dahinter hat. Steht ein Recht nach dem neuen Erlauben immer noch in der Liste, fehlt es der Rolle des Kontos — dann hilft nur, wer Rollen vergibt. Mit einem API-Schlüssel stellen Sie stattdessen einen neuen mit dem passenden Recht aus. Alle Rechte, die dieser Dienst nennt, sieht der Rechte-Katalog für delegierten Zugang vor — auch die an Displays und an der Auswertung. Jedes Werkzeug ist damit über beide Wege erreichbar: über einen delegierten Zugang und über einen API-Schlüssel, der dieselben Rechte trägt. Was ein Werkzeug verändert, bleibt trotzdem an Ihre ausdrückliche Bestätigung gebunden (siehe unten)."
  },
  {
    "id": "api/mcp-service#die-werkzeuge",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Die Werkzeuge",
    "anchor": "die-werkzeuge",
    "text": "Die Werkzeuge stehen in drei Risikoklassen, und ein Client zeigt sie in dieser Reihenfolge. Klasse Werkzeuge Was sie tun Lesen whoami , list_scenes , get_scene , get_scene_document , list_scene_templates , list_widgets , get_widget_schema , list_graphics , list_assets , list_players , get_player , get_player_screenshot , list_display_groups , list_locations , get_location , list_playlists , get_playlist , list_schedules , get_hygiene , validate_scene Bestand ansehen: wer der Ausweis ist, welche Szenen, Bausteine, Motive, Medien, Displays, Leinwände, Standorte, Playlisten und Programme es gibt, was in einer Szene steht, was auf einem Display zu sehen ist (samt seiner jüngsten Aufnahme als Bild), was liegen geblieben ist — und ob ein Szenen-Dokument die Regeln einhält Vorschlagen create_scene_from_template , create_scene , update_scene , render_scene , create_playlist , add_to_playlist Entwürfe anlegen und ändern, die auf keinem Display erscheinen, solange sie niemand veröffentlicht — und eine veröffentlichte Fassung als Bild ansehen Schreiben publish_scene , set_player_program , set_display_group_program , set_player_override , clear_player_override , send_player_command , assign_player_location , create_location , set_location_opening_hours , create_web_display , issue_web_display_link , delete_web_display veröffentlichen, ein Programm setzen, übersteuern und wieder freigeben, einen Fernwartungs-Befehl absetzen, ein Display einem Standort zuordnen, einen Standort anlegen, sei"
  },
  {
    "id": "api/mcp-service#ein-display-auf-dem-etwas-zu-sehen-ist",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Ein Display, auf dem etwas zu sehen ist",
    "anchor": "ein-display-auf-dem-etwas-zu-sehen-ist",
    "text": "create_web_display ist der Weg, auf dem ein Assistent überhaupt etwas zeigen kann — ohne ihn setzt jedes andere Werkzeug einen Bildschirm voraus, den irgendwann jemand angeschlossen hat. Er antwortet mit der Kennung des Displays und einer Anschluss-Adresse . Die Adresse gehört in die Antwort an den Menschen: Sie auf dem Zielgerät im Browser zu öffnen ist der eine Schritt dieses Vorgangs, den kein Assistent selbst tun kann. Bis das geschieht, steht das Display in der Flotte als „nicht verbunden“ ( \"status\": \"not_connected\" in der Antwort) — mit Namen, Standort und, wenn gewünscht, schon mit seinem Programm. Es belegt von der Anlage an einen gebuchten Platz. Sind alle belegt, antwortet der Dienst mit no_seat_available statt mit einem technischen Fehler. Sobald ein Browser die Adresse geöffnet hat, zeigt das Display jede weitere Änderung von selbst — es muss dafür nicht neu geladen werden. issue_web_display_link gibt demselben Display eine neue Adresse, wenn der Browser dahinter seine Ablage verloren hat. Wer sie öffnet, übernimmt das Display, und der Browser, der es bis dahin zeigte, verliert es. Name, Standort, Programm und der gebuchte Platz bleiben unberührt. Die Aufräum-Fragen sind keine eigenen Werkzeuge, sondern Filter auf den Listen, die es ohnehin gibt: unbenutzte Medien, veröffentlichte Szenen ohne Zuweisung, Displays ohne Programm oder lange ohne Lebenszeichen. get_hygiene zählt sie und nennt zu jeder Zahl das Werkzeug, mit dem sich die Zeilen dahinter ansehen lassen."
  },
  {
    "id": "api/mcp-service#eine-szene-bauen",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Eine Szene bauen",
    "anchor": "eine-szene-bauen",
    "text": "Der Weg von der Idee zum Bild ist eine Kette aus sechs Schritten. Jede Werkzeug-Beschreibung nennt den nächsten, damit ein Assistent die Reihenfolge nicht raten muss. 1. Bausteine ansehen. list_widgets nennt die Widgets, aus denen eine Szene besteht: die Kern-Widgets text , image , video , shape , graphic und document , die immer da sind, und die Widgets der Apps, die Ihre Organisation installiert hat. Je Eintrag stehen Kennung, App, Name, ein Satz, die Pflichtfelder und ob die App installiert ist. Ein Widget einer nicht installierten App lässt sich in ein Dokument schreiben, läuft aber auf keinem Display. Hat Ihre Organisation keine App gebucht, stehen alle App-Widgets auf installed: false — die Antwort sagt das dann auch im Klartext, statt es Sie raten zu lassen. 2. Regeln holen. get_widget_schema liefert zu einem Widget das vollständige JSON-Schema-Dokument seiner Konfiguration (Draft 2020-12): jedes Feld mit title und description , die Pflichtfelder unter required und unter examples mindestens eine vollständige Beispiel-Konfiguration. Nehmen Sie ein Beispiel und tauschen Sie die Werte aus — das ist die kürzeste Anleitung, die es gibt. Dazu kommen die Start-Konfiguration und die Vorschau-Varianten des Widgets. Ein Feld nennt das Schema nicht mit Werten: die graphicId des Kern-Widgets graphic . Sie ist eine feste Kennung aus der Motiv-Bibliothek, und die listet list_graphics — mit Kategorie und Schlagworten, suchbar nach einer Absicht („pfeil“, „kaffee“). Geraten wird sie n"
  },
  {
    "id": "api/mcp-service#die-bestätigung",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Die Bestätigung",
    "anchor": "die-bestätigung",
    "text": "Jedes schreibende Werkzeug führt das Feld confirm . Ohne confirm: true geschieht nichts: Das Werkzeug liest den Ist-Zustand und antwortet mit einer Vorschau — welche Ziele es träfe, wie sie heißen, ob sie online sind, was ersetzt würde. Erst der zweite Aufruf mit denselben Werten und confirm: true führt aus und antwortet mit \"status\": \"executed\" , \"executed\": true und dem Ergebnis unter result . Die Vorschau ist ausdrücklich keine Reservierung: Sie hält nichts fest. Ändert sich zwischen beiden Aufrufen etwas, greift die Nebenläufigkeits-Prüfung der API, nicht der Dienst. Zusätzlich tragen alle Werkzeuge die Angaben readOnlyHint und destructiveHint , damit ein Client seine eigene Rückfrage stellen kann. Jeder schreibende Aufruf reist mit einem Idempotency-Key , der aus Sitzung, Werkzeugname und Argumenten abgeleitet ist: Dieselbe Absicht wirkt einmal, auch wenn sie zweimal ankommt."
  },
  {
    "id": "api/mcp-service#grenzen",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Grenzen",
    "anchor": "grenzen",
    "text": "Grenze Vorgabe Laufzeit eines Werkzeug-Aufrufs 30 Sekunden Laufzeit eines einzelnen Aufrufs an die API 15 Sekunden Länge eines Ergebnisses 20.000 Zeichen, darüber wird sichtbar gekürzt Einträge einer Liste 50 je Antwort. Die Zahl deckelt zugleich die angeforderte Seitengröße Ruhende Sitzung 24 Stunden, gleitend — jeder Aufruf setzt die Frist neu Der Betreiber Ihrer Umgebung kann diese Werte anders setzen. Ergebnisse enthalten keine signierten Download-Adressen, und Namen und Texte aus einer Antwort sind für den Assistenten Daten , keine Anweisungen — jede Werkzeug-Beschreibung sagt das ausdrücklich."
  },
  {
    "id": "api/mcp-service#wenn-etwas-nicht-klappt",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Ein gescheitertes Werkzeug antwortet mit drei Feldern: error (eine der Arten unten), hint (ein Satz auf Englisch) und correlationId (die Kennung, mit der der Betrieb den Aufruf wiederfindet). Die Feldpfade und Kennungen aus dem Fehlerrumpf der API bleiben absichtlich drinnen. error Bedeutung Lösung unauthenticated Die Plattform hat den Ausweis abgewiesen ( 401 ). Neu anmelden. Ein Zugriffstoken oat_… gilt eine Stunde. permission_denied Dem Ausweis fehlt das Recht ( 403 ). Der Einwilligung das Recht mitgeben oder den Schlüssel mit dem passenden Recht ausstellen. not_found Zu dieser Kennung gibt es in dieser Organisation nichts ( 404 ). Kennung aus einer Liste desselben Zugangs nehmen. conflict Der Gegenstand hat sich seit dem Lesen geändert ( 409 ). Neu lesen und erneut entscheiden. invalid_input Die Plattform hat die Angaben abgelehnt ( 422 ). Werte prüfen. no_seat_available Alle gebuchten Plätze der Organisation sind belegt ( 402 ). Einen zusätzlichen Platz bucht ein Mensch im Portal. rate_limited Das Kontingent ist erschöpft ( 429 ). Warten, siehe Drosselung und Rate-Limit-Kopfzeilen . platform_unreachable Die API antwortet nicht. Später erneut versuchen. Der Dienst kann ohne sie nichts. timeout Die zugestandene Zeit ist abgelaufen. Die Frage enger stellen, etwa mit einem Filter oder einer kleineren Liste. canceled Der Aufruf wurde abgebrochen, zum Beispiel weil die Verbindung abriss. Erneut aufrufen. unexpected Die Plattform hat auf eine Weise geantwortet, die der Dienst n"
  },
  {
    "id": "api/mcp-service#weiterführendes",
    "path": "/de/api/mcp-service",
    "area": "api",
    "areaTitle": "API",
    "page": "mcp-service",
    "pageTitle": "Der MCP-Dienst für KI-Assistenten",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Delegierter Zugang mit OAuth 2.1 Anmeldung: Schlüssel und Sitzungen Idempotenz von Anfang an"
  },
  {
    "id": "api/locations",
    "path": "/de/api/locations",
    "area": "api",
    "areaTitle": "API",
    "page": "locations",
    "pageTitle": "Standorte",
    "section": "Standorte",
    "anchor": "",
    "text": "Ein Standort ist der Ort, an dem Displays hängen: Name, Anschrift, Zeitzone, optional die Lage auf der Karte, das Bundesland, die Öffnungszeiten und die Art des Ladens. Diese Seite nennt die Felder, die Routen, die Zuordnung eines Displays und die Stellen, an denen der Standort sonst noch auftaucht."
  },
  {
    "id": "api/locations#was-ein-standort-ist",
    "path": "/de/api/locations",
    "area": "api",
    "areaTitle": "API",
    "page": "locations",
    "pageTitle": "Standorte",
    "section": "Was ein Standort ist",
    "anchor": "was-ein-standort-ist",
    "text": "Ein Ordner über Displays, kein zweiter Mandant. Er trägt keine Rechte, keine Lizenzen und keinen eigenen Bestand; ein Display zeigt mit locationId auf ihn, mehr Bindung gibt es nicht. Etiketten und Leinwände bleiben, was sie sind: Der Standort ersetzt sie nicht, er beantwortet eine andere Frage. Feld Regel id loc_ und 26 Zeichen name 1 bis 120 Zeichen, je Organisation eindeutig address vier Pflichtfelder, sobald überhaupt eine Anschrift da ist: street , postalCode , city , country (Ländercode nach ISO 3166-1 alpha-2, groß geschrieben); null heißt „keine hinterlegt“ timeZone Pflicht, eine Zone der IANA-Datenbank ( Europe/Berlin ) coordinates lat und lng oder null ; vom Menschen gesetzt, ohne Genauigkeitsangabe region das Bundesland als Kennung nach ISO 3166-2 ( DE-HH , heute die sechzehn deutschen Länder) oder null ; bestimmt die gesetzlichen Feiertage openingHours { weekly, specialDays } oder null , solange keine Zeiten gepflegt sind, siehe Öffnungszeiten note Freitext bis 500 Zeichen für die Leute vor Ort venueType die Art des Ladens ( pharmacy , workshop , bakery , beverage_store , grocery , kiosk , fitness_studio , medical_practice , retail , other ) oder null ; Werbebuchungen zielen danach, und die Auswertung fasst danach zusammen, ebenso nach dem Bundesland version die Fassung für If-Match , siehe ETag und If-Match Die Zeitzone gehört an den Ort und nicht an das Display: Zwei Bildschirme im selben Raum haben nie zwei verschiedene."
  },
  {
    "id": "api/locations#die-routen",
    "path": "/de/api/locations",
    "area": "api",
    "areaTitle": "API",
    "page": "locations",
    "pageTitle": "Standorte",
    "section": "Die Routen",
    "anchor": "die-routen",
    "text": "Route Recht Bemerkung GET /v1/locations location.list vollständig, ohne Blättern; jede Zeile nennt mit playerCount die Zahl der Displays am Ort GET /v1/locations/{id} location.read ein Ort mit allen Angaben POST /v1/locations location.create 201 ; braucht die Fähigkeit Standorte der Organisation PATCH /v1/locations/{id} location.update Teiländerung; address: null , coordinates: null , region: null , openingHours: null und venueType: null entfernen die Angabe, ein ausgelassenes Feld bleibt stehen. openingHours ersetzt den bisherigen Stand als Ganzes DELETE /v1/locations/{id} location.delete nur ein leerer Ort"
  },
  {
    "id": "api/locations#ein-display-zuordnen",
    "path": "/de/api/locations",
    "area": "api",
    "areaTitle": "API",
    "page": "locations",
    "pageTitle": "Standorte",
    "section": "Ein Display zuordnen",
    "anchor": "ein-display-zuordnen",
    "text": "Die Zuordnung läuft nicht über die Standort-Route, sondern über das Display: Ein Ort ist ein Stammdatum des Displays wie sein Name. \"locationId\": null löst das Display wieder. Ein Standort reißt nie Displays mit sich: Ein belegter Ort lässt sich nicht löschen, die Displays werden vorher umgehängt oder gelöst. Bekommt ein Display einen anderen Standort oder keinen mehr, baut es seine Momentaufnahme sofort neu: Öffnungszeiten, Notdienst, Wetter und Zeitzone folgen dem neuen Ort."
  },
  {
    "id": "api/locations#öffnungszeiten",
    "path": "/de/api/locations",
    "area": "api",
    "areaTitle": "API",
    "page": "locations",
    "pageTitle": "Standorte",
    "section": "Öffnungszeiten",
    "anchor": "öffnungszeiten",
    "text": "openingHours trägt den Wochenplan und die Sondertermine. Die Zeiten sind Wanduhrzeiten in der Zeitzone des Standorts. Feld Regel weekly.mon … weekly.sun je Tag bis zu sechs Fenster { \"opens\": \"08:00\", \"closes\": \"13:00\" } , aufsteigend und ohne Überschneidung, leer heißt geschlossen. Liegt closes nicht nach opens , endet das Fenster am Folgetag ( 22:00 bis 02:00 ), gleiche Uhrzeiten heißen 24 Stunden ( 00:00 bis 00:00 ist durchgehend geöffnet). Ein Fenster über Mitternacht ist das letzte des Tages und reicht nicht in das erste Fenster des Folgetags. Ein fehlender Tag gilt als geschlossen specialDays[] bis zu 100 Einträge { startDate, endDate, label, closed, windows } : ein Tag oder Zeitraum bis 92 Tage ( endDate einschließlich), eine Beschriftung bis 60 Zeichen, geschlossen ohne Fenster oder geöffnet mit mindestens einem. Sondertermine überschneiden sich nicht Was an einem Tag gilt, folgt einer festen Rangfolge: ein Sondertermin, sonst ein gesetzlicher Feiertag des Bundeslandes (geschlossen), sonst der Wochenplan. Die Feiertage speichert die Plattform nicht, sie rechnet sie je Bundesland aus dem Osterdatum und den Landesfeiertagen. Feiertage einzelner Gemeinden trägt die Organisation als Sondertermin ein. Ändern sich Wochenplan, Sondertermine, Bundesland oder Zeitzone, entsteht neben fleet.location.updated das Ereignis fleet.location.opening-hours-changed . Alle Displays der Organisation bauen daraufhin ihre Momentaufnahme neu, denn ein Widget kann die Zeiten jedes Standorts z"
  },
  {
    "id": "api/locations#wo-der-standort-sonst-auftaucht",
    "path": "/de/api/locations",
    "area": "api",
    "areaTitle": "API",
    "page": "locations",
    "pageTitle": "Standorte",
    "section": "Wo der Standort sonst auftaucht",
    "anchor": "wo-der-standort-sonst-auftaucht",
    "text": "Stelle Wie Display-Liste GET /v1/players?locationId=loc_… filtert auf einen Ort Auswertung der Wiedergabe location ist eine Gruppierungs-Achse; gruppiert wird über die heutige Zuordnung, ein Display ohne Ort fällt in eine eigene Zeile Kommandozeile die Gruppe location , dazu player assign-location und player unassign-location — siehe Befehle für Ihre Organisation Assistent die Werkzeuge list_locations , get_location , assign_player_location , create_location und set_location_opening_hours des MCP-Dienstes Apps der Standort des Displays im Resolver-Kontext, mit location-hours samt Öffnungszeiten, siehe Datenzugriff, Rechte und Bedingungen"
  },
  {
    "id": "api/locations#wenn-etwas-nicht-klappt",
    "path": "/de/api/locations",
    "area": "api",
    "areaTitle": "API",
    "page": "locations",
    "pageTitle": "Standorte",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung 409 Location.HasPlayers Am Ort hängen noch Displays. Die Antwort nennt die Zahl. Die Displays umhängen oder lösen, dann löschen. 409 Location.NameAlreadyExists Den Namen gibt es in dieser Organisation schon. Einen anderen wählen; zwei Zeilen „Filiale Nord“ wären in jeder Auswahl eine Falle. 404 Location.NotFound Die Kennung gehört zu keinem Ort dieser Organisation. Kennung aus GET /v1/locations nehmen. 422 Validation.Failed an timeZone Der Wert ist keine Zone der IANA-Datenbank. Europe/Berlin statt MEZ oder +01:00 . 403 Billing.FeatureNotEntitled beim Anlegen Die Fähigkeit Standorte ist für diese Organisation abgeschaltet. Beim Betreiber der Plattform freischalten lassen. 409 Conflict.OptimisticLock beim Ändern Jemand anderes hat zwischen Lesen und Schreiben geändert. Neu lesen und mit der neuen Fassung senden. Die drei Location.* -Codes stehen mit Bedeutung, Auslöser und Status unter Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb ."
  },
  {
    "id": "api/locations#weiterführendes",
    "path": "/de/api/locations",
    "area": "api",
    "areaTitle": "API",
    "page": "locations",
    "pageTitle": "Standorte",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Gleichzeitige Änderungen: ETag und If-Match Befehle für Ihre Organisation Handbuch: Standorte"
  },
  {
    "id": "api/web-displays",
    "path": "/de/api/web-displays",
    "area": "api",
    "areaTitle": "API",
    "page": "web-displays",
    "pageTitle": "Web-Displays",
    "section": "Web-Displays",
    "anchor": "",
    "text": "Ein Web-Display ist ein Display, hinter dem kein Gerät steht, sondern ein Browser-Tab. Diese Seite nennt die Routen, den Weg vom Anlegen bis zum ersten Bild, die beiden Zustände, in denen es leben kann, und die Grenzen, die es von einem Gerät unterscheiden."
  },
  {
    "id": "api/web-displays#was-ein-web-display-ist",
    "path": "/de/api/web-displays",
    "area": "api",
    "areaTitle": "API",
    "page": "web-displays",
    "pageTitle": "Web-Displays",
    "section": "Was ein Web-Display ist",
    "anchor": "was-ein-web-display-ist",
    "text": "Ein vollwertiges Display der Flotte: Es hat eine Kennung ply_… , einen Namen, einen Standort, ein Programm und einen gebuchten Platz. Was ihm fehlt, ist die Maschine — es läuft in einer Browser-Seite, und seine ganze Identität ist ein Geheimnis in der Ablage dieses Browsers. Daraus folgt das Eigentümliche dieses Weges: Anlegen und Anschließen sind zwei Schritte, und den zweiten kann kein Programm tun. Die Plattform legt das Display an und stellt eine Adresse aus; ein Mensch muss diese Adresse auf dem Zielgerät im Browser öffnen. Bis dahin steht das Display im Zustand paired-not-claimed — in der Oberfläche „nicht verbunden“. Zustand Bedeutung paired-not-claimed angelegt, aber noch kein Browser hat die Adresse geöffnet. Belegt bereits einen gebuchten Platz. online / stale / offline ein Browser hängt daran; der Zustand kommt wie bei jedem Display aus dem Alter des letzten Lebenszeichens"
  },
  {
    "id": "api/web-displays#die-routen",
    "path": "/de/api/web-displays",
    "area": "api",
    "areaTitle": "API",
    "page": "web-displays",
    "pageTitle": "Web-Displays",
    "section": "Die Routen",
    "anchor": "die-routen",
    "text": "Route Recht Bemerkung POST /v1/web-displays fleet.web-display.manage oder fleet.player.pair 201 ; legt Display und Anschluss-Adresse in einem Zug an POST /v1/web-displays/{id}/link fleet.web-display.manage oder fleet.player.pair 201 ; eine neue Adresse für ein bestehendes Web-Display POST /v1/web-displays/batch fleet.web-display.manage oder fleet.player.pair 201 ; legt bis zu 50 Displays in einem Zug an ( count , optional locationId und namePrefix ), mit durchnummerierten Namen und je einer Anschluss-Adresse. Alles oder nichts: Reichen die gebuchten Plätze nicht, entsteht keines. Die Antwort hat die Form einer Provisionierungs-Datei, jede Adresse gilt einmal und verfällt nach 24 Stunden DELETE /v1/web-displays/{id} fleet.web-display.manage nimmt Display und Browser-Zugang aus dem Bestand GET /v1/web-displays/address fleet.web-display.manage oder fleet.player.pair die Adresse der Anzeige-Anwendung — für Bildschirme, auf die sich keine Adresse kopieren lässt Alle Routen tragen x-stability: beta , auch POST /v1/web-displays/batch . Ihre Form kann sich noch bewegen; der Weg selbst hat sich mit dieser Welle bereits einmal verschoben (früher entstand das Display erst beim Öffnen des Links). Zum Löschen gilt: fleet.player.pair genügt hier nicht . Wer das Kopplungsrecht hält, hat ein Recht zum Anschließen erteilt bekommen und keines zum Wegräumen; ihm hier stillschweigend eines mitzugeben, machte aus einer alten Zustimmung eine neue."
  },
  {
    "id": "api/web-displays#anlegen-und-anschließen",
    "path": "/de/api/web-displays",
    "area": "api",
    "areaTitle": "API",
    "page": "web-displays",
    "pageTitle": "Web-Displays",
    "section": "Anlegen und anschließen",
    "anchor": "anlegen-und-anschließen",
    "text": "connectUrl ist die Adresse für den Menschen am Zielgerät. Sie beginnt mit der Adresse der Anzeige, die auch GET /v1/web-displays/address nennt, und gilt einmal : Sobald ein Browser sie geöffnet hat, ist die Marke verbraucht. Die Frist hängt daran, ob schon ein Browser an diesem Display hängt — bei der ersten Adresse 24 Stunden, danach zwei. Ein Startprogramm ist an dieser Route bewusst nicht vorgesehen. Die Zuweisung hat ihre eigene Route mit ihren eigenen Prüfungen; ein Display mit Inhalt entsteht deshalb in zwei Aufrufen: Die Reihenfolge ist gleichgültig: Ein Display, dem vor dem Anschluss ein Programm zugewiesen wurde, zeigt es, sobald der Browser da ist."
  },
  {
    "id": "api/web-displays#eine-neue-adresse",
    "path": "/de/api/web-displays",
    "area": "api",
    "areaTitle": "API",
    "page": "web-displays",
    "pageTitle": "Web-Displays",
    "section": "Eine neue Adresse",
    "anchor": "eine-neue-adresse",
    "text": "Der Anlass ist immer derselbe: Der Browser weiß nicht mehr, dass er dieses Display ist — geleerter Speicher, frisches Profil, anderer Rechner — oder die erste Adresse ist abgelaufen, ohne dass jemand sie geöffnet hat. Wer die neue Adresse öffnet, übernimmt das Display. Der Browser, der es bis dahin zeigte, verliert es samt seinen laufenden Renderer-Sitzungen, und alle älteren offenen Adressen desselben Displays verfallen. Name, Standort, Programm, Verlauf und der gebuchte Platz bleiben unberührt — genau darin unterscheidet sich dieser Weg vom Neuanlegen."
  },
  {
    "id": "api/web-displays#löschen",
    "path": "/de/api/web-displays",
    "area": "api",
    "areaTitle": "API",
    "page": "web-displays",
    "pageTitle": "Web-Displays",
    "section": "Löschen",
    "anchor": "löschen",
    "text": "Es verschwinden beide Hälften : das Display in der Flotte und das Browser-Gerät dahinter. Der gebuchte Platz wird frei, Programm, Übersteuerung und Verlauf werden aufgelöst. Ein Display mit Gerät wird an dieser Route abgewiesen — dafür gibt es DELETE /v1/players/{id} , und das bleibt einem Menschen in einer Sitzung vorbehalten."
  },
  {
    "id": "api/web-displays#was-ein-web-display-nicht-kann",
    "path": "/de/api/web-displays",
    "area": "api",
    "areaTitle": "API",
    "page": "web-displays",
    "pageTitle": "Web-Displays",
    "section": "Was ein Web-Display nicht kann",
    "anchor": "was-ein-web-display-nicht-kann",
    "text": "Hinter der Fläche steht keine Maschine, die sich steuern ließe. Das ist keine Lücke der Schnittstelle, sondern die Beschaffenheit der Sache: Nicht möglich Warum Systemabbild, Kanal, Rückfallebene Browser und Betriebssystem pflegt, wer den Rechner betreut Bildschirm ein- und ausschalten, Panel-Helligkeit CEC und DDC/CI sprechen die Maschine am Kabel an, nicht eine Seite im Browser Neustart der Maschine Neu geladen wird der Tab; der Rechner bleibt unberührt Bildschirmfoto ohne Erweiterung Kein Web-Standard erlaubt einer Seite, sich selbst zu fotografieren Zwei Bildschirme an einem Browser Ein zweiter braucht ein zweites Browser-Profil und wird ein eigenes Display mit eigenem Platz"
  },
  {
    "id": "api/web-displays#wenn-etwas-nicht-klappt",
    "path": "/de/api/web-displays",
    "area": "api",
    "areaTitle": "API",
    "page": "web-displays",
    "pageTitle": "Web-Displays",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung 409 WebDisplay.NotABrowserDisplay Hinter der Kennung steht ein Display mit Gerät. DELETE /v1/players/{id} bzw. POST /v1/devices/{id}/reconnect-link — beide für Menschen in einer Sitzung. 402 Billing.LicensesExhausted Alle gebuchten Plätze sind belegt. Die Antwort nennt booked und used . Einen Platz dazubuchen oder ein nicht mehr gebrauchtes Display löschen. 403 Billing.FeatureNotEntitled Die Fähigkeit Displays ohne Agent ist für diese Organisation abgeschaltet. Beim Betreiber der Plattform freischalten lassen. 404 Location.NotFound beim Anlegen Die genannte Standort-Kennung gehört zu keinem Ort dieser Organisation. Kennung aus GET /v1/locations nehmen. Die Adresse führt auf einen Kopplungscode Die Marke war verbraucht oder abgelaufen, der Browser hat sich daraufhin als neues Gerät angemeldet. Eine neue Adresse ausstellen und diese öffnen; das leere Gerät bleibt ungekoppelt und verfällt."
  },
  {
    "id": "api/web-displays#weiterführendes",
    "path": "/de/api/web-displays",
    "area": "api",
    "areaTitle": "API",
    "page": "web-displays",
    "pageTitle": "Web-Displays",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Der MCP-Dienst — die Werkzeuge create_web_display , issue_web_display_link und delete_web_display Standorte Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb"
  },
  {
    "id": "api/error-codes-general",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Fehlercodes: auf jeder Route möglich",
    "anchor": "",
    "text": "Diese Codes entstehen in der Plattform-Schicht, nicht in einem Fachbereich — sie können auf jeder Route unter /v1 auftreten. Ihre Bereiche sind Anmeldung, Rechte, Eingabeprüfung, Konflikte, Drosselung, Nutzlast, HTTP sowie die Betreiberrouten für Ereignisse, Zwischenspeicher, Prüfprotokoll und Hintergrundaufträge."
  },
  {
    "id": "api/error-codes-general#so-lesen-sie-die-tabellen",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "So lesen Sie die Tabellen",
    "anchor": "so-lesen-sie-die-tabellen",
    "text": "Die Tabellen lesen Sie so: Code ist der Wert im Feld code des Fehlerrumpfs — darauf schaltet Ihre Logik, nie auf message . Bedeutung und Typischer Auslöser ordnen den Fall ein. HTTP ist der tatsächlich gesendete Status. Erwartete Reaktion ist die Antwort Ihres Clients in einem festen Wortschatz, der auf allen sieben Fehlercode-Seiten gleich ist: „Ausweis prüfen“ (401), „Recht oder Paket prüfen“ (402, 403), „Eingabe korrigieren“ (400, 413, 415, 422), „neu laden, erneut senden“ (Versionskonflikte), „nicht wiederholen, Vorgang prüfen“ (404, 409, 410, 426), „warten (Retry-After)“ (429) und „später erneut, bei Wiederholung Trace-Id melden“ (5xx)."
  },
  {
    "id": "api/error-codes-general#die-codes",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Die Codes",
    "anchor": "die-codes",
    "text": ""
  },
  {
    "id": "api/error-codes-general#anmeldung-auth",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Anmeldung (Auth)",
    "anchor": "anmeldung-auth",
    "text": "Der Ausweis fehlt, ist ungültig oder abgelaufen. Fast alle Codes antworten 401, nur die unzulässige Sitzungsübernahme ist ein 403 und eine Fernwartung mit einer Protokoll-Fassung, die die Schnittstelle nicht bedient, ein 426. Für Integrationen sind Auth.ApiKeyRevoked , Auth.ApiKeyExpired und Auth.ApiKeyUnknown die drei, die Sie am ehesten sehen. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Auth.Required Authentifizierung erforderlich Anfrage ohne gültiges Token 401 Ausweis prüfen Auth.TokenExpired Zugriffstoken abgelaufen Lebensdauer von 15 Minuten überschritten 401 Ausweis prüfen Auth.TokenInvalid Token ungültig Manipuliertes oder unlesbares Token 401 Ausweis prüfen Auth.TokenSignatureInvalid Token-Signatur ungültig Falscher Signierschlüssel 401 Ausweis prüfen Auth.TokenKidUnknown Kennung des Signaturschlüssels unbekannt Token mit einer gewechselten Schlüsselkennung 401 Ausweis prüfen Auth.TokenMalformed Token strukturell fehlerhaft Kein gültiges Zugriffstoken 401 Ausweis prüfen Auth.SessionRevoked Sitzung widerrufen Abmeldung oder Beenden der Sitzung 401 Ausweis prüfen Auth.MfaRequired Zweiter Faktor erforderlich Anmeldung ohne zweiten Faktor 401 Ausweis prüfen Auth.MfaPending Schritt des zweiten Faktors ausstehend Zwischenschritt vor der Bestätigung des zweiten Faktors 401 Ausweis prüfen Auth.MfaCodeInvalid Code des zweiten Faktors falsch Falscher Einmal- oder Wiederherstellungscode 401 Ausweis prüfen Auth.ImpersonationNotPermitted Sitzungsübernahme nicht erla"
  },
  {
    "id": "api/error-codes-general#rechte-permission",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Rechte (Permission)",
    "anchor": "rechte-permission",
    "text": "Der Ausweis ist gültig, aber das Recht fehlt. Alle Codes antworten 403. Permission.OperatorOnly bekommen Sie, wenn ein Schlüssel oder eine Sitzung einer Organisation eine Route unter /v1/platform/… aufruft — bewusst kein 401, damit ein Prozess nicht in eine Anmeldeschleife läuft. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Permission.Denied Berechtigung fehlt Das nötige Recht fehlt 403 Recht oder Paket prüfen Permission.OperatorOnly Route nur für Betreiber-Konten Sitzung oder Schlüssel einer Organisation auf einer /v1/platform/… -Route. Eigener Code statt Auth.Required , weil ein Prozess auf 401 die Anmeldung wiederholt und sich im Kreis dreht — hier hilft kein neuer Ausweis, sondern nur eine andere Route 403 Recht oder Paket prüfen Permission.UnknownKey Unbekannter Rechte-Schlüssel Nicht im Katalog 403 Recht oder Paket prüfen Permission.OwnershipDenied Objekt-Zugriff verweigert Keine Erteilung für die Ressource 403 Recht oder Paket prüfen Permission.ResourceIdMissing Ressourcen-Kennung fehlt Objektgebundene Prüfung ohne Kennung 403 Recht oder Paket prüfen"
  },
  {
    "id": "api/error-codes-general#eingabeprüfung-validation",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Eingabeprüfung (Validation)",
    "anchor": "eingabeprüfung-validation",
    "text": "Rumpf, Parameter oder Kopfzeilen verletzen das Schema oder eine Regel. Regelfall 422, nur die fehlende Idempotenz-Kopfzeile auf einer Route, die sie erzwingt, antwortet 409. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Validation.Failed Eingabe ungültig Rumpf oder Parameter verletzen das Schema 422 Eingabe korrigieren Validation.IdempotencyKeyRequired Idempotency-Key erforderlich Mutation ohne Idempotency-Key 409 nicht wiederholen, Vorgang prüfen Validation.IdempotencyKeyReused Idempotency-Key mit anderem Rumpf Derselbe Schlüssel für einen zweiten, inhaltlich anderen Vorgang 422 Eingabe korrigieren Validation.UrlProtocolBlocked URL-Protokoll blockiert Nicht erlaubtes Schema (z. B. file: ) 422 Eingabe korrigieren Validation.UrlProtocolNotAllowed URL-Protokoll nicht zulässig Protokoll außerhalb der Freigabeliste 422 Eingabe korrigieren Validation.FileTypeUnsupported Dateityp nicht unterstützt Upload eines unzulässigen Formats 422 Eingabe korrigieren Validation.PathTraversalBlocked Pfad-Traversal blockiert ../ in einem Pfad-Parameter 422 Eingabe korrigieren"
  },
  {
    "id": "api/error-codes-general#konflikte-conflict",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Konflikte (Conflict)",
    "anchor": "konflikte-conflict",
    "text": "Der Zustand der Ressource lässt die Aktion nicht zu, oder jemand anderes hat gleichzeitig geschrieben. Alle Codes antworten 409. Conflict.OptimisticLock ist der Fall aus Gleichzeitige Änderungen . Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Conflict.OptimisticLock Versionskonflikt If-Match / expectedVersion veraltet 409 neu laden, erneut senden Conflict.DuplicateKey Eindeutigkeit verletzt Doppelter Wert in einem eindeutigen Feld 409 nicht wiederholen, Vorgang prüfen Conflict.ReferenceConstraint Verweis-Bedingung verletzt Verweis auf einen nicht vorhandenen Datensatz 409 nicht wiederholen, Vorgang prüfen Conflict.ResourceLocked Ressource gesperrt Konkurrierender Zugriff 409 neu laden, erneut senden"
  },
  {
    "id": "api/error-codes-general#drosselung-ratelimit",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Drosselung (RateLimit)",
    "anchor": "drosselung-ratelimit",
    "text": "Zu viele Anfragen im Fenster. Alle Codes antworten 429 mit Retry-After . Welche Routen gedrosselt sind, steht unter Drosselung . Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion RateLimit.Exceeded Rate-Limit überschritten Zu viele Anfragen 429 warten (Retry-After) RateLimit.GlobalCircuitBreaker Globaler Circuit-Breaker offen Plattformweite Überlast-Sicherung 429 warten (Retry-After)"
  },
  {
    "id": "api/error-codes-general#nutzlast-payload",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Nutzlast (Payload)",
    "anchor": "nutzlast-payload",
    "text": "Der Rumpf ist zu groß oder in einer Kodierung, die der Server nicht verarbeitet. Alle Codes antworten 413. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Payload.TooLarge Body zu groß Limit überschritten 413 Eingabe korrigieren Payload.UnsupportedEncoding Kodierung nicht unterstützt Nicht verarbeitbare Content-Encoding 413 Eingabe korrigieren"
  },
  {
    "id": "api/error-codes-general#herkunft-und-http-cors-http",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Herkunft und HTTP (Cors, Http)",
    "anchor": "herkunft-und-http-cors-http",
    "text": "Ablehnung einer Browser-Herkunft und die allgemeinen HTTP-Fehler: falsche Methode, falscher Medientyp, unlesbare Anfrage. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Cors.OriginRejected Origin nicht erlaubt Anfrage von unzulässiger Herkunft 403 Recht oder Paket prüfen Http.BadRequest Fehlerhafte Anfrage Anfrage, die der Server nicht lesen kann 400 Eingabe korrigieren Http.MethodNotAllowed Methode nicht erlaubt Falsches HTTP-Verb 405 Eingabe korrigieren Http.UnsupportedMediaType Medientyp nicht unterstützt Falscher Content-Type 415 Eingabe korrigieren"
  },
  {
    "id": "api/error-codes-general#ereignisse-events",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Ereignisse (Events)",
    "anchor": "ereignisse-events",
    "text": "Routen des Plattformbetriebs zur erneuten Zustellung von Ereignissen. Mit einem Schlüssel einer Organisation erreichen Sie sie nicht. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Events.EventNotFound Ereignis nicht gefunden Erneute Zustellung eines unbekannten Ereignisses 404 nicht wiederholen, Vorgang prüfen Events.EventNotReplayable Ereignis nicht wiederholbar Erneute Zustellung eines nicht wiederholbaren Ereignisses 409 nicht wiederholen, Vorgang prüfen Events.BulkReplayLimitExceeded Grenze der Sammel-Wiederholung überschritten Zu viele Ereignisse auf einmal 422 Eingabe korrigieren"
  },
  {
    "id": "api/error-codes-general#zwischenspeicher-cache",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Zwischenspeicher (Cache)",
    "anchor": "zwischenspeicher-cache",
    "text": "Routen des Plattformbetriebs zum Verwerfen von Zwischenspeicher-Einträgen. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Cache.PatternMatchingInvalid Ungültiges Zwischenspeicher-Muster Fehlerhaftes Muster zum Verwerfen 400 Eingabe korrigieren Cache.OpsInvalidateReasonTooShort Begründung zu kurz Verwerfen ohne ausreichende Begründung 400 Eingabe korrigieren"
  },
  {
    "id": "api/error-codes-general#prüfprotokoll-der-plattform-audit",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Prüfprotokoll der Plattform (Audit)",
    "anchor": "prüfprotokoll-der-plattform-audit",
    "text": "Routen des Plattformbetriebs für den Wechsel des Pseudonymisierungsschlüssels und die Auflösung von Pseudonymen. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Audit.RotationCooldownActive Sperrfrist des Schlüsselwechsels aktiv Wechsel des Pseudonymisierungsschlüssels zu früh erneut 409 nicht wiederholen, Vorgang prüfen Audit.LookupRateLimitExceeded Abfrage-Häufigkeit überschritten Zu viele Pseudonym-Abfragen 429 warten (Retry-After)"
  },
  {
    "id": "api/error-codes-general#hintergrundaufträge-jobs",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Hintergrundaufträge (Jobs)",
    "anchor": "hintergrundaufträge-jobs",
    "text": "Routen des Plattformbetriebs für Warteschlangen und endgültig fehlgeschlagene Aufträge. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Jobs.JobNotFound Auftrag nicht gefunden Aktion auf einen unbekannten Auftrag 404 nicht wiederholen, Vorgang prüfen Jobs.JobNotKillable Auftrag nicht abbrechbar Erzwungener Abbruch nicht möglich 409 nicht wiederholen, Vorgang prüfen Jobs.DeadLetterNotFound Eintrag der endgültig fehlgeschlagenen Aufträge fehlt Erneutes Einstellen eines unbekannten Eintrags 404 nicht wiederholen, Vorgang prüfen Jobs.ReplayAlreadyDone Erneutes Einstellen bereits erfolgt Doppeltes erneutes Einstellen 409 nicht wiederholen, Vorgang prüfen Jobs.BulkReplayLimitExceeded Grenze der Sammel-Wiederholung überschritten Zu viele Aufträge auf einmal 422 Eingabe korrigieren"
  },
  {
    "id": "api/error-codes-general#ressourcen-resource",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Ressourcen (Resource)",
    "anchor": "ressourcen-resource",
    "text": "Der allgemeine Rückfall, wenn keine Route zum Pfad passt. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Resource.NotFound Ressource nicht gefunden Generischer 404-Fallback 404 nicht wiederholen, Vorgang prüfen"
  },
  {
    "id": "api/error-codes-general#zählsatz",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Zählsatz",
    "anchor": "zählsatz",
    "text": "Diese Seite führt 66 Codes . Zusammen mit den übrigen sechs Fehlercode-Seiten ergeben sich 493 fachliche Codes (4xx) und 119 Serverfehler (5xx), also 612 Codes — der vollständige Katalog der Plattform. Gezählt wird dabei nach der Art des Fehlers und nicht nach dem gesendeten Status: Als fachlich zählen die 493 Codes, auf die Ihr Client gezielt reagieren kann — neun von ihnen antworten trotzdem mit einem 5xx, weil ein nachgelagerter Dienst ausfällt, das Haus für einen Augenblick nicht rechenbereit ist oder ihm ein Zugang fehlt —, als Serverfehler die 119, bei denen nur Wiederholen oder Melden bleibt. Nach dem Status allein gezählt wären es 484 und 128."
  },
  {
    "id": "api/error-codes-general#weiterführendes",
    "path": "/de/api/error-codes-general",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-general",
    "pageTitle": "Fehlercodes: auf jeder Route möglich",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Das Fehlerformat Wenn der erste Aufruf scheitert"
  },
  {
    "id": "api/error-codes-accounts",
    "path": "/de/api/error-codes-accounts",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-accounts",
    "pageTitle": "Fehlercodes: Konten und Sitzungen",
    "section": "Fehlercodes: Konten und Sitzungen",
    "anchor": "",
    "text": "Die Codes der Bereiche Identity und Sessions: Benutzerkonten, Betreiber-Konten, API-Schlüssel, verbundene Anwendungen, der SCIM-Zugang, Gerätekonten, Anmeldung, zweiter Faktor, Auffrischung und Sitzungsübernahme."
  },
  {
    "id": "api/error-codes-accounts#so-lesen-sie-die-tabellen",
    "path": "/de/api/error-codes-accounts",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-accounts",
    "pageTitle": "Fehlercodes: Konten und Sitzungen",
    "section": "So lesen Sie die Tabellen",
    "anchor": "so-lesen-sie-die-tabellen",
    "text": "Die Tabellen lesen Sie so: Code ist der Wert im Feld code des Fehlerrumpfs — darauf schaltet Ihre Logik, nie auf message . Bedeutung und Typischer Auslöser ordnen den Fall ein. HTTP ist der tatsächlich gesendete Status. Erwartete Reaktion ist die Antwort Ihres Clients in einem festen Wortschatz, der auf allen sieben Fehlercode-Seiten gleich ist: „Ausweis prüfen“ (401), „Recht oder Paket prüfen“ (402, 403), „Eingabe korrigieren“ (400, 413, 415, 422), „neu laden, erneut senden“ (Versionskonflikte), „nicht wiederholen, Vorgang prüfen“ (404, 409, 410, 426), „warten (Retry-After)“ (429) und „später erneut, bei Wiederholung Trace-Id melden“ (5xx)."
  },
  {
    "id": "api/error-codes-accounts#die-codes",
    "path": "/de/api/error-codes-accounts",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-accounts",
    "pageTitle": "Fehlercodes: Konten und Sitzungen",
    "section": "Die Codes",
    "anchor": "die-codes",
    "text": ""
  },
  {
    "id": "api/error-codes-accounts#konten-schlüssel-geräte-identity",
    "path": "/de/api/error-codes-accounts",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-accounts",
    "pageTitle": "Fehlercodes: Konten und Sitzungen",
    "section": "Konten, Schlüssel, Geräte (Identity)",
    "anchor": "konten-schlüssel-geräte-identity",
    "text": "Konten, Betreiber-Konten, API-Schlüssel und Geräte. Für Integrationen sind die Schlüssel-Codes die wichtigsten. ApiKey.ScopeEscalationBlocked (403) heißt, der Schlüssel verlangt mehr, als sein Ersteller selbst darf. ApiKey.ScopeNotKeyCapable (422) heißt, ein gewähltes Recht ist für Schlüssel nicht vorgesehen, und da hilft kein Nachfragen, sondern nur ein anderer Weg. Dazu kommen ApiKey.ExpiresAtInPast (422) und ApiKey.AlreadyRevoked (409), denn der Widerruf ist endgültig. Die Codes OperatorApiKey.* betreffen Betreiber-Schlüssel ( opk_ ). Dazu die Zugänge, die nicht an einer Person hängen: OAuthClient.* und OAuthAuthorization.* für verbundene Anwendungen — OAuthAuthorization.Expired (410) und OAuthAuthorization.AlreadyDecided (409) sehen Sie am häufigsten, beide heißen „Einwilligung neu beginnen“ — und ScimToken.* für den Zugang, mit dem ein Personalverzeichnis Konten abgleicht . Unter den Device.* -Codes weist Device.Blocked (409) ein gesperrtes Gerät ab, bis der Betreiber die Sperre aufhebt. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion User.NotFound Konto nicht gefunden Zugriff auf eine unbekannte Konto-Kennung 404 nicht wiederholen, Vorgang prüfen User.EmailAlreadyExists E-Mail bereits vergeben Anlage mit belegter Adresse 409 nicht wiederholen, Vorgang prüfen User.PlanSeatQuotaExceeded Sitz-Kontingent erschöpft Konto über dem Kontingent des Pakets 422 Eingabe korrigieren User.MissingCredential Anmeldedaten fehlen Konto ohne Passwort und ohne Unternehmens-Anmeld"
  },
  {
    "id": "api/error-codes-accounts#anmeldung-und-sitzungen-sessions",
    "path": "/de/api/error-codes-accounts",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-accounts",
    "pageTitle": "Fehlercodes: Konten und Sitzungen",
    "section": "Anmeldung und Sitzungen (Sessions)",
    "anchor": "anmeldung-und-sitzungen-sessions",
    "text": "Anmeldung mit Passwort, Anmelde-Link, Unternehmens-Anmeldung und plattformweite Anbieter, zweiter Faktor, Auffrischung, Zurücksetzen des Passworts und die Sitzungsübernahme durch den Betreiber. Die Codes mit 410 markieren verbrauchte oder abgelaufene Links. Ein neuer Link ist der einzige Weg. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Login.InvalidCredentials Anmeldedaten falsch Falsches Passwort oder falsche E-Mail-Adresse 401 Ausweis prüfen Login.AccountSuspended Konto gesperrt Anmeldung eines gesperrten Kontos 403 Recht oder Paket prüfen Login.TenantSuspended Organisation gesperrt Anmeldung in eine gesperrte Organisation 403 Recht oder Paket prüfen Login.ChoiceTicketInvalid Wahl-Ticket ungültig Organisations-Wahl mit unbekanntem, abgelaufenem oder bereits eingelöstem Ticket (auch: anderes Gerät) 401 Ausweis prüfen Login.OrganizationNotEligible Organisation nicht wählbar Gewählte Organisation gehört nicht zur Menge des Tickets 403 Recht oder Paket prüfen Login.SsoRequired Unternehmens-Anmeldung erzwungen Passwort, Anmelde-Link oder Zurücksetzen für eine Adresse in einer nachgewiesenen Domäne der Unternehmens-Anmeldung 403 Recht oder Paket prüfen Login.HandoffTicketInvalid Übergabe-Ticket ungültig Ticket unbekannt, abgelaufen (3 min) oder bereits eingelöst 401 Ausweis prüfen Mfa.InvalidCode Code des zweiten Faktors ungültig Falscher Code bei der Anmeldung 401 Ausweis prüfen Mfa.TooManyAttempts Zu viele Versuche mit dem zweiten Faktor Fehlversuch-Grenze übersch"
  },
  {
    "id": "api/error-codes-accounts#zählsatz",
    "path": "/de/api/error-codes-accounts",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-accounts",
    "pageTitle": "Fehlercodes: Konten und Sitzungen",
    "section": "Zählsatz",
    "anchor": "zählsatz",
    "text": "Diese Seite führt 103 Codes . Zusammen mit den übrigen sechs Fehlercode-Seiten ergeben sich 493 fachliche Codes (4xx) und 119 Serverfehler (5xx), also 612 Codes — der vollständige Katalog der Plattform."
  },
  {
    "id": "api/error-codes-accounts#weiterführendes",
    "path": "/de/api/error-codes-accounts",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-accounts",
    "pageTitle": "Fehlercodes: Konten und Sitzungen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Anmeldung: Schlüssel und Sitzungen Das Fehlerformat"
  },
  {
    "id": "api/error-codes-organization",
    "path": "/de/api/error-codes-organization",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-organization",
    "pageTitle": "Fehlercodes: Organisation, Zugang, Beitritt",
    "section": "Fehlercodes: Organisation, Zugang, Beitritt",
    "anchor": "",
    "text": "Die Codes der Bereiche Tenancy, Access, Onboarding und Cloud-Connections: Stammdaten und Marke der Organisation, eigene Domain, Unternehmens-Anmeldung, Rollen und Erteilungen, Einladungen, Registrierung, Beitrittsanfragen und Speicher-Verbindungen."
  },
  {
    "id": "api/error-codes-organization#so-lesen-sie-die-tabellen",
    "path": "/de/api/error-codes-organization",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-organization",
    "pageTitle": "Fehlercodes: Organisation, Zugang, Beitritt",
    "section": "So lesen Sie die Tabellen",
    "anchor": "so-lesen-sie-die-tabellen",
    "text": "Die Tabellen lesen Sie so: Code ist der Wert im Feld code des Fehlerrumpfs — darauf schaltet Ihre Logik, nie auf message . Bedeutung und Typischer Auslöser ordnen den Fall ein. HTTP ist der tatsächlich gesendete Status. Erwartete Reaktion ist die Antwort Ihres Clients in einem festen Wortschatz, der auf allen sieben Fehlercode-Seiten gleich ist: „Ausweis prüfen“ (401), „Recht oder Paket prüfen“ (402, 403), „Eingabe korrigieren“ (400, 413, 415, 422), „neu laden, erneut senden“ (Versionskonflikte), „nicht wiederholen, Vorgang prüfen“ (404, 409, 410, 426), „warten (Retry-After)“ (429) und „später erneut, bei Wiederholung Trace-Id melden“ (5xx)."
  },
  {
    "id": "api/error-codes-organization#die-codes",
    "path": "/de/api/error-codes-organization",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-organization",
    "pageTitle": "Fehlercodes: Organisation, Zugang, Beitritt",
    "section": "Die Codes",
    "anchor": "die-codes",
    "text": ""
  },
  {
    "id": "api/error-codes-organization#organisation-tenancy",
    "path": "/de/api/error-codes-organization",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-organization",
    "pageTitle": "Fehlercodes: Organisation, Zugang, Beitritt",
    "section": "Organisation (Tenancy)",
    "anchor": "organisation-tenancy",
    "text": "Stammdaten, Marke, Anschrift, Unternehmens-Anmeldung samt Domänen und die eigene Adresse des Portals. Tenant.Frozen (403) sperrt Änderungen bei offener Zahlung, Lesen und Abrechnung bleiben offen. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Tenant.NotFound Organisation nicht gefunden Unbekannte Kennung der Organisation 404 nicht wiederholen, Vorgang prüfen Tenant.SlugAlreadyExists Kurzname bereits vergeben Anlage mit belegtem Kurznamen 409 nicht wiederholen, Vorgang prüfen Tenant.SlugReserved Kurzname reserviert Reservierter Kurzname 422 Eingabe korrigieren Tenant.InvalidStatusTransition Ungültiger Zustandswechsel der Organisation Unzulässiger Statuswechsel 409 nicht wiederholen, Vorgang prüfen Tenant.Frozen Organisation eingefroren Änderung bei offener Zahlung. Lesen und Abrechnung bleiben offen 403 Recht oder Paket prüfen Tenant.DeleteConfirmationRequired Löschung braucht Bestätigung Löschung ohne Bestätigung 422 Eingabe korrigieren Branding.ContrastInsufficient Kontrast unzureichend Farbwahl unterschreitet Kontrast-Minimum 422 Eingabe korrigieren Branding.AssetNotFound Markenzeichen fehlt Verweis auf unbekanntes Logo/Favicon 422 Eingabe korrigieren Address.InvalidCountryCode Ländercode ungültig Unbekannter ISO-Ländercode 422 Eingabe korrigieren Sso.ConfigInvalid Konfiguration der Unternehmens-Anmeldung ungültig Fehlerhafte Felder der Unternehmens-Anmeldung, oder der Aussteller antwortet nicht mit einer gültigen Anbieter-Beschreibung 422 Eingabe korrigieren Ss"
  },
  {
    "id": "api/error-codes-organization#rollen-und-erteilungen-access",
    "path": "/de/api/error-codes-organization",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-organization",
    "pageTitle": "Fehlercodes: Organisation, Zugang, Beitritt",
    "section": "Rollen und Erteilungen (Access)",
    "anchor": "rollen-und-erteilungen-access",
    "text": "Rollen, Erteilungen und Rechte-Schlüssel. Eingebaute Rollen sind unveränderlich. Eine Rolle kann nie Rechte tragen, die die handelnde Person selbst nicht hat. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Role.NotFound Rolle nicht gefunden Unbekannte Rollen-Kennung 404 nicht wiederholen, Vorgang prüfen Role.BuiltInImmutable System-Rolle unveränderlich Änderung einer eingebauten Rolle 409 nicht wiederholen, Vorgang prüfen Role.InUse Rolle in Verwendung Löschung einer Rolle, die noch jemand trägt oder die ein Anmeldeweg als Standard führt 409 nicht wiederholen, Vorgang prüfen Role.PermissionNotInCatalog Recht nicht im Katalog Rolle mit einem unbekannten Rechte-Schlüssel 422 Eingabe korrigieren Role.PermissionEscalationBlocked Rechte über dem eigenen Set Rolle mit Rechten, die der Handelnde nicht trägt 403 Recht oder Paket prüfen Grant.NotFound Erteilung nicht gefunden Unbekannte Erteilungs-Kennung 404 nicht wiederholen, Vorgang prüfen Grant.AllowDenyConflict Erlauben-Verbieten-Konflikt Widersprüchliche Erteilungs-Regeln 422 Eingabe korrigieren Grant.ResourceNotOwned Ressource nicht im Besitz Erteilung auf eine fremde Ressource 403 Recht oder Paket prüfen PermissionKey.InvalidSyntax Aufbau des Rechte-Schlüssels ungültig Fehlerhaft aufgebauter Rechte-Schlüssel 422 Eingabe korrigieren"
  },
  {
    "id": "api/error-codes-organization#einladungen-und-registrierung-onboarding",
    "path": "/de/api/error-codes-organization",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-organization",
    "pageTitle": "Fehlercodes: Organisation, Zugang, Beitritt",
    "section": "Einladungen und Registrierung (Onboarding)",
    "anchor": "einladungen-und-registrierung-onboarding",
    "text": "Einladungen, Einrichtungsassistent, Anlage einer Organisation, Selbst-Registrierung und Beitrittsanfragen. Zwei Abweichungen von der Bereichsregel sind Absicht: Signup.TicketInvalid antwortet 401, weil das Ticket der Nachweis ist, und Signup.Disabled 403 — scharf wird die Abschaltung erst, wo ein Ticket vorliegt. Die Registrierung gibt bewusst keine Auskunft über den Bestand: Ein unbekannter, ein verbrauchter und ein abgelaufener Bestätigungscode antworten identisch mit Signup.TicketInvalid . Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Invitation.NotFound Einladung nicht gefunden Unbekannte Einladungs-Kennung 404 nicht wiederholen, Vorgang prüfen Invitation.TokenInvalid Einladungs-Token ungültig Fehlerhaftes Token 400 Eingabe korrigieren Invitation.Expired Einladung abgelaufen Abgelaufenes Token 410 nicht wiederholen, Vorgang prüfen Invitation.AlreadyAccepted Einladung bereits angenommen Doppelte Annahme 409 nicht wiederholen, Vorgang prüfen Invitation.Revoked Einladung widerrufen Eingelöste, aber widerrufene Einladung 410 nicht wiederholen, Vorgang prüfen Invitation.RoleNotInTenant Rolle nicht in der Organisation Einladung mit fremder Rolle 422 Eingabe korrigieren SetupStep.Unknown Einrichtungsschritt unbekannt Ungültiger Schritt des Einrichtungsassistenten 422 Eingabe korrigieren Provisioning.SagaFailed Anlage der Organisation fehlgeschlagen Fehler in der mehrstufigen Anlage einer Organisation 500 später erneut, bei Wiederholung Trace-Id melden Provisioning.Ow"
  },
  {
    "id": "api/error-codes-organization#speicher-verbindungen-cloud-connections",
    "path": "/de/api/error-codes-organization",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-organization",
    "pageTitle": "Fehlercodes: Organisation, Zugang, Beitritt",
    "section": "Speicher-Verbindungen (Cloud-Connections)",
    "anchor": "speicher-verbindungen-cloud-connections",
    "text": "Die Vollmachten auf fremde Speicher-Konten, aus denen die Mediathek ihre Orte speist. Die 409-Codes teilen eine Aussage: Die Verbindung gibt es, ihr Zustand trägt nur nicht — die Antwort darauf ist ein neuer Rundlauf, keine Fehlersuche. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion CloudConnection.NotFound Verbindung nicht gefunden Trennen, Reparieren oder Token-Bezug mit unbekannter oder fremder Verbindungs-Kennung — fremd und erfunden sind absichtlich ununterscheidbar 404 nicht wiederholen, Vorgang prüfen CloudConnection.ProviderUnavailable Anbieter nicht eingerichtet Verbindungsaufbau zu einem Speicher-Anbieter, für den dieser Betrieb keine Konfiguration hinterlegt hat 404 nicht wiederholen, Vorgang prüfen CloudConnection.StateInvalid Rücksprung-Zustand unbekannt Der Rücksprung trägt keinen state , den dieser Server begonnen hat: unbekannt, abgelaufen oder schon eingelöst — alle drei antworten gleich 401 Ausweis prüfen CloudConnection.CallbackFailed Rücksprung ohne Vollmacht Code-Tausch gescheitert, Anbieter gewährt keinen Offline-Zugriff, oder die Kontoabfrage schlug fehl 401 Ausweis prüfen CloudConnection.AccountMismatch Anderes Konto zurückgekommen Reparatur-Rundlauf, bei dem die Anmeldung mit einem anderen Konto erfolgt als dem der Verbindung. Eigener Fehler statt stiller Neuanlage — sonst stünden zwei Einträge da, von denen einer nie wieder trägt 409 nicht wiederholen, Vorgang prüfen CloudConnection.Broken Vollmacht trägt nicht mehr Der Anbieter lehnt die "
  },
  {
    "id": "api/error-codes-organization#zählsatz",
    "path": "/de/api/error-codes-organization",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-organization",
    "pageTitle": "Fehlercodes: Organisation, Zugang, Beitritt",
    "section": "Zählsatz",
    "anchor": "zählsatz",
    "text": "Diese Seite führt 51 Codes . Zusammen mit den übrigen sechs Fehlercode-Seiten ergeben sich 493 fachliche Codes (4xx) und 119 Serverfehler (5xx), also 612 Codes — der vollständige Katalog der Plattform."
  },
  {
    "id": "api/error-codes-organization#weiterführendes",
    "path": "/de/api/error-codes-organization",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-organization",
    "pageTitle": "Fehlercodes: Organisation, Zugang, Beitritt",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Das Fehlerformat Handbuch: Organisation"
  },
  {
    "id": "api/error-codes-billing",
    "path": "/de/api/error-codes-billing",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-billing",
    "pageTitle": "Fehlercodes: Abrechnung",
    "section": "Fehlercodes: Abrechnung",
    "anchor": "",
    "text": "Die Codes des Bereichs Billing: Paket, Lizenzen, Zubuchungen, Zahlungsmittel, Rechnungen, Bestellungen und die Rückmeldungen des Zahlungsdienstleisters."
  },
  {
    "id": "api/error-codes-billing#so-lesen-sie-die-tabellen",
    "path": "/de/api/error-codes-billing",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-billing",
    "pageTitle": "Fehlercodes: Abrechnung",
    "section": "So lesen Sie die Tabellen",
    "anchor": "so-lesen-sie-die-tabellen",
    "text": "Die Tabellen lesen Sie so: Code ist der Wert im Feld code des Fehlerrumpfs — darauf schaltet Ihre Logik, nie auf message . Bedeutung und Typischer Auslöser ordnen den Fall ein. HTTP ist der tatsächlich gesendete Status. Erwartete Reaktion ist die Antwort Ihres Clients in einem festen Wortschatz, der auf allen sieben Fehlercode-Seiten gleich ist: „Ausweis prüfen“ (401), „Recht oder Paket prüfen“ (402, 403), „Eingabe korrigieren“ (400, 413, 415, 422), „neu laden, erneut senden“ (Versionskonflikte), „nicht wiederholen, Vorgang prüfen“ (404, 409, 410, 426), „warten (Retry-After)“ (429) und „später erneut, bei Wiederholung Trace-Id melden“ (5xx)."
  },
  {
    "id": "api/error-codes-billing#die-codes",
    "path": "/de/api/error-codes-billing",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-billing",
    "pageTitle": "Fehlercodes: Abrechnung",
    "section": "Die Codes",
    "anchor": "die-codes",
    "text": ""
  },
  {
    "id": "api/error-codes-billing#abrechnung-billing",
    "path": "/de/api/error-codes-billing",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-billing",
    "pageTitle": "Fehlercodes: Abrechnung",
    "section": "Abrechnung (Billing)",
    "anchor": "abrechnung-billing",
    "text": "Die Statuscodes sind hier eine Aussage: 402 steht an Sperren, die sich durch eine Buchung lösen — Zahlungsmittel, Kontingent, Lizenzen, Zubuchung, App, Paket. 403 Billing.FeatureNotEntitled heißt: Eine vertragliche Fähigkeit fehlt, etwa der API-Zugang oder Webhooks. Das löst nur ein anderes Paket oder der Betreiber, keine Buchung im Portal. Welche Fähigkeit welches Paket enthält, steht im Handbuch unter Abrechnung . 409 steht, wo erst etwas anderes zu erledigen ist (Rechnungsprofil, Zahlungsmittel, Reihenfolge). Die Codes Stripe.* bleiben wörtlich, weil sie so im Katalog stehen. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Subscription.NotFound Abo nicht gefunden Unbekannte Abo-Kennung 404 nicht wiederholen, Vorgang prüfen Subscription.AlreadyExists Abo existiert bereits Doppelte Anlage 409 nicht wiederholen, Vorgang prüfen Subscription.InvalidStatusTransition Ungültiger Abo-Übergang Unzulässiger Statuswechsel 409 nicht wiederholen, Vorgang prüfen PaymentMethod.Required Zahlungsmittel erforderlich Aktion ohne hinterlegtes Zahlungsmittel 402 Recht oder Paket prüfen Quota.Exceeded Kontingent überschritten Vertragliches Limit erreicht 402 Recht oder Paket prüfen Billing.LicensesExhausted Alle Lizenzen belegt Koppeln eines weiteren Bildschirms, obwohl der Vertrag keine freie Lizenz mehr trägt. Das Detail nennt gebucht und belegt, damit die Oberfläche den Weg zum Dazubuchen zeigen kann 402 Recht oder Paket prüfen Billing.LicenseReductionBlocked Lizenzzahl unter dem Ve"
  },
  {
    "id": "api/error-codes-billing#zählsatz",
    "path": "/de/api/error-codes-billing",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-billing",
    "pageTitle": "Fehlercodes: Abrechnung",
    "section": "Zählsatz",
    "anchor": "zählsatz",
    "text": "Diese Seite führt 51 Codes . Zusammen mit den übrigen sechs Fehlercode-Seiten ergeben sich 493 fachliche Codes (4xx) und 119 Serverfehler (5xx), also 612 Codes — der vollständige Katalog der Plattform."
  },
  {
    "id": "api/error-codes-billing#weiterführendes",
    "path": "/de/api/error-codes-billing",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-billing",
    "pageTitle": "Fehlercodes: Abrechnung",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Das Fehlerformat Einen Webhook-Endpunkt anlegen Handbuch: Abrechnung"
  },
  {
    "id": "api/error-codes-content",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "anchor": "",
    "text": "Die Codes der Bereiche Media-Library, Composition, Scheduling, App-Catalog, Marketplace, App-Runtime, Feeds und Advertising: Medien und Ordner, Szenen und Vorlagen, Playlisten und Programme, App-Releases, Installationen, die Laufzeit installierter Apps, der Katalog der Nachrichten-Quellen und Werbung."
  },
  {
    "id": "api/error-codes-content#so-lesen-sie-die-tabellen",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "So lesen Sie die Tabellen",
    "anchor": "so-lesen-sie-die-tabellen",
    "text": "Die Tabellen lesen Sie so: Code ist der Wert im Feld code des Fehlerrumpfs — darauf schaltet Ihre Logik, nie auf message . Bedeutung und Typischer Auslöser ordnen den Fall ein. HTTP ist der tatsächlich gesendete Status. Erwartete Reaktion ist die Antwort Ihres Clients in einem festen Wortschatz, der auf allen sieben Fehlercode-Seiten gleich ist: „Ausweis prüfen“ (401), „Recht oder Paket prüfen“ (402, 403), „Eingabe korrigieren“ (400, 413, 415, 422), „neu laden, erneut senden“ (Versionskonflikte), „nicht wiederholen, Vorgang prüfen“ (404, 409, 410, 426), „warten (Retry-After)“ (429) und „später erneut, bei Wiederholung Trace-Id melden“ (5xx)."
  },
  {
    "id": "api/error-codes-content#die-codes",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "Die Codes",
    "anchor": "die-codes",
    "text": ""
  },
  {
    "id": "api/error-codes-content#medien-und-ordner-media-library",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "Medien und Ordner (Media-Library)",
    "anchor": "medien-und-ordner-media-library",
    "text": "Medien, Ordner, Uploads, dynamische Sammlungen sowie die Orte fremder Speicher und ihre Ordner-Abgleiche. Die drei Upload-Codes Upload.MimeTypeNotAllowed , Upload.QuotaExceeded und Upload.Sha256Mismatch sind die, die ein Skript beim Hochladen trifft. Die Kommandozeile beendet einen abgewiesenen Upload mit Exit 11 — siehe Ausgabe als JSON und Exit-Codes . Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Asset.NotFound Medium nicht gefunden Unbekannte Medien-Kennung 404 nicht wiederholen, Vorgang prüfen Asset.InUse Medium in Verwendung Löschung eines verwendeten Mediums 409 nicht wiederholen, Vorgang prüfen Asset.NotInTrash Medium liegt nicht im Papierkorb Endgültiges Löschen eines noch aktiven Mediums 409 nicht wiederholen, Vorgang prüfen Asset.InvalidStatusTransition Statuswechsel unzulässig Erneute Verarbeitung eines Mediums, das gerade hochgeladen oder verarbeitet wird 409 nicht wiederholen, Vorgang prüfen AssetVariant.NotFound Variante nicht vorhanden URL-Anforderung für eine nie erzeugte Variante 404 nicht wiederholen, Vorgang prüfen Folder.NotFound Ordner nicht gefunden Unbekannte Ordner-Kennung 404 nicht wiederholen, Vorgang prüfen Folder.MaxDepthExceeded Maximale Ordnertiefe überschritten Zu tiefe Verschachtelung 422 Eingabe korrigieren Folder.CycleDetected Ordner-Zyklus erkannt Verschieben in eigenen Unterbaum 422 Eingabe korrigieren Folder.Protected Marken-Ordner schreibgeschützt Umbenennen, Verschieben oder Löschen im Systemordner „Markenzeichen“. Lesen, Su"
  },
  {
    "id": "api/error-codes-content#szenen-und-vorlagen-composition",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "Szenen und Vorlagen (Composition)",
    "anchor": "szenen-und-vorlagen-composition",
    "text": "Abspieler-Sitzungen, Szenen, Layout-Vorlagen, Widgets und Integrität. Versionskonflikte beim Speichern laufen über Conflict.OptimisticLock , nicht über einen Code dieses Bereichs. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion RendererSession.NotFound Abspieler-Sitzung nicht gefunden Unbekannte Sitzungs-Kennung 404 nicht wiederholen, Vorgang prüfen RendererSession.AlreadyRevoked Abspieler-Sitzung bereits widerrufen Doppelter Widerruf 409 nicht wiederholen, Vorgang prüfen Scene.NotFound Szene nicht gefunden Unbekannte Szenen-Kennung 404 nicht wiederholen, Vorgang prüfen Scene.PublishValidationFailed Veröffentlichung ungültig Szene erfüllt die Regeln zum Veröffentlichen nicht 422 Eingabe korrigieren Scene.PublishBlocked Veröffentlichung blockiert Offene Integritäts-Probleme 409 nicht wiederholen, Vorgang prüfen Scene.Blocked Szene durch Sperrkaskade blockiert Neue Zuweisung (Programm, Übersteuerung, Playlist) einer Szene, die eine App verwendet, die das gebuchte Paket nicht mehr deckt. Steht hier und nicht bei Scheduling, weil die SZENE blockiert ist. 422 statt 403, weil zwei Wege hinausführen: Paket vergrößern oder die App aus der Szene nehmen 422 Eingabe korrigieren Scene.InUse Szene in Verwendung Löschung einer verwendeten Szene 409 nicht wiederholen, Vorgang prüfen SceneVersion.NotFound Szenen-Version nicht gefunden Unbekannte Version 404 nicht wiederholen, Vorgang prüfen SceneVersion.ThumbnailNotFound Kein Vorschaubild zur Version Abruf des Vorschaubilds einer V"
  },
  {
    "id": "api/error-codes-content#playlisten-und-programme-scheduling",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "Playlisten und Programme (Scheduling)",
    "anchor": "playlisten-und-programme-scheduling",
    "text": "Playlisten, Programme und die Zuweisung am Display. Schedule.ContentNotFound deckt Szene, Playlist und Medium in einem Code ab. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Playlist.NotFound Playlist nicht gefunden Unbekannte Playlist-Kennung 404 nicht wiederholen, Vorgang prüfen Playlist.Empty Playlist leer Verwendung ohne Einträge 422 Eingabe korrigieren Playlist.InUse Playlist in Verwendung Löschung einer verwendeten Playlist 409 nicht wiederholen, Vorgang prüfen Playlist.SceneNotFound Szene der Playlist fehlt Eintrag verweist auf unbekannte Szene 422 Eingabe korrigieren Playlist.AssetNotFound Medium der Playlist fehlt Eintrag verweist auf unbekanntes Medium 422 Eingabe korrigieren Playlist.AutoSourceItemsForbidden Automatische Playlist mit Einträgen Ordner-/Tag-Quelle mit gepflegter Sequenz 422 Eingabe korrigieren Playlist.SourceNotAuto Quelle nicht automatisch Quellen-Vorschau für eine manuelle Playlist 422 Eingabe korrigieren Schedule.NotFound Programm nicht gefunden Unbekannte Programm-Kennung 404 nicht wiederholen, Vorgang prüfen Schedule.ContentNotFound Inhalt des Programms fehlt Verweis auf unbekannte Szene, Playlist oder unbekanntes Medium 422 Eingabe korrigieren Schedule.InvalidRecurrence Ungültige Wiederholung Fehlerhafte Wiederholungsregel eines Programmpunkts 422 Eingabe korrigieren Schedule.Assigned Programm ist zugewiesen Löschung eines Programms, das noch auf einem Gerät läuft 409 nicht wiederholen, Vorgang prüfen Schedule.ItemNotFound Programmp"
  },
  {
    "id": "api/error-codes-content#app-katalog-app-catalog",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "App-Katalog (App-Catalog)",
    "anchor": "app-katalog-app-catalog",
    "text": "Releases, Manifest, der Import aus der Registry und Kanäle — die Betreiberseite des Katalogs. Eine Fassung kommt als signiertes Artefakt aus der Registry in den Katalog. Ist das Artefakt nicht in Ordnung, antwortet der Import mit AppRelease.RegistryImportFailed (422), ohne Zugang zur Registry mit AppRelease.RegistryUnavailable (503). Nach stable hebt keine Person allein: Eine Person fordert an, eine zweite bestätigt, die Codes AppReleaseApproval.* gehören zu diesem Weg. Wie eine Fassung signiert wird und welche Aufrufe sie durch die Kanäle bringen, steht unter Signieren und der Weg in den Katalog . Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion AppRelease.NotFound Release nicht gefunden Unbekannte Release-Kennung 404 nicht wiederholen, Vorgang prüfen AppRelease.VersionAlreadyExists Fassung existiert bereits Die Fassung steht schon mit anderen Bytes im Katalog 409 nicht wiederholen, Vorgang prüfen AppRelease.AlreadyRetired Release bereits stillgelegt Doppelte Stilllegung 409 nicht wiederholen, Vorgang prüfen Manifest.ValidationFailed Manifest ungültig Manifest verletzt das Schema 422 Eingabe korrigieren Channel.InvalidTransition Ungültiger Kanal-Übergang Unzulässiger Kanalwechsel (kein Überspringen) 409 nicht wiederholen, Vorgang prüfen AppStatusOverride.NotFound Sonderregel nicht gefunden Zurücknehmen einer Regel, die es für dieses Paar aus Organisation und App nicht gibt 404 nicht wiederholen, Vorgang prüfen AppRelease.RegistryArtifactNotFound Fassung nicht in der"
  },
  {
    "id": "api/error-codes-content#marktplatz-marketplace",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "Marktplatz (Marketplace)",
    "anchor": "marktplatz-marketplace",
    "text": "Installation und Erteilungen aus Sicht der Organisation, dazu die Bitten an die Inhaber und die Löschung der Daten einer deinstallierten App. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion App.NotFound App nicht gefunden Unbekannte App-Kennung 404 nicht wiederholen, Vorgang prüfen App.NotVisible App nicht sichtbar Kein Zugriff auf die App 403 Recht oder Paket prüfen Grant.AlreadyInstalled Bereits installiert Doppelte Installation 409 nicht wiederholen, Vorgang prüfen App.NotInstalled App nicht installiert Aktion ohne Installation 409 nicht wiederholen, Vorgang prüfen Grant.PendingReacceptance Neu-Zustimmung ausstehend Nutzung vor erneuter Zustimmung 409 nicht wiederholen, Vorgang prüfen App.ManifestHashMismatch Manifest-Hash weicht ab Zustimmung zu anderem Manifest 409 nicht wiederholen, Vorgang prüfen App.UpdateUnavailable Kein solches Update Wechsel auf eine Fassung, die nicht über der installierten liegt, Annehmen oder Ablehnen ohne anstehendes Update, Ablehnen nach Ablauf der Frist. details.reason nennt den Fall 409 nicht wiederholen, Vorgang prüfen OwnerRequest.NotApplicable Nichts zu entscheiden Bitte an die Inhaber um eine installierte App, um ein Update, das nicht ansteht, oder von einem Konto, das selbst entscheiden darf. details.reason nennt installed , no-pending-update oder can-decide 409 nicht wiederholen, Vorgang prüfen AppData.NotDeletable Daten nicht löschbar Vormerken der Daten einer installierten App, einer App ohne Daten oder einer schon vorgemer"
  },
  {
    "id": "api/error-codes-content#app-laufzeit-app-runtime",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "App-Laufzeit (App-Runtime)",
    "anchor": "app-laufzeit-app-runtime",
    "text": "Abruf-Zwischenspeicher und geplante Aufträge installierter Apps. Fetch.Failed ist ein bewusster 502 unter den Fachcodes: Eine externe Quelle hat nicht verwertbar geantwortet. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion FetcherCache.NotFound Zwischenspeicher-Eintrag nicht gefunden Unbekannter Eintrag im Abruf-Zwischenspeicher 404 nicht wiederholen, Vorgang prüfen FetchKey.Invalid Abruf-Schlüssel ungültig Unbekannter oder fehlerhafter Abruf-Schlüssel 422 Eingabe korrigieren Fetch.Failed Datenabruf fehlgeschlagen Fehler beim Abruf einer externen Quelle 502 später erneut, bei Wiederholung Trace-Id melden JobSchedule.NotFound Auftrags-Zeitplan nicht gefunden Unbekannte Zeitplan-Kennung 404 nicht wiederholen, Vorgang prüfen Job.CronInvalid Zeitplan-Ausdruck ungültig Fehlerhafter Zeitplan-Ausdruck 422 Eingabe korrigieren"
  },
  {
    "id": "api/error-codes-content#nachrichten-quellen-feeds",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "Nachrichten-Quellen (Feeds)",
    "anchor": "nachrichten-quellen-feeds",
    "text": "Der Katalog der Nachrichten-Quellen, den der Betrieb pflegt und den die Nachrichten-Apps lesen. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion FeedSource.NotFound Nachrichten-Quelle nicht gefunden Ändern einer Quelle, die es im Katalog nicht gibt. details.sourceId nennt sie 404 nicht wiederholen, Vorgang prüfen"
  },
  {
    "id": "api/error-codes-content#werbung-advertising",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "Werbung (Advertising)",
    "anchor": "werbung-advertising",
    "text": "Werbepartner, Werbekampagnen, Buchungen, Motive, Werbefläche, Publikum, die Teilnahme am Werbenetz und die Erlösaufstellungen der Standorte. Eine fremde und eine erfundene Kennung antworten gleich mit 404. Ihre Organisation erfährt so nie, ob eine Werbekampagne einer anderen Organisation oder des Werbenetzes existiert. Ohne die Fähigkeit advertisingDirect antworten Anlegen und Ändern im eigenen Werbegeschäft mit Billing.FeatureNotEntitled (403). Lesen, Anhalten, Beenden und Archivieren bleiben offen, damit laufende Werbekampagnen auslaufen können. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Advertiser.NotFound Werbepartner nicht gefunden Unbekannte Kennung oder ein Werbepartner einer anderen Organisation 404 nicht wiederholen, Vorgang prüfen Advertiser.Archived Werbepartner archiviert Neue Werbekampagne, Buchung oder neues Motiv, Buchen oder Wiederaufnehmen einer Werbekampagne oder Ändern einer Buchung (außer Anhalten) für einen archivierten Werbepartner 409 nicht wiederholen, Vorgang prüfen AdCampaign.NotFound Werbekampagne nicht gefunden Unbekannte Kennung, eine Werbekampagne einer anderen Organisation oder eine des Werbenetzes, die Ihre Displays nicht erreicht 404 nicht wiederholen, Vorgang prüfen AdCampaign.InvalidPeriod Zeitraum verkehrt Die Werbekampagne beginnt nach ihrem Ende 422 Eingabe korrigieren AdCampaign.StatusTransitionInvalid Übergang nicht erlaubt Zurück in den Entwurf oder eine beendete Werbekampagne fortsetzen 409 nicht wiederholen, Vorgang pr"
  },
  {
    "id": "api/error-codes-content#zählsatz",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "Zählsatz",
    "anchor": "zählsatz",
    "text": "Diese Seite führt 120 Codes . Zusammen mit den übrigen sechs Fehlercode-Seiten ergeben sich 493 fachliche Codes (4xx) und 119 Serverfehler (5xx), also 612 Codes — der vollständige Katalog der Plattform."
  },
  {
    "id": "api/error-codes-content#weiterführendes",
    "path": "/de/api/error-codes-content",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-content",
    "pageTitle": "Fehlercodes: Medien, Szenen, Programme, Apps",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Das Fehlerformat Ausgabe als JSON und Exit-Codes Signieren und der Weg in den Katalog"
  },
  {
    "id": "api/error-codes-devices-operations",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "anchor": "",
    "text": "Die Codes der Bereiche Fleet, Playback, Realtime, Notifications, Analytics und Audit — und die der Betriebskonsole, die nur auf Routen des Plattformbetriebs entstehen."
  },
  {
    "id": "api/error-codes-devices-operations#so-lesen-sie-die-tabellen",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "So lesen Sie die Tabellen",
    "anchor": "so-lesen-sie-die-tabellen",
    "text": "Die Tabellen lesen Sie so: Code ist der Wert im Feld code des Fehlerrumpfs — darauf schaltet Ihre Logik, nie auf message . Bedeutung und Typischer Auslöser ordnen den Fall ein. HTTP ist der tatsächlich gesendete Status. Erwartete Reaktion ist die Antwort Ihres Clients in einem festen Wortschatz, der auf allen sieben Fehlercode-Seiten gleich ist: „Ausweis prüfen“ (401), „Recht oder Paket prüfen“ (402, 403), „Eingabe korrigieren“ (400, 413, 415, 422), „neu laden, erneut senden“ (Versionskonflikte), „nicht wiederholen, Vorgang prüfen“ (404, 409, 410, 426), „warten (Retry-After)“ (429) und „später erneut, bei Wiederholung Trace-Id melden“ (5xx)."
  },
  {
    "id": "api/error-codes-devices-operations#die-codes",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "Die Codes",
    "anchor": "die-codes",
    "text": ""
  },
  {
    "id": "api/error-codes-devices-operations#geräte-und-displays-fleet",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "Geräte und Displays (Fleet)",
    "anchor": "geräte-und-displays-fleet",
    "text": "Displays, Kopplung, Befehle, Etiketten, Leinwände, Standorte und Aktualisierungen. Device.NotPaired (409) ist ein Zustand, der von selbst vergeht: Das Gerät ist registriert, aber noch keiner Organisation zugeordnet. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Device.NotPaired Gerät noch keiner Organisation zugeordnet Selbstmeldung eines frisch registrierten Agenten (Fähigkeiten, Ereignisse, Bildschirmfotos), bevor jemand im Portal den Kopplungscode eingelöst hat. Eigener Code, weil dieser Vorlauf als Player.NotFound nicht von einem Ausfall zu unterscheiden war — er vergeht von selbst 409 nicht wiederholen, Vorgang prüfen Player.NotFound Display nicht gefunden Unbekannte Display-Kennung 404 nicht wiederholen, Vorgang prüfen Player.AlreadyDecommissioned Display bereits entfernt Aktion auf ein entferntes Display, auch ein zweites Entfernen 409 nicht wiederholen, Vorgang prüfen Player.ConfigInvalid Display-Konfiguration ungültig Fehlerhafte Konfiguration 422 Eingabe korrigieren Player.OutputNotAvailable Kein gemeldeter Anschluss an dieser Stelle „Display hinzufügen“ an einem Ausgang, den das Gerät nicht als belegt gemeldet hat (Kabel fehlt, oder die Fähigkeits-Meldung steht noch aus) 409 nicht wiederholen, Vorgang prüfen Player.ScreenIndexTaken Stelle bereits belegt „Display hinzufügen“ an einem Anschluss, an dem schon ein Display hängt 409 nicht wiederholen, Vorgang prüfen Player.ScreenshotNotFound Keine Aufnahme abgelegt Abruf des Bildschirmfotos als Bytes zu eine"
  },
  {
    "id": "api/error-codes-devices-operations#wiedergabe-playback",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "Wiedergabe (Playback)",
    "anchor": "wiedergabe-playback",
    "text": "Wiedergabepakete und die Meldungen der Anzeige. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Snapshot.NotFound Wiedergabepaket nicht gefunden Unbekanntes Wiedergabepaket eines Displays 404 nicht wiederholen, Vorgang prüfen Renderer.DiagnosticsRateLimited Diagnose-Häufigkeit überschritten Zu häufige Diagnose-Meldungen 429 warten (Retry-After)"
  },
  {
    "id": "api/error-codes-devices-operations#echtzeit-verbindungen-realtime",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "Echtzeit-Verbindungen (Realtime)",
    "anchor": "echtzeit-verbindungen-realtime",
    "text": "Nur Connection.NotFound tritt regulär als HTTP-Fehler auf. Die übrigen vier Codes sind Ablehnungsgründe der Echtzeit-Verbindung ohne Fehlerrumpf. Die HTTP-Spalte nennt ihr Äquivalent, falls sie doch einen HTTP-Pfad erreichen. Mit einem API-Schlüssel ist die Echtzeit-Verbindung nicht erreichbar. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Connection.AuthFailed Anmeldung der Verbindung fehlgeschlagen Aufbau der Echtzeit-Verbindung ohne gültigen Ausweis 401 Ausweis prüfen Connection.NotFound Verbindung nicht gefunden Unbekannte Verbindungs-Kennung 404 nicht wiederholen, Vorgang prüfen Connection.RateLimited Verbindungs-Rate überschritten Zu viele Verbindungsversuche 429 warten (Retry-After) Subscription.PermissionDenied Recht auf das Thema fehlt Kein Zugriff auf das Thema 403 Recht oder Paket prüfen Topic.InvalidFormat Themen-Format ungültig Fehlerhafter Themen-Name 422 Eingabe korrigieren"
  },
  {
    "id": "api/error-codes-devices-operations#benachrichtigungen-und-webhooks-notifications",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "Benachrichtigungen und Webhooks (Notifications)",
    "anchor": "benachrichtigungen-und-webhooks-notifications",
    "text": "Postfach, Zustellwege, E-Mail und Webhooks. Die fünf Webhook-Codes — WebhookEndpoint.NotFound (404), WebhookEndpoint.Disabled (409), WebhookEndpoint.UrlInvalid (422), WebhookEndpoint.EventUnknown (422), WebhookDelivery.NotFound (404) — treffen die Verwaltung der Endpunkte. Was bei ausbleibenden Zustellungen zu tun ist, steht unter Wenn keine Zustellung ankommt . Email.ProviderUnavailable ist ein bewusster 502 unter den Fachcodes. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion Notification.NotFound Benachrichtigung nicht gefunden Unbekannte Benachrichtigungs-Kennung 404 nicht wiederholen, Vorgang prüfen Preferences.InvalidChannel Ungültiger Kanal Unbekannter Benachrichtigungskanal 422 Eingabe korrigieren WebhookEndpoint.NotFound Webhook-Endpunkt nicht gefunden Unbekannte Endpunkt-Kennung 404 nicht wiederholen, Vorgang prüfen WebhookEndpoint.Disabled Webhook-Endpunkt deaktiviert Nutzung eines deaktivierten Endpunkts 409 nicht wiederholen, Vorgang prüfen WebhookEndpoint.UrlInvalid Webhook-URL ungültig Fehlerhafte Ziel-URL 422 Eingabe korrigieren WebhookEndpoint.EventUnknown Ereignisart unbekannt Ein Eintrag in events steht nicht im Ereignis-Katalog und hat auch nicht die Form <bereich>.* oder * . Die Antwort nennt die unbekannten Namen unter details.unknownEvents 422 Eingabe korrigieren WebhookDelivery.NotFound Zustellung nicht gefunden Unbekannte Zustellungs-Kennung 404 nicht wiederholen, Vorgang prüfen Email.ProviderUnavailable E-Mail-Anbieter nicht erreichbar Ausfa"
  },
  {
    "id": "api/error-codes-devices-operations#auswertungen-analytics",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "Auswertungen (Analytics)",
    "anchor": "auswertungen-analytics",
    "text": "Berichte und Abfragen. Dimension.Unknown ersetzt eine stille Leere: Das Detail nennt die erlaubten Achsen. Die freie Auswertung prüft jede Abfrage gegen den Kennzahlen-Katalog: AnalyticsQuery.NotAllowed nennt in details.reason denselben Grund, den GET /v1/analytics/catalog je Kombination aus Kennzahl und Dimension liefert. Ein abgelaufener oder widerrufener Verweis antwortet mit 410 und nicht mit 404: Den Verweis gab es, er gilt nur nicht mehr. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion AnalyticsReport.NotFound Bericht nicht gefunden Unbekannte Berichts-Kennung 404 nicht wiederholen, Vorgang prüfen GeneratedReport.NotFound Generierter Bericht nicht gefunden Unbekannter Berichtslauf 404 nicht wiederholen, Vorgang prüfen GeneratedReport.Expired Erzeugter Bericht abgelaufen Abgelaufener Download 410 nicht wiederholen, Vorgang prüfen AnalyticsQuery.Invalid Abfrage ungültig Fehlerhafte Auswertungs-Abfrage 422 Eingabe korrigieren Metric.Unknown Kennzahl unbekannt Kennzahl existiert nicht 422 Eingabe korrigieren Dimension.Unknown Achse trägt die Kennzahl nicht Aufschlüsselung nach einer Dimension, die der Metrik-Katalog für diese Kennzahl nicht führt. Der Code ersetzt stille Leere — vorher kam ein leeres Ergebnis ohne Hinweis. Das Detail nennt die erlaubten Achsen 422 Eingabe korrigieren AnalyticsQuery.CardinalityExceeded Zu viele Wertekombinationen Abfrage mit zu vielen Achsen 422 Eingabe korrigieren AnalyticsQuery.NotAllowed Kombination nicht erlaubt Kennzahl und Di"
  },
  {
    "id": "api/error-codes-devices-operations#prüfprotokoll-audit",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "Prüfprotokoll (Audit)",
    "anchor": "prüfprotokoll-audit",
    "text": "Protokolleinträge, Exporte und Pseudonymisierung aus Sicht der Organisation. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion AuditEntry.NotFound Protokolleintrag nicht gefunden Unbekannte Eintrags-Kennung 404 nicht wiederholen, Vorgang prüfen AuditExport.NotFound Protokoll-Export nicht gefunden Unbekannte Export-Kennung 404 nicht wiederholen, Vorgang prüfen AuditExport.NotReady Protokoll-Export nicht fertig Download vor Fertigstellung 409 nicht wiederholen, Vorgang prüfen Audit.CrossTenantForbidden Organisationsübergreifend verboten Zugriff auf das Prüfprotokoll einer fremden Organisation 403 Recht oder Paket prüfen Pseudonymization.ReasonRequired Begründung erforderlich Pseudonymisierung ohne Grund (≥20 Zeichen) 422 Eingabe korrigieren Pseudonymization.JobNotFound Pseudonymisierungs-Auftrag fehlt Unbekannte Auftrags-Kennung 404 nicht wiederholen, Vorgang prüfen Pseudonym.NotFound Pseudonym nicht gefunden Abfrage ohne Treffer 404 nicht wiederholen, Vorgang prüfen"
  },
  {
    "id": "api/error-codes-devices-operations#betriebskonsole-ops-console",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "Betriebskonsole (Ops-Console)",
    "anchor": "betriebskonsole-ops-console",
    "text": "Diese Codes entstehen ausschließlich auf Routen des Plattformbetriebs: die Gerätesoftware samt dem Kanal eines Geräts, Wartungsfenster und Störungen. Ein Schlüssel einer Organisation erreicht diese Routen nicht. Die Tabelle steht hier der Vollständigkeit halber. Code Bedeutung Typischer Auslöser HTTP Erwartete Reaktion DeviceSoftware.ComponentNotSupported Baustein ohne Lieferweg Holen, Freigeben oder Ausliefern für einen Baustein, den diese Fassung der Plattform noch nicht ausliefert. Heute ist das alles außer der Anzeige 422 Eingabe korrigieren DeviceSoftware.BuildNotFound Bau nicht in der Ablage Freigabe eines Baus, den die Umgebung noch nicht aus der Registry geholt hat 404 nicht wiederholen, Vorgang prüfen DeviceSoftware.ArtifactNotFound Archiv nicht in der Ablage Abruf eines Archivs mit unbekanntem Baustein, Bau oder Namen 404 nicht wiederholen, Vorgang prüfen DeviceSoftware.RegistryArtifactNotFound Bau nicht in der Registry Der Tag existiert nicht (mehr) in der Registry 404 nicht wiederholen, Vorgang prüfen DeviceSoftware.RegistryImportFailed Holen aus der Registry gescheitert Zugang fehlt, das Manifest ist unvollständig, eine Prüfsumme weicht ab, ein Profil fehlt oder die Beschreibung im Archiv widerspricht dem Manifest. Das Detail nennt die Ursache 422 Eingabe korrigieren DeviceSoftware.NotReleasable Bau nicht freigebbar Der Bau verlangt eine Fassung der Schnittstelle, die die Umgebung nicht spricht, oder ein Profil oder Archiv fehlt 422 Eingabe korrigieren DeviceSoft"
  },
  {
    "id": "api/error-codes-devices-operations#zählsatz",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "Zählsatz",
    "anchor": "zählsatz",
    "text": "Diese Seite führt 102 Codes . Zusammen mit den übrigen sechs Fehlercode-Seiten ergeben sich 493 fachliche Codes (4xx) und 119 Serverfehler (5xx), also 612 Codes — der vollständige Katalog der Plattform."
  },
  {
    "id": "api/error-codes-devices-operations#weiterführendes",
    "path": "/de/api/error-codes-devices-operations",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-devices-operations",
    "pageTitle": "Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Das Fehlerformat Wenn keine Zustellung ankommt"
  },
  {
    "id": "api/error-codes-server",
    "path": "/de/api/error-codes-server",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-server",
    "pageTitle": "Fehlercodes: Serverfehler (5xx)",
    "section": "Fehlercodes: Serverfehler (5xx)",
    "anchor": "",
    "text": "Die Codes, auf die ein Client nicht fachlich reagieren kann: verletzte Invarianten, Fehler beim Start, Datenbank und nachgelagerte Dienste. Hier gibt es keine Reaktionsspalte — die Reaktion ist je Abschnitt dieselbe."
  },
  {
    "id": "api/error-codes-server#so-lesen-sie-die-tabellen",
    "path": "/de/api/error-codes-server",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-server",
    "pageTitle": "Fehlercodes: Serverfehler (5xx)",
    "section": "So lesen Sie die Tabellen",
    "anchor": "so-lesen-sie-die-tabellen",
    "text": "Code ist der Wert im Feld code des Fehlerrumpfs, HTTP der gesendete Status. Eine Spalte für die Reaktion gibt es hier nicht, weil sie je Abschnitt dieselbe ist: Bei 500 wiederholen Sie nicht und melden die traceId aus dem Fehlerrumpf. Bei 502 , 503 und 504 versuchen Sie es später erneut und beachten retryAfterSeconds im Rumpf und Retry-After in den Kopfzeilen. Kommt derselbe Fehler wieder, melden Sie die Trace-Id. Neun Codes senden einen 5xx und stehen trotzdem nicht hier, sondern bei ihrem Fachbereich: Sie sind dort bewusst gesetzt, weil ein bestimmter nachgelagerter Dienst ausfällt oder das Haus für einen Augenblick nicht rechenbereit ist und der Aufrufer weiß, woran es liegt. Es sind Provisioning.SagaFailed (500) unter Organisation, Zugang, Beitritt , Stripe.AdapterError (502), Billing.VatIdCheckUnavailable (503), Billing.PriceBackfillPending (503) und Billing.EInvoiceGuardFailed (500) unter Abrechnung , Social.ProviderUnavailable (503) unter Konten und Sitzungen , Email.ProviderUnavailable (502) unter Geräte, Wiedergabe, Meldungen, Betrieb sowie AppRelease.RegistryUnavailable (503) und Fetch.Failed (502) unter Medien, Szenen, Programme, Apps ."
  },
  {
    "id": "api/error-codes-server#die-codes",
    "path": "/de/api/error-codes-server",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-server",
    "pageTitle": "Fehlercodes: Serverfehler (5xx)",
    "section": "Die Codes",
    "anchor": "die-codes",
    "text": ""
  },
  {
    "id": "api/error-codes-server#allgemein-internal-errors",
    "path": "/de/api/error-codes-server",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-server",
    "pageTitle": "Fehlercodes: Serverfehler (5xx)",
    "section": "Allgemein (Internal, Errors)",
    "anchor": "allgemein-internal-errors",
    "text": "Nicht klassifizierte Ausnahmen und Defekte im Fehlerkatalog selbst. Reaktion: nicht wiederholen, Trace-Id melden. Code Bedeutung Typischer Auslöser HTTP Internal.Unexpected Unerwarteter Fehler Nicht klassifizierte Ausnahme 500 Errors.CatalogInconsistent Fehlerkatalog inkonsistent Katalog-Validierung fehlgeschlagen 500 Errors.UnknownCode Unbekannter Fehlercode Nicht registrierter Code 500 Errors.SerializationFailed Fehler-Serialisierung fehlgeschlagen Antwort nicht serialisierbar 500"
  },
  {
    "id": "api/error-codes-server#rechte-zur-laufzeit-und-beim-start",
    "path": "/de/api/error-codes-server",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-server",
    "pageTitle": "Fehlercodes: Serverfehler (5xx)",
    "section": "Rechte zur Laufzeit und beim Start",
    "anchor": "rechte-zur-laufzeit-und-beim-start",
    "text": "Fehlkonfigurationen der Rechteprüfung — sie zeigen sich beim Start oder an einer einzelnen Route. Reaktion: nicht wiederholen, Trace-Id melden. Code Bedeutung Typischer Auslöser HTTP Internal.AuthGuardConfigError Anmeldeprüfung falsch konfiguriert Fehlerhafte Verdrahtung der Anmeldeprüfung 500 Internal.PermissionPipelineMisconfigured Rechte-Prüfkette falsch konfiguriert Fehlende Bausteine der Prüfkette 500 Internal.PermissionResourceParamMissing Ressourcen-Parameter fehlt Beschreibung der Route unvollständig 500 Internal.PermissionResourceIdMalformed Ressourcen-Kennung fehlerhaft Ungültige Kennung bei der Auflösung 500 Internal.PermissionResourceTypeUnknown Ressourcen-Typ unbekannt Nicht registrierter Typ 500 Internal.PermissionCatalogInconsistent Rechte-Katalog inkonsistent Katalog-Validierung fehlgeschlagen 500 Internal.PermissionMetadataMalformed Rechte-Angaben fehlerhaft Ungültige Metadaten 500 Internal.PermissionRuntimeUnknownActorKind Unbekannte Art des Aufrufers Aufrufer-Art nicht vorgesehen 500"
  },
  {
    "id": "api/error-codes-server#datenbank",
    "path": "/de/api/error-codes-server",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-server",
    "pageTitle": "Fehlercodes: Serverfehler (5xx)",
    "section": "Datenbank",
    "anchor": "datenbank",
    "text": "Verletzte Regeln der Datenbank-Schicht, etwa eine Abfrage ohne Kontext der Organisation. Die Codes tragen den Namen der Datenbank-Bibliothek wörtlich, weil sie so im Katalog stehen. Reaktion: nicht wiederholen, Trace-Id melden. Code Bedeutung Typischer Auslöser HTTP Prisma.TenantScopeMissing Kontext der Organisation fehlt Abfrage ohne Kontext einer Organisation 500 Prisma.RawQueryOnTenantIsolatedModel Roh-Abfrage auf einem getrennt geführten Modell Umgehung der Trennung zwischen Organisationen 500 Prisma.AsPlatformNoRequestScope Plattform-Abfrage ohne Anfrage-Kontext Plattform-Abfrage ohne Kontext 500 Prisma.AsPlatformInvalidReason Ungültige Begründung einer Plattform-Abfrage Fehlende/leere Begründung 500 Prisma.NestedTransactionError Verschachtelte Transaktion Transaktion in Transaktion 500 Prisma.UnknownApp Unbekannte App (Datenbank) App-Client für unbekannte App 500 Prisma.AppHasNoSchema App ohne Schema App-Query ohne eigenes Schema 500 Prisma.AppClientLoadFailed App-Client-Laden fehlgeschlagen Fehler beim Laden des App-Clients 500 Prisma.SchemaRegistryFileNotFound Schema-Verzeichnis-Datei fehlt Start ohne Verzeichnis-Datei 500 Prisma.SchemaRegistryInvalidFormat Schema-Verzeichnis ungültig Fehlerhaftes Verzeichnis-Format 500 Prisma.SchemaRegistryDuplicate Schema-Verzeichnis-Duplikat Doppelter Verzeichnis-Eintrag 500 Prisma.BootSchemaValidationFailed Schema-Prüfung beim Start fehlgeschlagen Schema-Prüfung beim Start 500 Prisma.UnknownOperation Unbekannte Datenbank-Operation"
  },
  {
    "id": "api/error-codes-server#weitere-bereiche",
    "path": "/de/api/error-codes-server",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-server",
    "pageTitle": "Fehlercodes: Serverfehler (5xx)",
    "section": "Weitere Bereiche",
    "anchor": "weitere-bereiche",
    "text": "HTTP, Eingabeprüfung, Anfrage-Kontext, Ereignisse, Zwischenspeicher, Beobachtung, OpenAPI, Prüfprotokoll, Aufträge, Apps und Verträge. Alle antworten 500 bis auf zwei: Internal.HttpRequestTimeout (504) und Internal.OpenApiNotInitialized (503) — bei diesen beiden ist ein späterer Versuch sinnvoll, bei allen anderen nicht. Code Bedeutung Typischer Auslöser HTTP Internal.HttpResponseSerializationFailed HTTP-Antwort nicht serialisierbar Fehler beim Aufbau der Antwort 500 Internal.HttpRequestTimeout Zeitüberschreitung der Anfrage Zeitüberschreitung der Anfrage 504 Internal.HttpModuleOptionsInvalid HTTP-Modul-Optionen ungültig Fehlkonfiguration beim Start 500 Internal.HttpPrismaFilterMisconfigured Datenbank-Fehlerfilter falsch konfiguriert Fehlkonfiguration beim Start 500 Internal.ValidationModuleOptionsInvalid Optionen des Prüf-Moduls ungültig Fehlkonfiguration beim Start 500 Internal.ValidationAllowlistConsistencyFailed Freigabeliste inkonsistent Prüfung beim Start fehlgeschlagen 500 Internal.ValidationProtocolConsistencyFailed Protokoll-Konsistenz verletzt Prüfung beim Start fehlgeschlagen 500 Internal.RequestScopeNotInitialized Anfrage-Kontext nicht initialisiert Zugriff außerhalb des Kontexts 500 Internal.RequestScopeIdentityMissing Identität im Anfrage-Kontext fehlt Kontext ohne Identität 500 Internal.RequestScopeOptionsInvalid Optionen des Anfrage-Kontexts ungültig Fehlkonfiguration beim Start 500 Events.MissingRequestScope Ereignis ohne Anfrage-Kontext Auslösen außerhalb de"
  },
  {
    "id": "api/error-codes-server#nachgelagerte-dienste-integration",
    "path": "/de/api/error-codes-server",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-server",
    "pageTitle": "Fehlercodes: Serverfehler (5xx)",
    "section": "Nachgelagerte Dienste (Integration)",
    "anchor": "nachgelagerte-dienste-integration",
    "text": "Ein fremder Dienst oder ein Baustein der eigenen Infrastruktur antwortet nicht. Durchgereichte Fehler eines fremden Dienstes antworten 502 , der Ausfall eines plattformeigenen Bausteins 503 . Reaktion bei beiden: später erneut, retryAfterSeconds und Retry-After beachten. Bleibt es dabei, Trace-Id melden. Code Bedeutung Typischer Auslöser HTTP Integration.VaultUnreachable Geheimnisspeicher nicht erreichbar Ausfall des Geheimnisspeichers 502 Integration.JwtKeyStoreUnavailable Speicher der Signaturschlüssel nicht verfügbar Signaturschlüssel nicht ladbar 503 Integration.MtlsCaUnavailable Zertifikatsstelle der Geräte nicht verfügbar Zertifikatsstelle nicht erreichbar 503 Integration.IdentityResolverFailed Identitätsauflösung fehlgeschlagen Fehler bei der Identitätsauflösung 503 Integration.AccessReadModelUnreachable Lesesicht der Rechte nicht erreichbar Ausfall der Lesesicht der Rechte 503 Integration.PrismaConnectionLost Datenbank-Verbindung verloren Verbindungsabbruch zur Datenbank 502 Integration.PrismaQueryTimeout Zeitüberschreitung einer Datenbank-Abfrage Zeitüberschreitung einer Datenbank-Abfrage 502 Integration.PrismaTransactionTimeout Zeitüberschreitung einer Datenbank-Transaktion Transaktions-Zeitüberschreitung 502 Integration.PrismaTransactionMaxWait Wartezeit einer Datenbank-Transaktion Wartezeit-Grenze überschritten 502 Integration.PrismaForeignKey Fremdschlüsselfehler der Datenbank Verweis auf einen nicht vorhandenen Datensatz auf Datenbank-Ebene 502 Integration.Outbo"
  },
  {
    "id": "api/error-codes-server#zählsatz",
    "path": "/de/api/error-codes-server",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-server",
    "pageTitle": "Fehlercodes: Serverfehler (5xx)",
    "section": "Zählsatz",
    "anchor": "zählsatz",
    "text": "Diese Seite führt 119 Codes — alle Serverfehler des Katalogs. Zusammen mit den 493 fachlichen Codes der sechs anderen Fehlercode-Seiten sind es 612 Codes."
  },
  {
    "id": "api/error-codes-server#weiterführendes",
    "path": "/de/api/error-codes-server",
    "area": "api",
    "areaTitle": "API",
    "page": "error-codes-server",
    "pageTitle": "Fehlercodes: Serverfehler (5xx)",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Das Fehlerformat Release-Züge, Notizen und Abkündigungen"
  },
  {
    "id": "api/read-reference",
    "path": "/de/api/read-reference",
    "area": "api",
    "areaTitle": "API",
    "page": "read-reference",
    "pageTitle": "Die Referenz lesen",
    "section": "Die Referenz lesen",
    "anchor": "",
    "text": "Nach dieser Anleitung finden Sie in der Referenz unter /api/reference eine Operation, lesen das benötigte Recht, das Sicherheitsverfahren, das Beispiel und die Stabilität ab und bauen daraus einen Aufruf mit curl."
  },
  {
    "id": "api/read-reference#was-die-referenz-zeigt",
    "path": "/de/api/read-reference",
    "area": "api",
    "areaTitle": "API",
    "page": "read-reference",
    "pageTitle": "Die Referenz lesen",
    "section": "Was die Referenz zeigt",
    "anchor": "was-die-referenz-zeigt",
    "text": "Die Referenz ist die öffentliche Spezifikation des aktuellen Release, interaktiv dargestellt: 443 Operationen in 19 Bereichen und 7 Gruppen, jede Operation mit deutscher Kurzbeschreibung, jedes Schema mit einem Beispiel aus einem zusammenhängenden Demo-Universum. Was die Spezifikation als internal führt, steht weder in der Referenz noch in der Datei /api/openapi.json — darunter die gesamte Betreiber-Fläche unter /v1/platform/… , die Brücken zu Gerät und Anzeige-Anwendung und die Betriebsrouten unter /health/… ."
  },
  {
    "id": "api/read-reference#schritte",
    "path": "/de/api/read-reference",
    "area": "api",
    "areaTitle": "API",
    "page": "read-reference",
    "pageTitle": "Die Referenz lesen",
    "section": "Schritte",
    "anchor": "schritte",
    "text": "Wählen Sie in der linken Navigation die Gruppe und darunter den Bereich. Die sieben Gruppen sind Identität & Zugang , Organisation & Abrechnung , Inhalte & Planung , Apps & Marketplace , Flotte & Wiedergabe , Überwachung & Meldungen und Plattform-Betrieb . Die ersten sechs sind die Fläche für Organisationen; unter Plattform-Betrieb stehen hier nur die beiden anmeldefreien Betriebsrouten. Was jeder Bereich enthält, steht unter Die Bereiche der Referenz . Öffnen Sie die Operation. Der Titel ist die deutsche Kurzbeschreibung („Listet Szenen gefiltert und seitenweise“), darunter stehen Methode und Pfad. Lesen Sie die Beschreibung. Sie nennt das benötigte Recht in der Form Benötigte Berechtigung: scene.list und bei öffentlichen Routen den Satz „Öffentlich — keine Anmeldung erforderlich“. Fehlt ein Recht, braucht die Route nur eine Anmeldung. Sehen Sie den Abschnitt security an. Er sagt maschinenlesbar, welcher Ausweis die Route annimmt: Eintrag Ausweis apiKey API-Schlüssel ak_… oauthAccessToken Zugriffstoken oat_… eines delegierten Zugangs , angenommen an denselben Stellen wie ein API-Schlüssel sessionJwt Zugriffstoken einer Sitzung eyJ… scimToken SCIM-Zugang sct_… , nur auf der Fläche unter /scim/v2 leere Liste öffentlich Ob ein Schlüssel die Route auch fachlich darf, entscheidet das Recht aus Schritt 3 — apiKey in security heißt nur, dass der Ausweis angenommen wird. Die Fläche unter /scim/v2 nennt ausschließlich scimToken , die Einwilligung eines delegierten Zugangs und die Lis"
  },
  {
    "id": "api/read-reference#optionen",
    "path": "/de/api/read-reference",
    "area": "api",
    "areaTitle": "API",
    "page": "read-reference",
    "pageTitle": "Die Referenz lesen",
    "section": "Optionen",
    "anchor": "optionen",
    "text": "Sie wollen So Die Spezifikation als Datei /api/openapi.json auf dieser Seite (Stand des Release) oder GET /v1/openapi.json an Ihrer Umgebung (genau der laufende Stand) Nur die Operationen, die ein Schlüssel erreicht Nach dem Recht filtern: schlüsselfähig sind heute asset.* , scene.* , layout-template.* , playlist.* , schedule.* , location.* , die vier Display-Rechte fleet.player.list , …read , …update , …command.issue , dazu fleet.web-display.manage , advertising.* , analytics.proof.read und analytics.proof.manage sowie billing.usage.read und analytics.query.run — siehe Anmeldung Die Fehler einer Operation Jede Operation listet 401, 403, 404, 409, 422, 429 und 502 mit den sieben zentralen Antworten; die konkreten Codes stehen auf den Fehlercode-Seiten"
  },
  {
    "id": "api/read-reference#ergebnis-prüfen",
    "path": "/de/api/read-reference",
    "area": "api",
    "areaTitle": "API",
    "page": "read-reference",
    "pageTitle": "Die Referenz lesen",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "Jede Operation, die Sie öffnen, trägt eine deutsche Kurzbeschreibung, und jedes Schema ein Beispiel. Der Aufruf aus Schritt 7 antwortet 200 mit items — oder mit einem Fehlerrumpf, dessen code Sie auf den Fehlercode-Seiten finden."
  },
  {
    "id": "api/read-reference#wenn-etwas-nicht-klappt",
    "path": "/de/api/read-reference",
    "area": "api",
    "areaTitle": "API",
    "page": "read-reference",
    "pageTitle": "Die Referenz lesen",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung Der Aufruf mit der Kennung aus dem Beispiel antwortet 404 Die Beispiele sind Demo-Daten. Kennungen aus Ihrer Organisation einsetzen — etwa aus einer Liste. 403 Permission.OperatorOnly Sie haben eine Route unter /v1/platform/… gerufen — die Betreiber-Fläche. Sie steht nicht in dieser Referenz; für Organisationen gibt es dort nichts zu rufen. Eine Route Ihrer Umgebung fehlt, obwohl sie antwortet Sie ist als internal geführt — etwa GET /health/ready oder eine Route der Betreiber-Fläche. Ihre Beschreibung steht nicht in der öffentlichen Fassung; verlassen Sie sich in einer Integration nur auf das, was hier steht. POST /v1/webhooks/stripe sieht aus wie ein Webhook-Endpunkt Es ist der Eingang des Zahlungsdienstleisters, kein Kunden-Endpunkt. Eigene Webhooks legen Sie unter Einen Webhook-Endpunkt anlegen an. Eine Route Ihrer Umgebung fehlt in der Referenz Ihre Umgebung läuft auf einem anderen Stand als das Release dieser Seite. GET /v1/openapi.json Ihrer Umgebung laden."
  },
  {
    "id": "api/read-reference#weiterführendes",
    "path": "/de/api/read-reference",
    "area": "api",
    "areaTitle": "API",
    "page": "read-reference",
    "pageTitle": "Die Referenz lesen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Die Bereiche der Referenz Basis-URL, /v1 und Stabilität Einen eigenen Client aus der Spezifikation erzeugen"
  },
  {
    "id": "api/reference-areas",
    "path": "/de/api/reference-areas",
    "area": "api",
    "areaTitle": "API",
    "page": "reference-areas",
    "pageTitle": "Die Bereiche der Referenz",
    "section": "Die Bereiche der Referenz",
    "anchor": "",
    "text": "Die ausgelieferte Spezifikation gliedert ihre 443 Operationen in 19 Bereiche (Tags) und 7 Gruppen — in genau dieser Reihenfolge zeigt sie die Referenz. Lesen Sie die Tabellen als Landkarte: Was ein Bereich enthält, ob ein API-Schlüssel dort etwas darf und welche Befehlsgruppe der Kommandozeile ihm entspricht. Die Spalte Mit Schlüssel leitet sich aus dem Rechte-Katalog ab: „ja“ heißt, die Rechte des Bereichs sind schlüsselfähig. „teilweise“ heißt, einzelne Routen brauchen nur eine Anmeldung oder ein schlüsselfähiges Recht, der Rest eine Sitzung. „Sitzung“ heißt, kein Recht des Bereichs ist für Schlüssel vorgesehen. „anmeldefrei“ heißt, die Route braucht keinen Ausweis. Was ein Schlüssel tragen kann, steht unter Anmeldung ."
  },
  {
    "id": "api/reference-areas#identität--zugang",
    "path": "/de/api/reference-areas",
    "area": "api",
    "areaTitle": "API",
    "page": "reference-areas",
    "pageTitle": "Die Bereiche der Referenz",
    "section": "Identität & Zugang",
    "anchor": "identität--zugang",
    "text": "Tag Was Sie dort finden Mit Schlüssel Kommandozeile identity Benutzerkonten, das eigene Profil und die Zugangsmittel einer Organisation: Konten anlegen, sperren und reaktivieren, zweiter Faktor mit Wiederherstellungscodes, API-Schlüssel, verknüpfte Anbieter-Konten, dazu der delegierte Zugang mit seinen Endpunkten außerhalb von /v1 und die SCIM-Fläche unter /scim/v2 . teilweise ( GET /v1/me ) user , api-key , invitation , operator sessions Alle Wege in eine Sitzung: Passwort, Anmelde-Link, plattformweite Anbieter, Unternehmens-Anmeldung, zweiter Faktor, Organisationswahl. Dazu Erneuern und Beenden, Passwort zurücksetzen, Sitzungsübernahme durch den Betreiber. Sitzung (öffentliche Anmelderouten) whoami access Rollen und einzelne Erteilungen, der Katalog aller vergebbaren Rechte und die wirksamen Rechte des angemeldeten Kontos. teilweise ( GET /v1/permission-catalog , GET /v1/me/permissions ) role , grant , permission onboarding Selbst-Registrierung mit E-Mail-Bestätigung, Einladungen samt öffentlicher Annahmeseite, Beitrittsanfragen und der Einrichtungsassistent. Sitzung invitation , self-registration"
  },
  {
    "id": "api/reference-areas#organisation--abrechnung",
    "path": "/de/api/reference-areas",
    "area": "api",
    "areaTitle": "API",
    "page": "reference-areas",
    "pageTitle": "Die Bereiche der Referenz",
    "section": "Organisation & Abrechnung",
    "anchor": "organisation--abrechnung",
    "text": "Tag Was Sie dort finden Mit Schlüssel Kommandozeile tenancy Die Organisation selbst: Stammdaten, Geschäftsprofil, Marke, eigene Domain und die Unternehmens-Anmeldung mit ihren nachgewiesenen Domänen. Betreiber führen die Liste der Organisationen und sperren oder geben frei. teilweise ( GET /v1/me/capabilities ) — (Stammdaten pflegen Sie im Portal) billing Paket buchen, wechseln, kündigen, reaktivieren. Zahlungsmittel und Rechnungen einsehen, Kontingente prüfen. Betreiber pflegen den Artikelkatalog und bearbeiten Rechnungen und Erstattungen. teilweise ( GET /v1/me/licensing ) subscription , invoice , catalog"
  },
  {
    "id": "api/reference-areas#inhalte--planung",
    "path": "/de/api/reference-areas",
    "area": "api",
    "areaTitle": "API",
    "page": "reference-areas",
    "pageTitle": "Die Bereiche der Referenz",
    "section": "Inhalte & Planung",
    "anchor": "inhalte--planung",
    "text": "Tag Was Sie dort finden Mit Schlüssel Kommandozeile cloud-connections Die Vollmachten auf fremde Speicher-Konten, aus denen die Mediathek ihre Orte speist: Rundlauf zur Zustimmung, Zustand aller Verbindungen, Trennen. Zugriffs- und Auffrischungs-Token verlassen den Server nie. Sitzung — media-library Der Medienbestand: Hochladen einschließlich mehrteiliger Uploads, Ordner und dynamische Sammlungen, Umbenennen und Verschieben in Mengen. Je Medium Download-Verweise, Varianten, Verwendungsnachweise, Papierkorb und endgültiges Löschen. ja asset , folder composition Szenen anlegen, aus Vorlagen erzeugen, veröffentlichen, archivieren und wiederherstellen. Jede Änderung versioniert, Fassungen vergleichen und zurückrollen, Integritätsbefunde prüfen und ausräumen. ja scene , scene-version , scene-template , layout , widget scheduling Was wann läuft: Playlisten zusammenstellen, Programme anlegen, anhalten und fortsetzen, Standardszene bestimmen. Dazu Kalenderansicht und Konfliktvorschau. ja playlist , schedule , display-group , player (Programm und Übersteuerung) advertising Die Werbung der Organisation, in der Stabilität beta : Werbepartner, Werbekampagnen samt Buchungen, die Reichweite einer Buchung ( GET /v1/advertising/booking-lines/{id}/reach ) und die Vorschau, was eine Buchung liefern würde ( POST /v1/advertising/booking-lines/preview ). Dazu Motive aus der eigenen Mediathek mit ihrer Freigabe, die Werbefläche je Display, das Publikum je Standort und die Teilnahme am Werbenetz s"
  },
  {
    "id": "api/reference-areas#apps--marketplace",
    "path": "/de/api/reference-areas",
    "area": "api",
    "areaTitle": "API",
    "page": "reference-areas",
    "pageTitle": "Die Bereiche der Referenz",
    "section": "Apps & Marketplace",
    "anchor": "apps--marketplace",
    "text": "Tag Was Sie dort finden Mit Schlüssel Kommandozeile app-catalog Die Herkunftsseite der Apps: Betreiber holen Releases aus der Registry, prüfen sie, geben sie frei (nach stable mit einer zweiten Person), stufen zurück, nehmen zurück, steuern Auslauffristen und setzen Ausnahmen je Organisation. Öffentlich ausgeliefert werden Manifest, Bündel und Autorenmodul einer App-Version sowie die Kataloge der Widgets ( GET /v1/widget-catalog , mit der Motiv-Bibliothek des Grafik-Widgets unter GET /v1/widget-catalog/graphics ) und der Ressourcentypen ( GET /v1/resource-type-catalog ) über alle Apps, dazu die Fassungen einer App mit Datum und dem, was in ihnen neu ist ( GET /v1/apps/{appId}/history ). teilweise (öffentliche Auslieferung, beide Kataloge) app-release , app-status marketplace Die Sicht der Organisation auf das App-Angebot: entdecken, installieren, aktualisieren, deinstallieren mit Vorschau der Folgen. Über ein Update entscheiden: die offenen Entscheidungen ( GET /v1/me/apps/updates ), die einer App ( GET /v1/me/apps/{appId}/update ), annehmen und ablehnen ( POST …/update/accept , POST …/update/reject ). Für Fassungen aus beta eintragen, für alle Apps ( GET|PUT /v1/me/apps/beta-enrollment ) oder je App ( POST|DELETE /v1/me/apps/{appId}/beta-enrollment ). Dazu der eigene Bestand — die installierten Apps ( GET /v1/me/apps ). Der Store liefert seine Lage fertig entschieden: die Übersicht als Karten ( GET /v1/me/apps/store ), den Eintrag einer App ( GET /v1/me/apps/store/{appId} ),"
  },
  {
    "id": "api/reference-areas#flotte--wiedergabe",
    "path": "/de/api/reference-areas",
    "area": "api",
    "areaTitle": "API",
    "page": "reference-areas",
    "pageTitle": "Die Bereiche der Referenz",
    "section": "Flotte & Wiedergabe",
    "anchor": "flotte--wiedergabe",
    "text": "Tag Was Sie dort finden Mit Schlüssel Kommandozeile fleet Die Abspielgeräte: Inbetriebnahme über Kopplungscode, Etiketten, Fernbefehle mit Rückmeldung, Displays entfernen. Geräte melden Lebenszeichen, Fähigkeiten und den Verlauf ihrer Aktualisierungen. Die Handlungen am Gerät selbst (neu koppeln, Werkszustand, entfernen, sperren) stehen unter identity . Hier liegen auch die Standorte als Ordner über den Displays, die Web-Displays , die ohne Gerät im Browser laufen, und die Bildschirmaufnahmen eines Displays als Zeitreihe ( GET /v1/players/{playerId}/screenshots ). teilweise ( location.* , fleet.player.list , …read , …update , …command.issue , fleet.web-display.manage ) device , player , location , playback playback Die Schnittstelle zum Abspieler: Wiedergabepaket laden, Start zurückmelden, Diagnoseprotokolle liefern. Dazu Vorschau von Widget-Daten und Auslieferung des Abspieler-Bündels. Sitzung (Gerät oder Betreiber) playback"
  },
  {
    "id": "api/reference-areas#überwachung--meldungen",
    "path": "/de/api/reference-areas",
    "area": "api",
    "areaTitle": "API",
    "page": "reference-areas",
    "pageTitle": "Die Bereiche der Referenz",
    "section": "Überwachung & Meldungen",
    "anchor": "überwachung--meldungen",
    "text": "Tag Was Sie dort finden Mit Schlüssel Kommandozeile audit Das revisionssichere Prüfprotokoll: Einträge durchsuchen, Vorgänge über ihre Korrelation bündeln, Auszüge exportieren. Betreiber pseudonymisieren personenbezogene Einträge und lösen Pseudonyme auf. Sitzung audit notifications Postfach im Portal, persönliche Zustellwege und Webhooks samt Zustellprotokoll, erneuter Zustellung und Wechsel des Geheimnisses, dazu der Ereigniskatalog , aus dem eine Abo-Liste entsteht. Sitzung webhook analytics Frei auswerten nach dem Kennzahlen-Katalog und das Ergebnis als CSV oder PDF ausgeben, dazu Überblick, Tag und Protokoll eines Displays, die eigenen Inhalte und die Lieferung einer Werbekampagne. Ansichten speichern, planmäßig zustellen und herunterladen, Kennzahlen abfragen, dazu der Aufräum-Überblick, der zählt, was liegen geblieben ist. Der Standort ist eine Achse der freien Auswertung. Dazu die Nachweise der Buchungen samt Zwischenstand, die Verweise für Werbepartner und — ohne Anmeldung — die Prüfung eines Nachweises und der Kampagnenbericht hinter einem Verweis. teilweise ( analytics.query.run , analytics.proof.read , analytics.proof.manage ) analytics , hygiene , proof"
  },
  {
    "id": "api/reference-areas#plattform-betrieb",
    "path": "/de/api/reference-areas",
    "area": "api",
    "areaTitle": "API",
    "page": "reference-areas",
    "pageTitle": "Die Bereiche der Referenz",
    "section": "Plattform-Betrieb",
    "anchor": "plattform-betrieb",
    "text": "Ein Bereich mit zwei anmeldefreien Routen — der Rest der Betreiber-Fläche steht nicht in dieser Spezifikation (siehe unten). Tag Was Sie dort finden Mit Schlüssel Kommandozeile status Die beiden Routen des Betriebs, die jeder abrufen darf: der Status der Plattform, wie ihn die Statusseite zeigt ( GET /v1/public/status ), und das laufende Wartungsfenster ( GET /v1/public/maintenance-status ). anmeldefrei health public"
  },
  {
    "id": "api/reference-areas#was-hier-nicht-steht-die-betreiber-fläche",
    "path": "/de/api/reference-areas",
    "area": "api",
    "areaTitle": "API",
    "page": "reference-areas",
    "pageTitle": "Die Bereiche der Referenz",
    "section": "Was hier nicht steht: die Betreiber-Fläche",
    "anchor": "was-hier-nicht-steht-die-betreiber-fläche",
    "text": "Die Schnittstelle trägt daneben rund 280 Operationen unter /v1/platform/… — Warteschlangen, Zwischenspeicher, Signaturschlüssel, Rechte-Katalog, Ereignis-Verteiler, Prüfprotokoll-Betrieb. Sie sind die Innenansicht des Hauses : Sie gelten dem Betreiber der Plattform, nicht Ihrer Organisation, und ein Schlüssel oder eine Sitzung einer Organisation bekommt dort 403 Permission.OperatorOnly . Diese Routen sind deshalb kein Teil dieser Referenz , und die Spezifikation unter /api/openapi.json enthält sie nicht: Ein erzeugter Client bekommt aus ihr nur, was Sie auch aufrufen dürfen. Wer die Plattform selbst betreibt, findet die Betreiberfassung an seiner eigenen Umgebung hinter einer Anmeldung."
  },
  {
    "id": "api/reference-areas#was-die-referenz-sonst-nicht-zeigt",
    "path": "/de/api/reference-areas",
    "area": "api",
    "areaTitle": "API",
    "page": "reference-areas",
    "pageTitle": "Die Bereiche der Referenz",
    "section": "Was die Referenz sonst nicht zeigt",
    "anchor": "was-die-referenz-sonst-nicht-zeigt",
    "text": "Fläche Warum nicht Die Betriebsrouten GET /health/live , GET /health/ready , GET /health/version und GET /metrics Sie sind als internal geführt: Sie beschreiben den Zustand einer Umgebung, nicht Ihren Bestand. Sie antworten weiter ohne Anmeldung, und szenera health fragt sie ab. Der Betrieb installierter Apps und die Auskunft über offene Echtzeit-Verbindungen ebenfalls internal — die Innenansicht der Laufzeit. Routen, die eine App mitbringt ( /v1/apps/<app-id>/… ) Welche es gibt, hängt vom App-Bestand Ihrer Umgebung ab. Ein Release-Stand kann sie nicht kennen. Was internal bedeutet und was die öffentliche Fassung stattdessen zusagt, steht unter Stabilitätsklassen und Auslauf ."
  },
  {
    "id": "api/reference-areas#weiterführendes",
    "path": "/de/api/reference-areas",
    "area": "api",
    "areaTitle": "API",
    "page": "reference-areas",
    "pageTitle": "Die Bereiche der Referenz",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Die Referenz lesen Stabilitätsklassen und Auslauf Befehle für Ihre Organisation"
  },
  {
    "id": "api/generate-client",
    "path": "/de/api/generate-client",
    "area": "api",
    "areaTitle": "API",
    "page": "generate-client",
    "pageTitle": "Einen eigenen Client aus der Spezifikation erzeugen",
    "section": "Einen eigenen Client aus der Spezifikation erzeugen",
    "anchor": "",
    "text": "Es gibt heute keinen veröffentlichten Client für die Szenera-API. Nach dieser Anleitung erzeugen Sie aus openapi.json Ihren eigenen und bilden die Konventionen der API darin ab: Ausweis, Fehler, Versionsprüfung, Idempotenz und Blättern."
  },
  {
    "id": "api/generate-client#der-ehrliche-stand",
    "path": "/de/api/generate-client",
    "area": "api",
    "areaTitle": "API",
    "page": "generate-client",
    "pageTitle": "Einen eigenen Client aus der Spezifikation erzeugen",
    "section": "Der ehrliche Stand",
    "anchor": "der-ehrliche-stand",
    "text": "Die Plattform hat einen TypeScript-Client, aber er ist Teil der Portale und wird nicht veröffentlicht; die Entwicklerseite verspricht keinen. Was es gibt, ist die Spezifikation — vollständig, mit Beispiel je Schema, im Format OpenAPI 3.1.0. Daraus erzeugt jeder gängige Generator einen Client."
  },
  {
    "id": "api/generate-client#schritte",
    "path": "/de/api/generate-client",
    "area": "api",
    "areaTitle": "API",
    "page": "generate-client",
    "pageTitle": "Einen eigenen Client aus der Spezifikation erzeugen",
    "section": "Schritte",
    "anchor": "schritte",
    "text": "Laden Sie die Spezifikation: Oder von Ihrer Umgebung, wenn deren Stand vom Release abweicht: curl -sS -o openapi.json \"$SZENERA_API/v1/openapi.json\" . Erzeugen Sie den Client mit einem Generator Ihrer Wahl. Prüfen Sie vorher, dass er OpenAPI 3.1 versteht — die Spezifikation nutzt Typen wie [\"string\", \"null\"] , die ein 3.0-Generator nicht kennt. Die 19 Tags der Spezifikation eignen sich als Modulgrenzen. Setzen Sie den Ausweis zentral: Authorization: Bearer ak_… an jedem Aufruf, aus einer Umgebungsvariablen, nie aus dem Quelltext. Ein Schlüssel läuft nicht nach 15 Minuten ab — Ihr Client braucht keine Auffrischung. Bilden Sie Fehler auf code ab. Jeder Fehler hat die Form Bereich.Ursache ; leiten Sie je Namensraum einen typisierten Fehler ab ( Auth.* , Permission.* , Validation.* , Conflict.* , RateLimit.* , *.NotFound , Integration.* ) und tragen Sie traceId in jeden davon. Schalten Sie nie auf message . Das Format steht unter Das Fehlerformat . Bilden Sie die Versionsprüfung ab: Lesen Sie bei Szenen, Playlisten, Programmen, Rollen und Displays das Feld version und senden Sie es bei jeder Änderung als If-Match . Bei 409 Conflict.OptimisticLock liest Ihr Client neu und wiederholt einmal — siehe Gleichzeitige Änderungen . Geben Sie jedem verändernden Aufruf einen Idempotency-Key — selbst gewählt, mindestens 8 Zeichen, je Vorgang neu. Eine Wiederholung mit demselben Schlüssel liefert die gespeicherte Antwort mit Idempotent-Replay: true ; derselbe Schlüssel mit anderem Rumpf antwo"
  },
  {
    "id": "api/generate-client#ergebnis-prüfen",
    "path": "/de/api/generate-client",
    "area": "api",
    "areaTitle": "API",
    "page": "generate-client",
    "pageTitle": "Einen eigenen Client aus der Spezifikation erzeugen",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "Die Beispiele der Schemata sind Ihre ersten Testdaten: alle 741 Schemata der ausgelieferten Spezifikation tragen eines, und sie passen zusammen (dieselben Kennungen, dieselbe Organisation). Ein erster lesender Aufruf — GET /v1/me — antwortet 200 mit tenantId Ihrer Organisation. Ein Aufruf mit falschem Schlüssel landet in Ihrem typisierten Auth -Fehler mit code: Auth.ApiKeyUnknown . Jede Operation, die Ihr Client anbietet, trägt in der Spezifikation x-stability: stable oder beta (bei beta kann sich die Form noch bewegen); eine spätere Fassung der Spezifikation vergleichen Sie mit Die Spezifikation gegen Ihren Stand vergleichen ."
  },
  {
    "id": "api/generate-client#wenn-etwas-nicht-klappt",
    "path": "/de/api/generate-client",
    "area": "api",
    "areaTitle": "API",
    "page": "generate-client",
    "pageTitle": "Einen eigenen Client aus der Spezifikation erzeugen",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung Der Generator bricht mit einem Formatfehler ab Er kennt OpenAPI 3.1 nicht. Einen Generator mit 3.1-Unterstützung wählen. 422 Validation.Failed mit details.issues Der Rumpf verletzt das Schema; issues nennt den Feldpfad. Das Beispiel des Anfrageschemas mit Ihrem Rumpf vergleichen. 403 Permission.Denied bei einer Route, die der Client anbietet Der Generator erzeugt jede Operation — auch die, die ein Schlüssel nicht darf. Das Recht in der Referenz prüfen; nicht schlüsselfähige Rechte bleiben Sitzungen vorbehalten — siehe Anmeldung . Ein Feld fehlt in der Antwort, das der Client verlangt Ihre Umgebung läuft auf einem anderen Stand als die Spezifikation, aus der Sie erzeugt haben. Die Spezifikation der Umgebung laden ( GET /v1/openapi.json ) und neu erzeugen."
  },
  {
    "id": "api/generate-client#weiterführendes",
    "path": "/de/api/generate-client",
    "area": "api",
    "areaTitle": "API",
    "page": "generate-client",
    "pageTitle": "Einen eigenen Client aus der Spezifikation erzeugen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Die Referenz lesen Das Fehlerformat Die Spezifikation gegen Ihren Stand vergleichen"
  },
  {
    "id": "cli/index",
    "path": "/de/cli",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "index",
    "pageTitle": "CLI",
    "section": "CLI",
    "anchor": "",
    "text": "Die Kommandozeile szenera : Profile, Ausweis, 136 Befehle, JSON-Ausgabe und Exit-Codes — und was heute beim Bezug gilt."
  },
  {
    "id": "cli/index#einrichten",
    "path": "/de/cli",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "index",
    "pageTitle": "CLI",
    "section": "Einrichten",
    "anchor": "einrichten",
    "text": "Seite Wann Sie es brauchen Die CLI beziehen Wie Sie die CLI heute bekommen: kein npm-Paket, kein Download — die CLI wird aus dem Quellbaum der Plattform gebaut; ohne Zugang bleibt die API. Profile und Ausweis Ein Profil mit Umgebung, Ausweisart und Schreibschutz anlegen — als Datei oder nur mit Umgebungsvariablen — und mit whoami prüfen."
  },
  {
    "id": "cli/index#befehle",
    "path": "/de/cli",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "index",
    "pageTitle": "CLI",
    "section": "Befehle",
    "anchor": "befehle",
    "text": "Seite Wann Sie es brauchen Befehle für Ihre Organisation Alle Befehle von szenera, die mit einem Schlüssel oder Konto Ihrer Organisation laufen: Medien, Szenen, Playlisten, Programme, Geräte, Team, Apps, Abo. Befehle für den Plattformbetrieb Die Befehle des internen Werkzeugs szenera-ops, die nur mit einem Betreiber-Profil laufen: Geräte-Software, Approvals, Störungen, Warteschlangen, Organisationen, Betreiberkonten."
  },
  {
    "id": "cli/index#in-skripten",
    "path": "/de/cli",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "index",
    "pageTitle": "CLI",
    "section": "In Skripten",
    "anchor": "in-skripten",
    "text": "Seite Wann Sie es brauchen Ausgabe als JSON und Exit-Codes JSON ohne Hülle auf der Standardausgabe, Fehler nur auf der Fehlerausgabe, --raw bei Vertragsbrüchen und die 13 Exit-Codes von 0 bis 12. Skripte und CI Ohne Profildatei mit Umgebungsvariablen arbeiten, Exit-Codes auswerten, Listen vollständig holen, Wartezeiten begrenzen und Wiederholungen absichern. Wenn die CLI abbricht Unbekannter Befehl, Profil gesperrt, 401 trotz Schlüssel, Antwort widerspricht dem Vertrag, Exit 11 oder 12 — Symptom, Bedeutung, Lösung."
  },
  {
    "id": "cli/get-cli",
    "path": "/de/cli/get-cli",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "get-cli",
    "pageTitle": "Die CLI beziehen",
    "section": "Die CLI beziehen",
    "anchor": "",
    "text": "Nach dieser Anleitung liegt die Kommandozeile szenera gebaut auf Ihrem Rechner, und szenera --help zeigt alle Befehle. Sie erfahren zuerst, was es heute an Bezugswegen gibt — und was nicht."
  },
  {
    "id": "cli/get-cli#der-ist-zustand",
    "path": "/de/cli/get-cli",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "get-cli",
    "pageTitle": "Die CLI beziehen",
    "section": "Der Ist-Zustand",
    "anchor": "der-ist-zustand",
    "text": "Die Kommandozeile ist das Paket @szenera/cli im Quellbaum der Plattform. Es trägt die Fassung 0.1.0 , ist nicht auf npm veröffentlicht und liegt keinem Release als Download bei — die Release-Abläufe der Plattform erzeugen Container-Abbilder und Geräte-Software, aber kein CLI-Artefakt. Wer den Quellbaum hat, baut die CLI daraus. Wer ihn nicht hat, arbeitet mit curl gegen dieselben Routen: Jeder Befehl ist ein API-Aufruf, und die Befehlsseiten nennen zu jedem Befehl die Route und das Recht, das er braucht. Über eine künftige Veröffentlichung sagt diese Seite nichts; sie beschreibt, was heute gilt. Das Paket enthält die Befehle für Organisationen. Die Befehle des Plattformbetriebs gehören zu einem eigenen, internen Werkzeug und sind nicht Teil von szenera ."
  },
  {
    "id": "cli/get-cli#schritte",
    "path": "/de/cli/get-cli",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "get-cli",
    "pageTitle": "Die CLI beziehen",
    "section": "Schritte",
    "anchor": "schritte",
    "text": "Installieren Sie die Abhängigkeiten des Quellbaums vollständig, nie nur für ein Teilpaket: Bauen Sie das Paket. Der Bau legt dist/ samt der ausführbaren Datei dist/main.js an: Rufen Sie die gebaute Datei direkt auf. Ohne Befehl und mit --help zeigt sie die Übersicht aller Befehle aus dem Register: Verknüpfen Sie die Datei optional als szenera , damit die Beispiele der übrigen Seiten wörtlich gelten. Das Paket erklärt szenera als Programmnamen für dist/main.js ; ein Symlink in ein Verzeichnis Ihres Suchpfads genügt:"
  },
  {
    "id": "cli/get-cli#optionen",
    "path": "/de/cli/get-cli",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "get-cli",
    "pageTitle": "Die CLI beziehen",
    "section": "Optionen",
    "anchor": "optionen",
    "text": "Zwei Aufrufformen im Überblick: Wer die Exit-Codes auswertet — in einem Skript, in der CI —, ruft die Datei direkt auf und nie über pnpm run : pnpm run verschluckt Exit-Codes und meldet jeden Fehlschlag als 1 ."
  },
  {
    "id": "cli/get-cli#ergebnis-prüfen",
    "path": "/de/cli/get-cli",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "get-cli",
    "pageTitle": "Die CLI beziehen",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "szenera --help (oder node packages/cli/dist/main.js --help ) listet 136 Befehle in Gruppen wie asset , scene , device , api und endet mit dem Block „Überall gültig“ ( --help , --profile=<name> , --json , --table , --raw ). szenera <befehl> --help zeigt Stellungen, Flaggen und Beispiele eines Befehls. Beides kommt aus dem Register, nicht aus gepflegtem Text; --help meldet sich nie an und braucht kein Profil. In der Sitzungsliste der API gibt sich die CLI später als szenera-cli/0.1.0 zu erkennen — das ist die Fassung, die auch in Fehlermeldungen zum Vertrag steht."
  },
  {
    "id": "cli/get-cli#wenn-etwas-nicht-klappt",
    "path": "/de/cli/get-cli",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "get-cli",
    "pageTitle": "Die CLI beziehen",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung node packages/cli/dist/main.js meldet, die Datei existiere nicht Der Bau fehlt; dist/ entsteht erst durch pnpm --filter @szenera/cli build . Schritt 2 ausführen. Der Aufruf bricht mit einem Syntax- oder Modulfehler ab, bevor ein Befehl läuft Die Node-Version ist zu alt; der Quellbaum verlangt Node ab 24. Node 24 oder neuer verwenden, dann neu bauen. Ein Skript sieht bei jedem Fehler Exit 1 Der Aufruf läuft über pnpm run , und das verschluckt die Codes. Die Datei direkt über node aufrufen; siehe Ausgabe als JSON und Exit-Codes . szenera health endet mit „Keine Konfiguration gefunden\" Die CLI läuft, weiß aber nicht, mit welcher Umgebung sie sprechen soll. Ein Profil anlegen oder SZENERA_BASE_URL setzen; siehe Profile und Ausweis . szenera kennt einen Befehl wie tenant list oder incident create nicht Das ist ein Befehl des Plattformbetriebs; er gehört nicht zu szenera . Siehe Befehle für den Plattformbetrieb ."
  },
  {
    "id": "cli/get-cli#weiterführendes",
    "path": "/de/cli/get-cli",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "get-cli",
    "pageTitle": "Die CLI beziehen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Profile und Ausweis Der erste Aufruf mit curl Befehle für Ihre Organisation"
  },
  {
    "id": "cli/profiles-and-credentials",
    "path": "/de/cli/profiles-and-credentials",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "profiles-and-credentials",
    "pageTitle": "Profile und Ausweis",
    "section": "Profile und Ausweis",
    "anchor": "",
    "text": "Nach dieser Anleitung kennt die CLI Ihre Umgebung, Ihren Ausweis und den Schreibschutz — als Profil in einer Datei oder nur über Umgebungsvariablen — und szenera whoami zeigt, mit welchem Konto und welcher Organisation sie spricht. Ein Profil bündelt zwei Dinge: den Ausweis und die Zuordnung Dienst → Basis-Adresse. Alles, was ein Befehl über seine Gegenseite wissen muss, steht dort — und nirgends sonst. Jedes Profil gehört einer Organisation."
  },
  {
    "id": "cli/profiles-and-credentials#schritte",
    "path": "/de/cli/profiles-and-credentials",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "profiles-and-credentials",
    "pageTitle": "Profile und Ausweis",
    "section": "Schritte",
    "anchor": "schritte",
    "text": "Legen Sie die Profil-Datei an: ~/.config/szenera/profile.json (ein gesetztes $XDG_CONFIG_HOME wird beachtet). Die Form: Tragen Sie unter services die Basis-Adresse ein. Der Eintrag * gilt für alles, was keinen eigenen Eintrag hat, und genügt heute; die Adresse muss mit http:// oder https:// beginnen, ein Schrägstrich am Ende wird entfernt. Wählen Sie den Ausweis unter credential . Die Art api-key mit einem Wert ak_… unter key ist der Regelweg; die beiden anderen Arten stehen unter „Optionen\". Entscheiden Sie über readOnly . true sperrt jeden verändernden Befehl, bevor ein einziges Netzpaket entsteht; für ein Produktions-Profil bleibt das stehen. Fehlt die Angabe, gilt true. Setzen Sie default auf das Profil, das ohne --profile= gelten soll. Führt die Datei genau ein Profil, gilt dieses auch ohne Vorgabe; führt sie mehrere ohne Vorgabe, bricht die CLI ab und nennt die Namen. Prüfen Sie das Profil:"
  },
  {
    "id": "cli/profiles-and-credentials#optionen",
    "path": "/de/cli/profiles-and-credentials",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "profiles-and-credentials",
    "pageTitle": "Profile und Ausweis",
    "section": "Optionen",
    "anchor": "optionen",
    "text": ""
  },
  {
    "id": "cli/profiles-and-credentials#ohne-datei-umgebungsvariablen",
    "path": "/de/cli/profiles-and-credentials",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "profiles-and-credentials",
    "pageTitle": "Profile und Ausweis",
    "section": "Ohne Datei: Umgebungsvariablen",
    "anchor": "ohne-datei-umgebungsvariablen",
    "text": "Umgebungsvariablen liegen über der Datei — jede gesetzte Variable ersetzt den entsprechenden Wert des gewählten Profils. SZENERA_BASE_URL und SZENERA_API_KEY allein tragen ein vollständiges Profil, ganz ohne Datei; es heißt dann environment und ist nicht schreibgeschützt. Variable Wirkung SZENERA_PROFILE Welches Profil der Datei gilt; --profile=<name> schlägt es SZENERA_BASE_URL Setzt den Eintrag * unter services SZENERA_API_KEY API-Schlüssel; ersetzt den Ausweis des Profils SZENERA_OUTPUT json oder table ; --json und --table schlagen es SZENERA_PROFILE_FILE Pfad der Profil-Datei; schlägt jede Suche"
  },
  {
    "id": "cli/profiles-and-credentials#wo-die-cli-sucht",
    "path": "/de/cli/profiles-and-credentials",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "profiles-and-credentials",
    "pageTitle": "Profile und Ausweis",
    "section": "Wo die CLI sucht",
    "anchor": "wo-die-cli-sucht",
    "text": "Der erste Treffer gewinnt: Ort Wann $SZENERA_PROFILE_FILE ausdrücklich benannt ~/.config/szenera/profile.json sonst; $XDG_CONFIG_HOME wird beachtet"
  },
  {
    "id": "cli/profiles-and-credentials#ausweisarten",
    "path": "/de/cli/profiles-and-credentials",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "profiles-and-credentials",
    "pageTitle": "Profile und Ausweis",
    "section": "Ausweisarten",
    "anchor": "ausweisarten",
    "text": "api-key — der Regelweg. Der Schlüssel reist als Authorization: Bearer an jedem Aufruf mit und läuft nicht nach einer Viertelstunde ab; die CLI braucht dafür weder Zwischenspeicher noch Auffrischung. Ein Wert ohne einen Präfix, den die API als Schlüssel kennt, wird abgewiesen, bevor ein Netzpaket entsteht — sobald ein Befehl den Ausweis braucht (Exit 2); --help und health laufen auch mit einem falschen Wert durch. password — für den Menschen am Rechner. Das Profil trägt email und password , bei Zwei-Faktor zusätzlich totpSecret (Base32); den Einmalcode rechnet die CLI selbst. Das Zugriffsmerkmal liegt danach mit Rechten 0600 neben der Konfiguration ( .szenera-token-<profil>.json ), wird eine Minute vor Ablauf verworfen und vor jedem Aufruf erneuert, wenn es die nächste Rundreise nicht mehr trägt — auch mitten in einem Befehl, der Minuten wartet. Gehört das Konto zu mehreren Organisationen, bricht die Anmeldung ab, statt zu raten. none — nur öffentliche Routen. szenera health läuft damit; jeder andere Befehl bricht mit dem Hinweis ab, dass das Profil keinen Ausweis trägt."
  },
  {
    "id": "cli/profiles-and-credentials#dienste-je-domäne",
    "path": "/de/cli/profiles-and-credentials",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "profiles-and-credentials",
    "pageTitle": "Profile und Ausweis",
    "section": "Dienste je Domäne",
    "anchor": "dienste-je-domäne",
    "text": "Statt * darf ein Eintrag unter services eine Domäne der Plattform nennen (etwa fleet oder composition ) und ihr eine eigene Adresse geben; * bleibt für den Rest. Ein Name, der keine Domäne ist, wird beim Lesen abgewiesen — ein Tippfehler fällt nicht still auf * zurück."
  },
  {
    "id": "cli/profiles-and-credentials#ergebnis-prüfen",
    "path": "/de/cli/profiles-and-credentials",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "profiles-and-credentials",
    "pageTitle": "Profile und Ausweis",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "szenera whoami zeigt Kennung, E-Mail, Name, Organisation und die Zahl der Rollen (Route GET /v1/me ). Steht dort eine andere Organisation als erwartet, zeigt das Profil woandershin, als gedacht — nicht der Schlüssel ist falsch, sondern die Wahl."
  },
  {
    "id": "cli/profiles-and-credentials#wenn-etwas-nicht-klappt",
    "path": "/de/cli/profiles-and-credentials",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "profiles-and-credentials",
    "pageTitle": "Profile und Ausweis",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung „Keine Konfiguration gefunden — die Kommandozeile weiß nicht, mit welcher Umgebung sie sprechen soll.\" (Exit 2) Weder eine Datei an einem der beiden Orte noch SZENERA_BASE_URL . Datei anlegen oder die beiden Variablen setzen. „Unbekanntes Profil „…\". … führt: dev, prod.\" (Exit 2) --profile= oder SZENERA_PROFILE nennt einen Namen, den die Datei nicht kennt. Einen der genannten Namen wählen. „… ist im Profil „prod\" gesperrt — das Profil ist schreibgeschützt.\" (Exit 2) Ein verändernder Befehl trifft auf ein schreibgeschütztes Profil — oder auf eines ohne die Angabe. readOnly: false setzen, wenn das Profil schreiben darf. „… „audience\" muss „tenant\" sein.\" (Exit 2) Die Datei führt ein Profil für den Plattformbetrieb. Das kennt szenera nicht. Das Profil aus der Datei nehmen oder eine eigene Datei über SZENERA_PROFILE_FILE benennen. „Kein API-Schlüssel: erwartet wird ein Wert mit dem Präfix …\" (Exit 2) Der Wert im Profil oder in SZENERA_API_KEY ist kein Schlüssel. Den Klartext aus dem Portal übernehmen; siehe Einen API-Schlüssel erzeugen . „… braucht einen Ausweis, das Profil „…\" trägt keinen.\" (Exit 1) Das Profil hat credential nicht oder kind: \"none\" . SZENERA_API_KEY=ak_… mitgeben oder credential im Profil setzen. HTTP 401 mit Exit 3 Der Schlüssel gilt nicht: widerrufen, abgelaufen, oder das Profil zeigt auf eine andere Umgebung als der Schlüssel. szenera whoami --profile=<name> je Profil prüfen; siehe Ausgabe als JSON und Exit-Codes . „… gehört zu mehre"
  },
  {
    "id": "cli/profiles-and-credentials#weiterführendes",
    "path": "/de/cli/profiles-and-credentials",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "profiles-and-credentials",
    "pageTitle": "Profile und Ausweis",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Die CLI beziehen Ausgabe als JSON und Exit-Codes Einen API-Schlüssel erzeugen"
  },
  {
    "id": "cli/commands-organization",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Befehle für Ihre Organisation",
    "anchor": "",
    "text": "Alle Befehle der Kommandozeile szenera . Sie laufen mit einem API-Schlüssel ak_… oder einem Konto Ihrer Organisation. Jede Tabelle nennt je Befehl die Route, das nötige Recht, ob ein API-Schlüssel das Recht tragen darf („Schlüssel“) und ob der Befehl etwas verändert. Die Spalte „Schlüssel“ folgt dem Rechte-Katalog der Plattform: Ein Recht, das nur Konten vorbehalten ist, bekommt kein Schlüssel — der Befehl antwortet dann mit 403 (Exit 4), und Sie brauchen ein Profil mit Kennwort-Anmeldung. szenera <befehl> --help zeigt zu jedem Befehl Stellungen, Flaggen und Beispiele. Die Beschreibung dort ist die Quelle dieser Tabellen. Stand ist das Register von szenera mit 136 Befehlen, alle stehen hier. 21 davon dienen auch dem Plattformbetrieb und nehmen dort zum Teil eine andere Route. Diese Wege und die Befehle, die nur dem Betrieb dienen, stehen unter Befehle für den Plattformbetrieb ."
  },
  {
    "id": "cli/commands-organization#überall-gültige-flaggen",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Überall gültige Flaggen",
    "anchor": "überall-gültige-flaggen",
    "text": "Flagge Wirkung --help Hilfe zum Befehl zeigen und nichts tun, meldet sich nie an --profile=<name> Welches Profil gilt (Umgebung, Ausweis, Dienst-Adressen) --json Ausgabe als JSON erzwingen --table Ausgabe als Tabelle erzwingen --raw Bricht eine Antwort den Vertrag, die Rohantwort ausgeben Eine Flagge trägt ihren Wert am Gleichheitszeichen ( --limit=20 ), --limit 20 gibt es nicht. Eine unbekannte Flagge ist ein Fehler (Exit 1), kein stiller Hinweis. Listen-Befehle blättern mit --limit=<anzahl> (1 bis 200, bei gefilterten Listen bis 100), --cursor=<marke> und --all (allen Seiten folgen). Befehle, die auf einen Vorgang warten, nehmen --timeout=<dauer> ( 90s , 20m , 2h oder eine Zahl in Sekunden), teils --interval=<dauer> und --no-wait . Unumkehrbare Eingriffe verlangen --yes . Ohne die Flagge bricht der Befehl mit Exit 1 ab und sagt, was geschehen würde."
  },
  {
    "id": "cli/commands-organization#umgebung-und-ausweis",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Umgebung und Ausweis",
    "anchor": "umgebung-und-ausweis",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert health Meldet die Umgebung sich bereit — und welche Prüfung sagt was GET /health/ready , kein Ausweis nötig, bei 503 Exit 9 — nein health public Was die Statusseite draußen zeigt: Zustand je Baustein, Wartungsfenster, öffentliche Störungen GET /v1/public/status , kein Ausweis nötig — nein whoami Wer der eigene Ausweis ist — Konto, Organisation, Rechte GET /v1/me , nur Anmeldung, kein eigenes Recht ja nein hygiene get Was liegen geblieben ist: sechs Zahlen und je Zahl der Befehl, der die Zeilen dahinter zeigt GET /v1/analytics/hygiene · analytics.query.run . --offline-since (Vorgabe P7D ) und --dormant-since (Vorgabe P90D ) setzen die beiden Schwellen ja nein"
  },
  {
    "id": "cli/commands-organization#medien-und-ordner",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Medien und Ordner",
    "anchor": "medien-und-ordner",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert asset list Die Medien der Mediathek. --usage=unused findet, was in keiner Szene, keiner Playliste und keinem Markenzeichen steckt GET /v1/assets · asset.list ja nein asset get <ast_…> Ein Medium samt seiner Varianten. --usages nennt, wo es steckt GET /v1/assets/{id} , GET /v1/assets/{id}/usage · asset.read ja nein asset upload --file=<pfad> Eine Datei in die Mediathek laden und warten, bis sie nutzbar ist POST /v1/assets/upload-precheck → Bytes → POST /v1/assets/upload-complete · asset.upload . Wartet bis ready , Vorgabe --timeout=10m , --no-wait ja ja asset download <ast_…> --output=<pfad> Ein Medium oder eine seiner Varianten holen POST /v1/assets/{id}/download-url (Variante: /variant-url ) → Bytes · asset.read ja nein asset update <ast_…> Name, Schlagworte oder Ordner eines Mediums ändern PATCH /v1/assets/{id} · asset.update ja ja asset reprocess <ast_…> Die Varianten eines Mediums neu erzeugen und warten POST /v1/assets/{id}/reprocess · asset.update . Wartet wie beim Hochladen ja ja asset delete <ast_…> --yes Ein Medium in den Papierkorb legen DELETE /v1/assets/{id} · asset.delete ja ja asset restore <ast_…> Ein Medium aus dem Papierkorb zurückholen POST /v1/assets/{id}/restore · asset.update ja ja asset purge <ast_…> --yes Ein Medium endgültig löschen POST /v1/assets/{id}/purge · asset.delete ja ja asset bulk-update --assets=<ast_…,…> Viele Medien verschieben oder verschlagworten (bis zu 100) POST /v1/assets/bulk-move bzw."
  },
  {
    "id": "cli/commands-organization#szenen-fassungen-und-layouts",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Szenen, Fassungen und Layouts",
    "anchor": "szenen-fassungen-und-layouts",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert scene list Die Szenen der Organisation. --assignment=none findet die, auf die kein Programm, kein Zeitplan und keine zugewiesene Playliste zeigt (sinnvoll mit --status=published ) GET /v1/scenes · scene.list ja nein scene get <scn_…> Eine Szene samt ihrer offenen Befunde GET /v1/scenes/{id} , GET /v1/scenes/{id}/integrity-issues · scene.read ja nein scene create --name=<name> Eine leere Szene anlegen POST /v1/scenes · scene.create ja ja scene create-from-template --template=<sct_…> --name=<name> Eine Szene aus einer kuratierten Vorlage anlegen POST /v1/scenes/from-template · scene.create ja ja scene update <scn_…> Den Entwurf einer Szene ändern. Ohne --version liest der Befehl die Fassung vorher PATCH /v1/scenes/{id} · scene.update ja ja scene publish <scn_…> Den Entwurf als neue Fassung veröffentlichen. Nennt Befunde statt sie zu verschlucken POST /v1/scenes/{id}/publish · scene.publish ja ja scene recheck <scn_…> Die Unversehrtheit neu prüfen und die Befunde nennen POST /v1/scenes/{id}/integrity-issues/recheck · scene.read ja ja scene resolve-issue <sii_…> --scene=<scn_…> --resolution=manual-fixed|ignored Einen Befund als behoben oder ignoriert schließen POST /v1/scenes/{id}/integrity-issues/{iid}/resolve · scene.update ja ja scene archive <scn_…> Eine Szene archivieren POST /v1/scenes/{id}/archive · scene.archive ja ja scene restore <scn_…> Eine archivierte oder gelöschte Szene zurückholen POST /v1/scenes/{id}/restore · scen"
  },
  {
    "id": "cli/commands-organization#playlisten-und-programme",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Playlisten und Programme",
    "anchor": "playlisten-und-programme",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert playlist list Die Wiedergabelisten der Organisation GET /v1/playlists · playlist.list ja nein playlist get <pll_…> Eine Wiedergabeliste samt ihrer Punkte GET /v1/playlists/{id} · playlist.read ja nein playlist create --name=<name> Eine Wiedergabeliste anlegen: --items (Sequenz) oder --folder / --tags (Regel) POST /v1/playlists · playlist.create ja ja playlist update <pll_…> Eine Wiedergabeliste ändern. --items ersetzt die ganze Sequenz PATCH /v1/playlists/{id} · playlist.update ja ja playlist delete <pll_…> --yes Eine Wiedergabeliste löschen. Zugewiesene weist die API ab DELETE /v1/playlists/{id} · playlist.delete ja ja playlist preview-source Eine automatische Quelle probeweise auflösen POST /v1/playlists/source-preview · playlist.read und asset.read ja nein schedule list Die Programme der Organisation GET /v1/schedules · schedule.list ja nein schedule get <sch_…> Ein Programm samt seiner Punkte. --players nennt die Geräte, die es spielen GET /v1/schedules/{id} , GET /v1/schedules/{id}/players · schedule.read ja nein schedule create --name=<name> Ein Programm anlegen, ohne --base mit Sendepause POST /v1/schedules · schedule.create ja ja schedule update <sch_…> Name oder Basis eines Programms ändern PATCH /v1/schedules/{id} · schedule.update ja ja schedule delete <sch_…> --yes Ein Programm löschen. Zugewiesene weist die API ab DELETE /v1/schedules/{id} · schedule.delete ja ja schedule create-item --schedule=<sch_…> --content=… "
  },
  {
    "id": "cli/commands-organization#wiedergabe-und-widgets",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Wiedergabe und Widgets",
    "anchor": "wiedergabe-und-widgets",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert playback now Was gerade auf allen Displays läuft ( play , pause , gap ) GET /v1/players/now-playing · fleet.player.list ja nein playback timeline <ply_…> Die aufgelöste Abspiel-Timeline eines Displays (Fenster ab jetzt, 4 Stunden bis 7 Tage) GET /v1/players/{id}/timeline · fleet.player.read ja nein playback history <ply_…> --date=<datum> Der aufgezeichnete Tagesverlauf eines Displays (90 Tage aufbewahrt) GET /v1/players/{id}/playback-history · fleet.playback-history.read nein nein playback refresh <ply_…> Die Momentaufnahme eines Displays neu berechnen POST /v1/players/{id}/snapshot/refresh · fleet.player.update ja ja widget preview --widget=<pfad> Die Daten eines Widgets für eine Vorschau auflösen POST /v1/widget-data/preview · scene.read ja nein"
  },
  {
    "id": "cli/commands-organization#werbung",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Werbung",
    "anchor": "werbung",
    "text": "Werbepartner, Werbekampagnen, Buchungen und Motive des eigenen Werbegeschäfts. Lesen geht in jedem Paket. Anlegen und Freigeben verlangen zusätzlich die Fähigkeit advertisingDirect des Pakets, ohne sie antwortet der Server mit 403 und Billing.FeatureNotEntitled (Exit 4). Werbekampagnen des Werbenetzes stehen in campaign list , sobald sie Displays Ihrer Organisation erreichen, ohne Preis und ohne Notiz des Betreibers. Befehl Zweck Route und Recht Schlüssel Verändert advertiser list Die Werbepartner, filterbar nach --status , --industry und --search GET /v1/advertising/advertisers · advertising.advertiser.read ja nein advertiser get <adv_…> Ein Werbepartner mit Marken, Ansprechpersonen und Notiz GET /v1/advertising/advertisers/{id} · advertising.advertiser.read ja nein advertiser create --name=<name> --industry=<branche> Einen Werbepartner anlegen. --brand darf sich wiederholen POST /v1/advertising/advertisers · advertising.advertiser.manage ja ja campaign list Die Werbekampagnen mit Phase, Zeitraum, Zahl der Buchungen und erreichten Displays. --owner=tenant oder --owner=network trennt eigenes Geschäft und Netz GET /v1/advertising/campaigns · advertising.campaign.read ja nein campaign get <acp_…> Eine Werbekampagne mit Phase, Zeitraum, Ziel und Preis GET /v1/advertising/campaigns/{id} · advertising.campaign.read ja nein booking list --campaign=<acp_…> Die Buchungen einer Werbekampagne mit Spotlänge, Frequenz und Zusage GET /v1/advertising/campaigns/{id}/booking-lines · advertis"
  },
  {
    "id": "cli/commands-organization#geräte",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Geräte",
    "anchor": "geräte",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert player list Die Displays der Organisation. --program=none findet die ohne Inhalt, --offline-since=P7D die, die so lange nichts melden (ein nie gemeldetes zählt mit) GET /v1/players · fleet.player.list , dazu --status , --search ja nein device list Die Geräte — was steht draußen, meldet es sich noch GET /v1/devices · identity.device.list nein nein device get <dev_…> Ein Gerät im Einzelnen: Zertifikat und Flächen GET /v1/devices/{id} · identity.device.list nein nein device send-command <dev_…> <kind> Einen Fernwartungs-Befehl an ein Gerät ( reboot , locate , tail-log , mark-good , check-ota ) oder eine Fläche ( screenshot , identify , set-config , display-power , display-source , refresh-snapshot , mit --screen=<n> ) absetzen. Antwort 201 heißt angenommen, --wait bleibt dran POST /v1/devices/{id}/commands , POST /v1/players/{id}/commands · fleet.player.command.issue . Flächen-Arten lösen zusätzlich über identity.device.list auf — das ist nicht schlüsselfähig, mit einem Schlüssel laufen deshalb nur die Maschinen-Arten ja ja device list-commands <dev_…> Die letzten Fernwartungs-Befehle eines Geräts GET /v1/devices/{id}/commands · fleet.player.read ja nein device screenshot <dev_…> Ein Bildschirmfoto anfordern, darauf warten und es holen ( --file ) POST /v1/players/{id}/commands → GET …/commands/{cid}/output · fleet.player.command.issue , fleet.player.read und identity.device.list . Das letzte ist nicht schlüsselfähig, deshalb bleib"
  },
  {
    "id": "cli/commands-organization#standorte",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Standorte",
    "anchor": "standorte",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert location list Die Standorte der Organisation. Die letzte Spalte zählt die Displays am Ort GET /v1/locations · location.list , vollständig, ohne Blättern ja nein location get <loc_…> Ein Standort mit Anschrift, Zeitzone, Lage, Notiz und Fassung GET /v1/locations/{id} · location.read ja nein location create --name=<name> --timezone=<IANA> Einen Standort anlegen. Die Anschrift kommt über --street , --postal-code , --city , --country , Lage über --latitude / --longitude , dazu --note POST /v1/locations · location.create , braucht die Fähigkeit Standorte ja ja location update <loc_…> Name, Zeitzone, Anschrift, Lage oder Notiz ändern. --clear-address und --clear-coordinates entfernen die Angabe PATCH /v1/locations/{id} · location.update ja ja location delete <loc_…> --yes Einen leeren Standort löschen. Mit Displays daran antwortet die API 409 Location.HasPlayers DELETE /v1/locations/{id} · location.delete ja ja player assign-location <ply_…> --location=<loc_…> Ein Display an einen Ort hängen. Ein zweiter Aufruf hängt um statt zu verdoppeln PATCH /v1/players/{id} · fleet.player.update ja ja player unassign-location <ply_…> Ein Display von seinem Ort lösen. Es behält Programm, Einstellungen und Lizenz PATCH /v1/players/{id} · fleet.player.update ja ja Ein Standort ist ein Ordner über Displays: Er trägt keine Rechte und keinen eigenen Bestand, und er reißt beim Löschen nie ein Display mit. Die Felder und die Fehlercodes stehen unter Sta"
  },
  {
    "id": "cli/commands-organization#auswertung",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Auswertung",
    "anchor": "auswertung",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert analytics overview Der Überblick: je Display die Verfügbarkeit in der Planzeit, darüber Lücken, rechtzeitig gemeldete Planstunden und Ausspielungen. --location (wiederholbar) schränkt auf Standorte ein GET /v1/analytics/overview · analytics.query.run ja nein analytics explore --measure=<kennzahl> Kennzahlen frei auswerten: --measure und --by sind wiederholbar, dazu --sort , --ascending , --limit , --perspective und --granularity . --query=<pfad> nimmt die vollständige Abfrage samt Filtern als JSON POST /v1/analytics/explore · analytics.query.run ja nein analytics delivery <acp_…> Die Lieferung einer Werbekampagne: je Buchung Zusage, Geliefertes, Erfüllung, Pacing, Prognose mit Spanne und der Befund GET /v1/analytics/campaigns/{id}/delivery · advertising.campaign.read ja nein Ohne --from und --to gelten die letzten sieben Tage bis gestern; die Werbekampagne zeigt ohne Fenster ihre Laufzeit bis gestern. Die Tabelle nennt Stunden statt Sekunden und Prozent statt Anteil, und neben einem Wert steht seine Obergrenze, solange gemeldete Lücken noch nachkommen können. Welche Kennzahl sich mit welcher Dimension rechnen lässt, zeigt szenera api get /v1/analytics/catalog ."
  },
  {
    "id": "cli/commands-organization#nachweise",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Nachweise",
    "anchor": "nachweise",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert proof list Die Nachweise der Organisation, die neuesten zuerst: Abschlüsse, Nachträge und Zwischenstände mit Nummer, Zeitraum, Werbekampagne, Buchung, gültigen Ausspielungen und Erfüllung. --campaign , --advertiser , --line , --kind , --from und --to schränken ein GET /v1/analytics/proofs · analytics.proof.read ja nein proof show <prf_…> Ein Nachweis mit Prüfcode, Signatur und seinen Dateien samt Prüfsumme. Zahlen, Nachträge und den Stand jeder Zustellung zeigt --json GET /v1/analytics/proofs/{id} · analytics.proof.read ja nein proof download <prf_…> --output=<pfad> Eine Datei holen: --kind=pdf (Vorgabe), csv , manifest oder capture mit --file=<pfi_…> . Stimmt die Prüfsumme nicht, wird die Datei wieder entfernt GET /v1/analytics/proofs/{id}/files/{kind} · analytics.proof.read ja nein Mit Nummer und Prüfcode aus proof show prüft jeder einen Nachweis auf der Prüfseite, auch ohne Konto. Zwischenstände und Verweise für Werbepartner entstehen im Portal oder über die API."
  },
  {
    "id": "cli/commands-organization#ereignis-ziele",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Ereignis-Ziele",
    "anchor": "ereignis-ziele",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert webhook list Die Ereignis-Ziele der Organisation. --status=dead zeigt die, welche die Zustellung selbst aufgegeben hat (abgeschaltet und mit einer Fehlerkette über zwanzig) GET /v1/webhooks · webhook.list nein nein Ein von Hand abgeschaltetes Ziel steht unter --status=disabled und ist etwas anderes. Angelegt und geändert werden Ziele im Portal oder über die API, siehe Einen Webhook-Endpunkt anlegen ."
  },
  {
    "id": "cli/commands-organization#team-rollen-und-erteilungen",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Team, Rollen und Erteilungen",
    "anchor": "team-rollen-und-erteilungen",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert user list Die Mitglieder der Organisation. --last-login-before=<zeitpunkt mit Zone> findet ruhende Konten, auch nie angemeldete GET /v1/users · identity.user.list nein nein user get <usr_…> Ein Mitglied im Einzelnen GET /v1/users/{id} · identity.user.read nein nein user create --email=… --name=… --role=<rol_…> Ein Mitglied direkt anlegen (ohne Einladung), mit --password oder --federated POST /v1/users · identity.user.create nein ja user update <usr_…> Profil eines Mitglieds ändern PATCH /v1/users/{id} · identity.user.update nein ja user set-role <usr_…> --role=<rol_…> --reason=… Die Rolle eines Mitglieds setzen — sie ersetzt die bisherige PATCH /v1/users/{id}/roles · identity.user.assign-roles nein ja user suspend <usr_…> --reason=… Ein Mitglied sperren POST /v1/users/{id}/suspend · identity.user.suspend nein ja user reactivate <usr_…> Die Sperre eines Mitglieds aufheben POST /v1/users/{id}/reactivate · identity.user.reactivate nein ja user delete <usr_…> --reason=<kategorie> --yes Ein Mitglied löschen. Der Grund ist eine der fünf Kategorien der API DELETE /v1/users/{id} · identity.user.delete nein ja invitation list Die Einladungen der Organisation, mit --pending nur die offenen GET /v1/invitations · identity.invitation.list nein nein invitation create --email=… --role=<rol_…> Jemanden in die Organisation einladen POST /v1/invitations · identity.user.invite nein ja invitation resend <ivt_…> Eine Einladung noch einmal verschick"
  },
  {
    "id": "cli/commands-organization#schlüssel",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Schlüssel",
    "anchor": "schlüssel",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert api-key list Die eigenen Zugangsschlüssel, mit --organization alle Schlüssel der Organisation GET /v1/me/api-keys (nur Sitzung), mit --organization GET /v1/api-keys · identity.api-key.list nein nein api-key issue --name=… --permissions=<a,b,c> Einen Schlüssel ausstellen — der Geheimwert erscheint genau einmal. --list-allowed zeigt die freigegebenen Rechte POST /v1/me/api-keys · identity.api-key.create nein ja api-key rotate <akm_…> Einen Schlüssel erneuern — der alte gilt noch eine Gnadenfrist (0–72 Stunden) POST /v1/me/api-keys/{id}/rotate · identity.api-key.create nein ja api-key revoke <akm_…> --yes Einen Schlüssel zurückziehen — sofort und endgültig. --compromised nennt im Protokoll den Verdacht, dass der Geheimwert in fremde Hände kam DELETE /v1/me/api-keys/{id} · identity.api-key.revoke nein ja Ein Schlüssel kann nie mehr als sein Aussteller und nie ein Recht tragen, das für Schlüssel nicht freigegeben ist. Das Zweite prüft die CLI schon vor dem Aufruf."
  },
  {
    "id": "cli/commands-organization#abonnement-rechnungen-und-apps",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Abonnement, Rechnungen und Apps",
    "anchor": "abonnement-rechnungen-und-apps",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert subscription get Das Abonnement und die Lizenzlage der eigenen Organisation GET /v1/me/subscription · billing.subscription.read , GET /v1/me/licensing · billing.usage.read nein nein subscription set-licenses --count=<n> Die Zahl gebuchter Lizenzen ändern, nicht unter die belegten PUT /v1/me/licensing/licenses · billing.subscription.update nein ja subscription cancel --yes Das Abonnement kündigen — zum Periodenende, mit --immediately jetzt POST /v1/me/subscription/cancel · billing.subscription.update nein ja subscription reactivate Eine vorgemerkte Kündigung zurücknehmen POST /v1/me/subscription/reactivate · billing.subscription.update nein ja app subscribe <app> Eine bepreiste App zur Abrechnung buchen POST /v1/me/licensing/apps/{appId} · billing.subscription.update nein ja app unsubscribe <app> --yes Eine gebuchte App kündigen, wirkt zum Periodenende DELETE /v1/me/licensing/apps/{appId} · billing.subscription.update nein ja invoice list Die eigenen Rechnungen GET /v1/me/invoices · billing.invoice.read nein nein invoice get <inv_…> Eine Rechnung samt Positionen. --pdf holt die Adresse des PDF GET /v1/me/invoices/{id} · billing.invoice.download nein nein app list Die Apps im Store, mit --installed nur die installierten GET /v1/me/apps/store , kein eigenes Recht ja nein app get <app> Eine App im Einzelnen: Rechte, Datenzugriff, Widgets GET /v1/me/apps/store/{appId} , kein eigenes Recht, nur Sitzung nein nein app install <app> Ein"
  },
  {
    "id": "cli/commands-organization#protokoll-rechte-katalog-und-notausgang",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Protokoll, Rechte-Katalog und Notausgang",
    "anchor": "protokoll-rechte-katalog-und-notausgang",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert audit list Das Prüfprotokoll lesen — anhängend, Nutzlasten geschwärzt GET /v1/audit · audit.read oder audit.read.self (dann nur eigene Vorgänge) nein nein audit get <aud_…> Einen Protokoll-Eintrag samt Nutzlast GET /v1/audit/{id} · audit.read oder audit.read.self nein nein permission catalog Der Rechte-Katalog, wie ihn die laufende Umgebung kennt, mit --key-eligible die Freigabeliste GET /v1/permission-catalog , nur Anmeldung nicht belegt nein api get <path> Ein GET auf einen beliebigen Pfad der API die Route des Pfads und ihr Recht je Route nein api post <path> <body> Ein POST auf einen beliebigen Pfad, dazu --idempotency-key=<schluessel> die Route des Pfads und ihr Recht je Route ja api put <path> <body> Ein PUT auf einen beliebigen Pfad, dazu --idempotency-key=<schluessel> die Route des Pfads und ihr Recht je Route ja api patch <path> <body> Ein PATCH auf einen beliebigen Pfad, dazu --idempotency-key=<schluessel> die Route des Pfads und ihr Recht je Route ja api delete <path> [body] Ein DELETE auf einen beliebigen Pfad, dazu --idempotency-key=<schluessel> die Route des Pfads und ihr Recht je Route ja api … ist der Notausgang zu jeder Route, für die es keinen eigenen Befehl gibt. Der Pfad beginnt mit einem Schrägstrich und darf eine Abfrage tragen. Der Rumpf ist JSON direkt oder @datei.json . Auch dieser Weg geht durch den erzeugten Client — Kopfzeilen, Trace-Kennung und die Fehlerarten gelten wie überall —, prüft die Antwort"
  },
  {
    "id": "cli/commands-organization#weiterführendes",
    "path": "/de/cli/commands-organization",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-organization",
    "pageTitle": "Befehle für Ihre Organisation",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Befehle für den Plattformbetrieb Ausgabe als JSON und Exit-Codes Die Bereiche der Referenz"
  },
  {
    "id": "cli/commands-platform-operations",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Befehle für den Plattformbetrieb",
    "anchor": "",
    "text": "Die Befehle des Werkzeugs szenera-ops , die nur mit einem Profil vom Publikum operator laufen — einem Betreiber-Konto oder einem Betreiber-Schlüssel opk_… . Sie betreffen Kundinnen und Kunden der Plattform nicht und gehören nicht zur Kommandozeile szenera . Sie stehen hier, weil whoami , device , audit , player , playback , permission , api-key und invoice je nach Profil beide Seiten bedienen. Das Werkzeug liegt im Quellbaum unter tools/ops-cli und wird nicht ausgeliefert. Es läuft mit pnpm -s szenera-ops <befehl> und führt alle 230 Befehle der Plattform. Profile, Umgebungsvariablen, Ausgabe und Exit-Codes sind dieselben wie bei szenera . Dazu kennt es das Publikum operator (Profilfeld audience , Umgebungsvariable SZENERA_AUDIENCE ), seine eigene Profil-Datei .secrets/szenera-profile.json im Quellbaum und den Rückfall auf die Umgebungen in .secrets/umgebungen.json . Ein Befehl, der nur einer Seite dient, weist das Profil der anderen ab, bevor ein Aufruf hinausgeht. Die Tabellen haben dieselbe Form wie unter Befehle für Ihre Organisation : Befehl, Zweck, Route und Recht, ob ein Schlüssel das Recht tragen darf, ob der Befehl etwas verändert. Betreiber-Schlüssel laufen 1 bis 365 Tage (Vorgabe 90). Welche Rechte ein solcher Schlüssel überhaupt führen darf, zeigt szenera-ops permission catalog --key-eligible unter einem Betreiber-Profil — es sind die lesenden Betriebsrouten, die Geräte-Software und das Lesen der Vermarktung. Alles andere bleibt Menschen mit Zwei-Faktor-Anmeldung v"
  },
  {
    "id": "cli/commands-platform-operations#gesundheit",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Gesundheit",
    "anchor": "gesundheit",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert health deep Ein Urteil aus fünf Quellen, der Befehl nach dem Aufspielen. Eine rote Quelle macht den Befehl rot, eine stumme steht als unknown GET /health/ready , GET /v1/platform/ops/platform-health , GET …/ops/jobs/health , GET …/ops/cache/health , GET …/ops/events/dispatcher/health , GET …/ops/audit/health · platform.ops.health.read , platform.jobs.health.read , platform.cache.stats.read , platform.events.dispatcher.read , platform.audit.health.read teils — Cache und Prüfprotokoll nur mit Konto nein Mit --json trägt die Antwort das Urteil unter verdict ( green oder yellow ) und je Quelle unter sources die Felder name , status und hint . Ein rotes Urteil endet stattdessen mit Exit 11. Die Gegenprobe von außen ist health public : dieselbe Auskunft, die die Statusseite zeigt, ohne Ausweis und ohne Betreiber-Profil — sie beantwortet „Was sehen unsere Kunden gerade?“ und nicht „Wie steht es wirklich?“. Der Befehl steht unter Befehle für Ihre Organisation ."
  },
  {
    "id": "cli/commands-platform-operations#geräte-der-flotte",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Geräte der Flotte",
    "anchor": "geräte-der-flotte",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert device list Die Geräte aller Organisationen, auch die ohne Organisation. Dazu --tenant=<tnt_…> , --all-tenants , --silent-only (seit über einer Viertelstunde stumm) GET /v1/platform/ops/devices · platform.ops.device.read ja nein device get <dev_…> Ein Gerät in der Betriebssicht: Organisation und Seriennummer GET /v1/platform/ops/devices/{deviceId} · platform.ops.device.read ja nein device lookup --serial=<text> Ein Gerät über die Hardware-Seriennummer finden GET /v1/platform/ops/devices/lookup · platform.ops.device.read ja nein device get-command <dev_…> <cmd_…> Was aus einem Befehl an ein Gerät wurde. --wait wartet bis zum Ende (Exit 11 bei failed / timeout , Exit 12 beim Zeitlimit) GET /v1/platform/ops/devices/{id}/commands/{commandId} · platform.ops.device.command-read ja nein device release <dev_…> --reason=reconnect|factory|removed --yes Ein Gerät freigeben, der Grund ist Pflicht. reconnect und removed lassen WLAN und Fernzugang stehen, factory nimmt beides mit. Alle Displays des Geräts gehen mit POST /v1/platform/ops/devices/{deviceId}/release · platform.ops.device.reset nein ja device block <dev_…> --reason=<text> --yes Ein Gerät sperren: Es verliert seine Organisation, alle Displays gehen mit, jedes Zertifikat wird gesperrt, und jede Anmeldung weist die API mit 409 Device.Blocked ab. Der Grund (1 bis 500 Zeichen) ist Pflicht POST /v1/platform/ops/devices/{deviceId}/block · platform.ops.device.reset nein ja device unbloc"
  },
  {
    "id": "cli/commands-platform-operations#störungen-und-wartungsfenster",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Störungen und Wartungsfenster",
    "anchor": "störungen-und-wartungsfenster",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert incident list Die Störungsmeldungen, mit --id=<inc_…> eine einzelne samt Chronik GET /v1/platform/ops/incidents · platform.ops.incident.list , mit --id GET …/incidents/{id} · platform.ops.incident.read nein nein incident create --title=… --severity=sev-1…sev-4 --scope=platform|tenant|device-fleet Eine Störung eröffnen. --public stellt sie zugleich auf die Statusseite, ohne die Flagge bleibt sie intern POST /v1/platform/ops/incidents · platform.ops.incident.open nein ja incident update <inc_…> --message=… Einen Eintrag in die Chronik schreiben. --status wandert zugleich weiter, --public stellt die Störung auf die Statusseite, --internal nimmt sie herunter PATCH /v1/platform/ops/incidents/{id} · platform.ops.incident.update nein ja incident resolve <inc_…> Eine Störung abschließen, dazu --postmortem=<url> POST /v1/platform/ops/incidents/{id}/resolve · platform.ops.incident.resolve nein ja maintenance-window list Die geplanten Wartungsfenster, mit --id=<mtw_…> ein einzelnes GET /v1/platform/ops/maintenance-windows · platform.ops.maintenance.list , mit --id platform.ops.maintenance.read nein nein maintenance-window create --title=… --start=… --end=… --scope=… Ein Wartungsfenster planen. --banner , --notify-days=<tage> , --block-mutations hält schreibende Aufrufe der Organisationen an POST /v1/platform/ops/maintenance-windows · platform.ops.maintenance.create nein ja maintenance-window update <mtw_…> Ein Fenster verschieben oder ums"
  },
  {
    "id": "cli/commands-platform-operations#warteschlangen-cache-ereignisse-und-verbindungen",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Warteschlangen, Cache, Ereignisse und Verbindungen",
    "anchor": "warteschlangen-cache-ereignisse-und-verbindungen",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert job queues Die Warteschlangen samt Kennzahlen und Zustand — aus drei Quellen, eine Tabelle GET …/ops/jobs/queues , GET …/ops/jobs/metrics , GET …/ops/jobs/health · platform.jobs.queues.read , platform.jobs.metrics.read , platform.jobs.health.read ja nein job pause <name> Eine Warteschlange pausieren. Was läuft, läuft zu Ende POST /v1/platform/ops/jobs/queues/{queueName}/pause · platform.jobs.queues.pause nein ja job resume <name> Eine angehaltene Warteschlange fortsetzen POST /v1/platform/ops/jobs/queues/{queueName}/resume · platform.jobs.queues.resume nein ja job failed Die endgültig gescheiterten Jobs, mit --not-replayed , --queue=<name> , --id=<kennung> GET /v1/platform/ops/jobs/dead-letter , GET …/dead-letter/{id} · platform.jobs.dead-letter.read ja nein job replay --id=<kennung>|--all --reason=… Einen gescheiterten Job — oder viele — erneut einstellen. --max (Vorgabe 100) deckelt POST …/jobs/dead-letter/{id}/replay , POST …/jobs/dead-letter/bulk-replay · platform.jobs.replay nein ja job kill <jobId> --queue=<name> --reason=… --yes Einen laufenden Job abbrechen, Antwort 202 POST /v1/platform/ops/jobs/{queueName}/{jobId}/force-kill · platform.jobs.force-kill nein ja job cleanup --yes Alte Dead-Letter-Einträge räumen POST /v1/platform/ops/jobs/cleanup/run · platform.jobs.cleanup nein ja cache stats Zustand, Trefferquote und die heißen Themen, dazu --window=1m|5m|1h GET …/ops/cache/health , GET …/ops/cache/stats , GET …/ops/ca"
  },
  {
    "id": "cli/commands-platform-operations#fehler--und-rechte-katalog",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Fehler- und Rechte-Katalog",
    "anchor": "fehler--und-rechte-katalog",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert error catalog Alle Fehlercodes der API samt HTTP-Status, dazu --search=<text> GET /v1/platform/ops/errors/catalog · platform.errors.catalog.read nein nein permission catalog Der Rechte-Katalog. --key-eligible zeigt, was ein Betreiber-Schlüssel führen darf GET /v1/permission-catalog , nur Anmeldung nicht belegt nein permission drift Was der Bestand gegenüber dem Katalog vermissen lässt. Ändert nichts GET /v1/platform/ops/permissions/catalog-drift · platform.permissions.catalog.read nein nein permission reload Den Katalog neu einlesen — nötig, wenn App-Rechte dazukommen POST /v1/platform/ops/permissions/catalog/reload · platform.permissions.catalog.reload nein ja permission backfill Die Rollen der Organisationen an den Katalog nachziehen, dazu --tenant=<tnt_…> . --dry-run rechnet nur, --reconcile nimmt auch weg. Betreiber-Konten brauchen das nicht: Ihre Rechte folgen der Rolle POST …/ops/permissions/roles/backfill · platform.permissions.role.backfill nein ja permission flush-cache --yes Den Cache der wirksamen Rechte verwerfen POST /v1/platform/ops/permissions/cache/flush · platform.permissions.cache.flush nein ja"
  },
  {
    "id": "cli/commands-platform-operations#organisationen",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Organisationen",
    "anchor": "organisationen",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert tenant list Die Organisationen der Plattform ( active , frozen , suspended , deleted ) GET /v1/platform/tenants · platform.tenant.list nein nein tenant get <tnt_…> Eine Organisation in der Betriebssicht GET /v1/platform/tenants/{id} · platform.tenant.read nein nein tenant provision --slug=… --name=… --owner-email=… --address=… Organisation, Abonnement, Inhaber und Einladung in einem Zug, dazu --licenses , --set=<k=v> POST /v1/platform/tenants/provision · platform.tenant.provision , mit --set PATCH …/tenants/{tenantId}/feature-entitlements · platform.billing.override nein ja tenant overview <tnt_…> Die Betriebsakte: Flotte, Inhalte, Ansprechpartner GET /v1/platform/tenants/{tenantId}/overview · platform.tenant.read nein nein tenant suspend <tnt_…> --reason=… --yes Eine Organisation sperren — kaskadiert auf ihre Konten POST /v1/platform/tenants/{id}/suspend · platform.tenant.suspend nein ja tenant resume <tnt_…> Eine Sperre aufheben POST /v1/platform/tenants/{id}/resume · platform.tenant.resume nein ja tenant delete <tnt_…> --slug=… --yes Eine Organisation löschen, nicht umkehrbar. Der Kurzname ist die Bestätigung. Ihre Geräte werden freigegeben und zeigen danach einen Kopplungscode DELETE /v1/platform/tenants/{id} · platform.tenant.delete nein ja tenant entitlements <tnt_…> Vertragliche Fähigkeiten lesen und mit --set=<k=v> setzen (Schalter on oder off ) GET …/tenants/{tenantId}/feature-entitlements · platform.billing.invoice.re"
  },
  {
    "id": "cli/commands-platform-operations#betreiber-konten-und-betreiber-schlüssel",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Betreiber-Konten und Betreiber-Schlüssel",
    "anchor": "betreiber-konten-und-betreiber-schlüssel",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert operator list Die Betreiber-Konten, mit --role=owner|manager|support|viewer , --status , --search und --with-permission=<platform.…> (Konten, deren Rolle das Recht trägt) GET /v1/platform/operators · platform.operator.list nein nein operator get <ops_…> Ein Betreiber-Konto samt Rolle und der Stufe der Rolle je Bündel GET /v1/platform/operators/{id} · platform.operator.list nein nein operator create --email=… --name=… --role=manager|support|viewer Ein Betreiber-Konto anlegen. Die Rolle ist Pflicht, owner gibt es hier nicht. Die Zwei-Faktor-Einrichtung erscheint genau einmal POST /v1/platform/operators · platform.operator.create nein ja operator set-role <ops_…> --role=manager|support|viewer --yes Die Rolle ersetzen. Nur der Owner, nie die eigene, nie die des Owners. Die neuen Rechte gelten mit der nächsten Anfrage des Kontos PATCH /v1/platform/operators/{id}/role · platform.operator.create nein ja operator transfer-owner <ops_…> --totp-code=<123456> Den Owner übertragen, Schritt 1: Der Owner bietet dem Konto an. Das Angebot gilt sieben Tage, ein neues ersetzt das frühere. --accept --totp-code=… (Schritt 2, unter dem Profil des Ziels) nimmt an, danach ist das Ziel Owner und der bisherige Owner Manager. --status zeigt das offene Angebot POST /v1/platform/operators/{id}/owner-transfer , POST …/operators/owner-transfer/accept , GET …/operators/owner-transfer · platform.operator.create , bei Annahme und Stand platform.operator.list n"
  },
  {
    "id": "cli/commands-platform-operations#gerätesoftware",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Gerätesoftware",
    "anchor": "gerätesoftware",
    "text": "Der Baustein ist immer das erste Wort hinter dem Befehl: os , agent , renderer , host-webos oder host-tizen . Eine Version nennt der Befehl mit der Bau-Kennung (zwölf Hex-Stellen), list zeigt sie. Befehl Zweck Route und Recht Schlüssel Verändert device-software list <baustein> Die Versionen in der Ablage, neueste zuerst: Anzeigename mit Welt ( 0.6.1-prod ), Bau, Stand, die Channels, in denen sie gilt, die Zahl der Geräte und die Größe GET /v1/platform/ops/device-software/{component}/builds · platform.ops.device-software.read ja nein device-software releases <baustein> Was in den Channels gilt, und der Verlauf der Freigaben, mit --limit und --cursor GET …/device-software/{component}/releases · platform.ops.device-software.read ja nein device-software devices <baustein> Die Geräte, die den Baustein fahren, mit Channel, Fassung, Bau und Stand zur Freigabe ( current , updating , rolled-back , outdated , no-release ). Dazu --channel , --build , --state GET …/device-software/{component}/devices · platform.ops.device-software.read ja nein device-software activity Der Verlauf über alle Bausteine: Freigaben, Importe, Löschungen, Anfragen und die Rücknahmen, die Geräte meldeten. Dazu --component=<baustein> GET …/device-software/activity · platform.ops.device-software.read ja nein device-software release <baustein> <bau> --channel=… --yes Eine Version in einem Channel gelten lassen. --pull-along=beta,canary zieht tiefere Channels im selben Schritt nach, --note , --security . Verlangt di"
  },
  {
    "id": "cli/commands-platform-operations#anfragen-an-eine-zweite-person",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Anfragen an eine zweite Person",
    "anchor": "anfragen-an-eine-zweite-person",
    "text": "Für Gerätesoftware und Apps gleich. Ob ein Release nach stable eine zweite Person braucht, stellt der Owner mit settings set releaseApproval.deviceSoftware|apps stable . Befehl Zweck Route und Recht Schlüssel Verändert approval list Die Anfragen, ohne --state die offenen. Dazu --state=open|approved|withdrawn|expired , --kind=device-software|app , --package=<baustein oder app> GET /v1/platform/ops/approvals · platform.ops.device-software.read oder platform.app-catalog.list ja nein approval approve <rap_…> Eine Anfrage als zweite Person bestätigen und damit ausführen. Die anfragende Person selbst bekommt einen Satz und Exit 4 ( ReleaseApproval.SamePerson ) POST /v1/platform/ops/approvals/{id}/approve · platform.ops.release.approve nein ja approval withdraw <rap_…> Eine Anfrage zurückziehen, als andere Person ablehnen. --reason=… ist freiwillig POST /v1/platform/ops/approvals/{id}/withdraw · platform.ops.release.approve oder platform.ops.device-software.release oder platform.app-catalog.promote nein ja"
  },
  {
    "id": "cli/commands-platform-operations#plattform-einstellungen",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Plattform-Einstellungen",
    "anchor": "plattform-einstellungen",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert settings get Alle Plattform-Einstellungen vollständig, eine nicht gesetzte mit ihrer Vorgabe GET /v1/platform/settings · platform.settings.read nein nein settings set <einstellung> <wert> --yes Eine Einstellung ändern, zum Beispiel releaseApproval.apps stable oder retention.deviceSoftware.keepNewest 3 . Der Wert wird gegen den Vertrag geprüft, bevor etwas hinausgeht PATCH /v1/platform/settings · platform.settings.manage (nur der Owner) nein ja Die Einstellungen sind releaseApproval.deviceSoftware und releaseApproval.apps ( off oder stable , Vorgabe off ) sowie retention.deviceSoftware.keepNewest (1 bis 100, Vorgabe 5) und retention.deviceSoftware.auto (Vorgabe false ). Dazu kommen retention.apps.keepNewest (1 bis 100, Vorgabe 5), retention.apps.unreferencedMonths (1 bis 120, Vorgabe 12) und retention.apps.auto (Vorgabe false ). Jede Änderung steht mit altem und neuem Wert im Prüfprotokoll ( platform.settings.changed )."
  },
  {
    "id": "cli/commands-platform-operations#app-katalog",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "App-Katalog",
    "anchor": "app-katalog",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert app-release list Die Releases des App-Katalogs, mit --app=<app-id> , --channel=canary|beta|stable|deprecated|retired GET /v1/platform/app-catalog/apps , GET …/apps/{appId}/releases · platform.app-catalog.list nein nein app-release set-notes <arl_…> --app=<app-id> --notes=… Die Release-Notizen ändern (max. 2000 Zeichen) PATCH …/apps/{appId}/releases/{id} · platform.app-catalog.update nein ja app-release promote <arl_…> --app=<app-id> --channel=beta|stable Nach beta oder stable heben, immer ein Schritt. Nach stable hebt der Befehl sofort, wenn die Plattform keine zweite Person verlangt ( releaseApproval.apps = off ), sonst legt er eine Anfrage an, und --reason=… (10 bis 500 Zeichen) ist dann Pflicht POST …/apps/{appId}/releases/{id}/promote · platform.app-catalog.promote nein ja app-release restore <arl_…> --app=<app-id> --reason=… Die Rücknahme einer veralteten oder abgelösten Fassung nach stable. Je nach Einstellung sofort oder als Anfrage, die eine zweite Person mit approval approve bestätigt. Die bis dahin geltende Fassung geht dabei nach deprecated POST …/apps/{appId}/releases/{id}/restore · platform.app-catalog.promote nein ja app-release demote <arl_…> --app=<app-id> --channel=deprecated Ein Release aus stable nach deprecated zurückstufen, der einzige Schritt rückwärts POST …/apps/{appId}/releases/{id}/demote · platform.app-catalog.promote nein ja app-release retire <arl_…> --app=<app-id> --reason=… --grace=regular|securit"
  },
  {
    "id": "cli/commands-platform-operations#abrechnung-und-artikel",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Abrechnung und Artikel",
    "anchor": "abrechnung-und-artikel",
    "text": "Befehl Zweck Route und Recht Schlüssel Verändert subscription set-payment-way <tnt_…> --way=invoice|stripe|none Den Zahlweg eines Vertrags stellen PUT /v1/platform/tenants/{tenantId}/billing/payment-way · platform.billing.override nein ja subscription approve-invoice <tnt_…> --yes Den Kauf auf Rechnung freigeben. --revoke räumt die Freigabe ab PUT /v1/platform/tenants/{tenantId}/billing/payment-way · platform.billing.override nein ja subscription check-stripe <tnt_…> Die Bindung eines Vertrags an den Zahlungsdienstleister prüfen. Ändert nichts GET /v1/platform/tenants/{tenantId}/billing/stripe-link · platform.billing.override nein nein subscription relink-stripe <tnt_…> --yes Einen Vertrag neu an den Zahlungsdienstleister binden. Lebt der Kunde dort noch, geschieht nichts ( unchanged ) POST /v1/platform/tenants/{tenantId}/billing/relink-stripe · platform.billing.override nein ja subscription backfill-period-prices --yes Die Perioden-Stückpreise des Bestands nachziehen — der Griff, wenn der Lauf beim Start der Anwendung gescheitert ist. Idempotent POST /v1/platform/billing/period-unit-prices/backfill · platform.billing.plan.manage nein ja invoice list --tenant=<tnt_…>|--all-tenants Die Rechnungen aller Organisationen GET /v1/platform/invoices · platform.billing.invoice.read nein nein invoice get <inv_…> Eine Rechnung samt Positionen GET /v1/platform/invoices/{id} · platform.billing.invoice.read nein nein invoice mark-paid <inv_…> --yes Einen Zahlungseingang buchen POST /v1/pla"
  },
  {
    "id": "cli/commands-platform-operations#weiterführendes",
    "path": "/de/cli/commands-platform-operations",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "commands-platform-operations",
    "pageTitle": "Befehle für den Plattformbetrieb",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Befehle für Ihre Organisation Profile und Ausweis"
  },
  {
    "id": "cli/output-and-exit-codes",
    "path": "/de/cli/output-and-exit-codes",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "output-and-exit-codes",
    "pageTitle": "Ausgabe als JSON und Exit-Codes",
    "section": "Ausgabe als JSON und Exit-Codes",
    "anchor": "",
    "text": "Was die CLI auf welche Ausgabe schreibt und welcher Exit-Code welche Fehlerart bedeutet — die Tabelle, gegen die ein Skript entscheidet, ohne Text zu vergleichen."
  },
  {
    "id": "cli/output-and-exit-codes#die-form-der-ausgabe",
    "path": "/de/cli/output-and-exit-codes",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "output-and-exit-codes",
    "pageTitle": "Ausgabe als JSON und Exit-Codes",
    "section": "Die Form der Ausgabe",
    "anchor": "die-form-der-ausgabe",
    "text": "Situation Was die CLI tut Standardausgabe ist kein Terminal (Röhre, Datei) JSON, ohne dass --json nötig wäre Standardausgabe ist ein Terminal Kopfzeile und Tabelle für Menschen --json bzw. --table erzwingt die Form; schlägt die Umgebungsvariable SZENERA_OUTPUT=json bzw. table erzwingt die Form; schlägt die Erkennung Im JSON-Modus steht auf der Standardausgabe die Nutzlast und sonst nichts — keine Hülle, kein ok , kein dauerMs . Eine Liste ist deshalb .items[] , nicht .daten.items[] ; nextCursor steht, solange weitere Seiten folgen, direkt in der Antwort: Für Menschen gilt das Gegenteil: Die Kopfzeile steht immer zuerst — auch bei einem schreibenden Befehl ohne Tabelle —, darunter die Tabelle oder, wenn der Befehl keine hat, die Daten als JSON."
  },
  {
    "id": "cli/output-and-exit-codes#fehler-gehen-auf-die-fehlerausgabe",
    "path": "/de/cli/output-and-exit-codes",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "output-and-exit-codes",
    "pageTitle": "Ausgabe als JSON und Exit-Codes",
    "section": "Fehler gehen auf die Fehlerausgabe",
    "anchor": "fehler-gehen-auf-die-fehlerausgabe",
    "text": "Fehler landen immer auf der Fehlerausgabe, nie auf der Standardausgabe. szenera device list > geraete.json hinterlässt im Fehlerfall keine Datei, die aussieht wie ein Ergebnis. Im JSON-Modus trägt der Fehler seine Art als Wort, damit ein Protokoll später ohne diese Tabelle lesbar bleibt: code , status , traceId , details und retryAfterSeconds stehen dabei, wenn der Fehler von der API kam. Im Tabellen-Modus lautet dieselbe Meldung ✗ … , gefolgt von HTTP 403 · Permission.Denied und Spur trc_… . Fortschrittszeilen wartender Befehle gehen ebenfalls auf die Fehlerausgabe; die Standardausgabe trägt am Ende genau ein Dokument."
  },
  {
    "id": "cli/output-and-exit-codes#die-exit-codes",
    "path": "/de/cli/output-and-exit-codes",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "output-and-exit-codes",
    "pageTitle": "Ausgabe als JSON und Exit-Codes",
    "section": "Die Exit-Codes",
    "anchor": "die-exit-codes",
    "text": "Code Art Woher 0 ok alles gut 1 usage unbekannter Befehl, fehlende Stellung, unbekannte Flagge, Flagge ohne Wert, fehlendes --yes , falscher Kennungs-Präfix — und eine Anfrage, die dem Vertrag widerspricht (mit Feldpfad) 2 config Profil fehlt oder ist kaputt, kein Schlüssel-Präfix, Profil gesperrt ( readOnly: true ) 3 auth HTTP 401 4 forbidden HTTP 403 5 not-found HTTP 404 6 conflict HTTP 409 — Zustands- oder Fassungskonflikt 7 validation HTTP 422 — der Server hat die Eingabe abgelehnt 8 rate-limited HTTP 429; retryAfterSeconds steht im Fehler 9 server HTTP 5xx oder eine Antwort, die dem Vertrag widerspricht 10 network gar keine HTTP-Antwort: kein Netz, kein Name, abgebrochen 11 failed der Vorgang kam durch und endete rot (Verarbeitung gescheitert, Upload abgewiesen, Befehl failed ) 12 timeout das Zeitlimit war erreicht; der Ausgang ist unbekannt Codes 1 und 2 sind Fehler auf Ihrer Seite, 3 bis 8 Absagen des Servers mit klarer Ursache, 9 und 10 Ursachen, bei denen ein späterer Versuch Sinn ergibt, 11 und 12 die Ausgänge eines Vorgangs, der lange nach seinem Aufruf endet. Andere Statuscodes unter 500, die keiner Zeile entsprechen, enden als 1."
  },
  {
    "id": "cli/output-and-exit-codes#11-und-12-sind-nicht-9",
    "path": "/de/cli/output-and-exit-codes",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "output-and-exit-codes",
    "pageTitle": "Ausgabe als JSON und Exit-Codes",
    "section": "11 und 12 sind nicht 9",
    "anchor": "11-und-12-sind-nicht-9",
    "text": "Beide kamen durch: Der Aufruf war richtig, der Server hat geantwortet. Bei 11 ist das Ergebnis negativ — asset upload konnte die Bytes schieben, die Verarbeitung ist gescheitert; der Befund steht im Fehler. Bei 12 ist das Ergebnis offen — der Vorgang läuft womöglich weiter. Wer 12 wie „gescheitert“ behandelt, stößt einen laufenden Vorgang ein zweites Mal an; wer 11 wie 9 behandelt, wiederholt einen Aufruf, dessen Antwort schon feststeht."
  },
  {
    "id": "cli/output-and-exit-codes#anfrage-und-antwort-sind-zwei-seiten",
    "path": "/de/cli/output-and-exit-codes",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "output-and-exit-codes",
    "pageTitle": "Ausgabe als JSON und Exit-Codes",
    "section": "Anfrage und Antwort sind zwei Seiten",
    "anchor": "anfrage-und-antwort-sind-zwei-seiten",
    "text": "Der erzeugte Client prüft beide Richtungen gegen dieselben Schemas. Ging für den Aufruf kein Netzpaket hinaus, war es die Anfrage — Exit 1, mit Feldpfad: Bricht die Antwort den Vertrag, ist der häufige Grund ein Versatz der Stände — die Gegenstelle ist älter oder neuer als die CLI und schickt ein Feld nicht, das diese schon verlangt. Die Meldung sagt das, nennt die Stellen und den Weg zur Rohantwort: --raw gilt überall und legt bei genau diesem Fehler die Rohantwort auf die Standardausgabe ( null , wenn kein Rumpf ankam); die Diagnose bleibt auf der Fehlerausgabe, der Exit-Code bleibt 9. Es ist die einzige Stelle, an der ein gescheiterter Aufruf etwas auf die Standardausgabe schreibt — damit das Dokument durch jq gehen kann statt aus einer Fehlermeldung gefischt zu werden."
  },
  {
    "id": "cli/output-and-exit-codes#weiterführendes",
    "path": "/de/cli/output-and-exit-codes",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "output-and-exit-codes",
    "pageTitle": "Ausgabe als JSON und Exit-Codes",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Skripte und CI Wenn die CLI abbricht Das Fehlerformat"
  },
  {
    "id": "cli/scripts-and-ci",
    "path": "/de/cli/scripts-and-ci",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "scripts-and-ci",
    "pageTitle": "Skripte und CI",
    "section": "Skripte und CI",
    "anchor": "",
    "text": "Nach dieser Anleitung läuft die CLI in einer Pipeline ohne Profildatei, Ihr Skript reagiert auf Exit-Codes statt auf Text, holt Listen vollständig, begrenzt Wartezeiten und wiederholt verändernde Aufrufe, ohne einen Vorgang doppelt anzustoßen."
  },
  {
    "id": "cli/scripts-and-ci#schritte",
    "path": "/de/cli/scripts-and-ci",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "scripts-and-ci",
    "pageTitle": "Skripte und CI",
    "section": "Schritte",
    "anchor": "schritte",
    "text": "Legen Sie Adresse und Schlüssel als Geheimnisse Ihrer CI ab und reichen Sie sie als SZENERA_BASE_URL und SZENERA_API_KEY in den Lauf. Beide Variablen tragen zusammen ein vollständiges Profil; eine Datei auf der Platte ist nicht nötig, und das Profil ist nicht schreibgeschützt. Rufen Sie die CLI direkt über die gebaute Datei auf, nie über pnpm run — sonst sieht Ihr Skript für jeden Fehler den Code 1 : Prüfen Sie am Anfang jeder Pipeline die Umgebung und den Ausweis — zwei Aufrufe, die Netz und Schlüssel trennen: Werten Sie den Exit-Code aus, nicht die Meldung. Die Standardausgabe trägt im Fehlerfall nichts, deshalb darf sie gefahrlos in eine Datei gehen: Verarbeiten Sie die JSON-Ausgabe mit jq . Listen tragen items und, solange Seiten folgen, nextCursor :"
  },
  {
    "id": "cli/scripts-and-ci#optionen",
    "path": "/de/cli/scripts-and-ci",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "scripts-and-ci",
    "pageTitle": "Skripte und CI",
    "section": "Optionen",
    "anchor": "optionen",
    "text": "Listen vollständig holen. --all folgt allen Seiten, bis keine mehr kommt, und liefert eine zusammengehängte Liste ohne nextCursor . Bei großen Beständen kombinieren Sie es mit --limit=200 , dem Deckel je Seite: Wartezeiten begrenzen. Befehle, die auf einen Vorgang warten — asset upload , asset reprocess , device screenshot , device send-command --wait —, nehmen --timeout=<dauer> ( 90s , 20m , 2h oder eine Zahl in Sekunden) und teils --interval=<dauer> . --no-wait stößt nur an und kommt sofort zurück, ohne den Ausgang zu kennen. Exit 12 heißt in jedem Fall: Ausgang unbekannt, der Vorgang läuft womöglich weiter. Wiederholungen absichern. Beim Notausgang api post|put|patch|delete erzeugt die CLI je Aufruf einen frischen Idempotenz-Schlüssel; --idempotency-key=<schluessel> ist nur nötig, um eine abgebrochene Wiederholung fortzusetzen — dann mit demselben Wert wie beim ersten Versuch. Befehle, die von sich aus idempotent sind, sagen es in ihrer Hilfe: player set-program zweimal zu senden ist kein zweiter Vorgang. Schutz vor Unwiderruflichem. Befehle, die etwas Unumkehrbares tun, verlangen --yes und brechen ohne die Flagge mit Exit 1 ab — asset purge , asset delete , scene delete , api-key revoke und weitere; szenera <befehl> --help nennt die Flagge. Für Profile in einer Datei bleibt readOnly: true der Riegel: Ein verändernder Befehl bricht dann mit Exit 2 ab, bevor ein Netzpaket entsteht. Ein Profil allein aus Umgebungsvariablen hat diesen Riegel nicht."
  },
  {
    "id": "cli/scripts-and-ci#ergebnis-prüfen",
    "path": "/de/cli/scripts-and-ci",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "scripts-and-ci",
    "pageTitle": "Skripte und CI",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "szenera health und szenera whoami laufen am Anfang der Pipeline mit Exit 0; whoami nennt die erwartete Organisation unter tenantId . Ein absichtlich falscher Aufruf ( szenera device list --state=active ) endet mit Exit 1 und einer Meldung auf der Fehlerausgabe; die umgeleitete Datei bleibt leer. Nach einem Upload zeigt szenera asset get <ast_…> --json den Zustand ready ."
  },
  {
    "id": "cli/scripts-and-ci#wenn-etwas-nicht-klappt",
    "path": "/de/cli/scripts-and-ci",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "scripts-and-ci",
    "pageTitle": "Skripte und CI",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung Jeder Fehler endet mit 1 , auch ein 401 Der Aufruf läuft über pnpm run . Die Datei direkt über node aufrufen (Schritt 2). „Keine Konfiguration gefunden“ (Exit 2) SZENERA_BASE_URL fehlt oder ist leer. Beide Variablen im Lauf setzen; siehe Profile und Ausweis . Exit 3 bei jedem Befehl außer health Der Schlüssel gilt nicht — widerrufen, abgelaufen oder für eine andere Umgebung ausgestellt. Neuen Schlüssel erzeugen und das Geheimnis austauschen. Exit 4 bei einem einzelnen Befehl Der Schlüssel trägt das Recht nicht, oder das Recht ist für Schlüssel nicht vorgesehen. Spalte „Schlüssel“ unter Befehle für Ihre Organisation prüfen; sonst Profil mit Kennwort-Anmeldung. Exit 12 nach einem Upload Das Zeitlimit war zu knapp; die Verarbeitung läuft weiter. --timeout=20m setzen und vor einer Wiederholung mit asset get nachsehen."
  },
  {
    "id": "cli/scripts-and-ci#weiterführendes",
    "path": "/de/cli/scripts-and-ci",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "scripts-and-ci",
    "pageTitle": "Skripte und CI",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Ausgabe als JSON und Exit-Codes Profile und Ausweis Idempotenz von Anfang an"
  },
  {
    "id": "cli/troubleshooting",
    "path": "/de/cli/troubleshooting",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "troubleshooting",
    "pageTitle": "Wenn die CLI abbricht",
    "section": "Wenn die CLI abbricht",
    "anchor": "",
    "text": "Die häufigsten Abbrüche der CLI in der Reihenfolge, in der sie auftreten — mit der Meldung, die Sie sehen, dem Exit-Code, seiner Bedeutung und dem Weg zurück. Jede Lösung verweist auf die zuständige Seite. Symptom Bedeutung Lösung „Unbekannter Befehl „device lister\".\" und darunter die ganze Übersicht (Exit 1) Die Wörter treffen keinen Befehl des Registers: Tippfehler, falsche Reihenfolge ( list device ) oder eine Flagge ohne = , die als Wort gelesen wurde. Den Befehl aus der Übersicht wählen; Flaggen tragen ihren Wert am Gleichheitszeichen ( --limit=20 ). Siehe Befehle für Ihre Organisation . „„device list\" kennt die Flagge „--state\" nicht. Bekannt sind: …\" (Exit 1) Eine unbekannte Flagge ist ein Fehler, kein stiller Hinweis — die Meldung zählt die gültigen auf. Eine der genannten Flaggen nehmen; szenera <befehl> --help erklärt sie. „„--limit\" braucht einen Wert: --limit= .\" (Exit 1) Die Flagge stand ohne Gleichheitszeichen; --limit 20 gibt es nicht. --limit=20 schreiben. „„device get\" braucht — …\" (Exit 1) Eine Pflicht-Stellung fehlt. Die Kennung anhängen, wie --help es zeigt. „„scn_01K…\" ist keine Kennung für ein Medium: erwartet wird ast_…\" (Exit 1) Der Präfix der Kennung passt nicht zum Befehl; es ging kein Aufruf hinaus. Die richtige Kennung nehmen — ast_ Medium, scn_ Szene, pll_ Playlist, sch_ Programm, ply_ Display, dev_ Gerät. „… wandert in den Papierkorb. Wenn das gewollt ist: --yes anhängen.\" (Exit 1) Ein unumkehrbarer Eingriff ohne Zustimmung im Aufruf. --yes anhän"
  },
  {
    "id": "cli/troubleshooting#weiterführendes",
    "path": "/de/cli/troubleshooting",
    "area": "cli",
    "areaTitle": "CLI",
    "page": "troubleshooting",
    "pageTitle": "Wenn die CLI abbricht",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Profile und Ausweis Ausgabe als JSON und Exit-Codes Skripte und CI"
  },
  {
    "id": "webhooks/index",
    "path": "/de/webhooks",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "index",
    "pageTitle": "Webhooks",
    "section": "Webhooks",
    "anchor": "",
    "text": "Ereignisse Ihrer Organisation an Ihre Systeme: Endpunkt anlegen, Signatur prüfen, Wiederholungen verstehen, Ereignistypen nachschlagen. Die Seite Einstellungen › Organisation › Webhooks mit der Liste Webhook-Endpunkte, den angelegten Endpunkten im Status Aktiv und der aufklappbaren Anleitung zur Signaturprüfung darunter Alle Anleitungen dieses Bereichs spielen auf dieser Fläche: Einstellungen › Organisation › Webhooks."
  },
  {
    "id": "webhooks/index#verstehen",
    "path": "/de/webhooks",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "index",
    "pageTitle": "Webhooks",
    "section": "Verstehen",
    "anchor": "verstehen",
    "text": "Seite Wann Sie es brauchen Wie wird ein Webhook zugestellt? Vom Ereignis über den Abgleich mit Ihren Endpunkten, die Signatur und den Versand bis zu Wiederholung oder Abschaltung. Was gesendet wird und wie wiederholt wird Die Anfrage an Ihren Endpunkt (Methode, Kopfzeilen, Rumpf), die Zustände einer Zustellung, das Wiederholungsschema und die Abschaltung."
  },
  {
    "id": "webhooks/index#einrichten",
    "path": "/de/webhooks",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "index",
    "pageTitle": "Webhooks",
    "section": "Einrichten",
    "anchor": "einrichten",
    "text": "Seite Wann Sie es brauchen Einen Webhook-Endpunkt anlegen Unter Einstellungen › Organisation › Webhooks eine https-Adresse und die abonnierten Ereignisse eintragen und das Signatur-Secret sicher übernehmen. Die Signatur einer Zustellung prüfen HMAC-SHA256 über den rohen Rumpf nachrechnen, in konstanter Zeit mit X-Signature vergleichen, nach X-Delivery-Id entdoppeln und das Secret rotieren."
  },
  {
    "id": "webhooks/index#ereignisse",
    "path": "/de/webhooks",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "index",
    "pageTitle": "Webhooks",
    "section": "Ereignisse",
    "anchor": "ereignisse",
    "text": "Seite Wann Sie es brauchen Ereignistypen: Organisation, Konten, Abrechnung Die abonnierbaren Ereignistypen aus Organisation, Speicher-Verbindungen, Zugang, Konten und delegiertem Zugang, Sitzungen, Abrechnung und Beitritt mit ihrem Nutzlast-Kern. Ereignistypen: Inhalte, Programme, Geräte, Apps Die abonnierbaren Ereignistypen aus Medien, Szenen, Programmen, Marktplatz, Flotte, App-Laufzeit, Wiedergabe, Auswertung und Werbung, dazu die Typen der Plattform, die nie ankommen. Den Ereigniskatalog über die API abrufen Sie legen Endpunkte aus einem eigenen System an oder zeigen die Auswahl in Ihrer Oberfläche: den Katalog als JSON holen, eine Ereignis-Liste prüfen, bevor der Server sie abweist."
  },
  {
    "id": "webhooks/index#wenn-es-hakt",
    "path": "/de/webhooks",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "index",
    "pageTitle": "Webhooks",
    "section": "Wenn es hakt",
    "anchor": "wenn-es-hakt",
    "text": "Seite Wann Sie es brauchen Wenn keine Zustellung ankommt Endpunkt abgelehnt, inaktiv nach Fehlschlägen, keine Wiederholung nach 4xx, Signatur passt nicht, doppelte Zustellungen, Ereignis tritt nie ein."
  },
  {
    "id": "webhooks/about-delivery",
    "path": "/de/webhooks/about-delivery",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "about-delivery",
    "pageTitle": "Wie wird ein Webhook zugestellt?",
    "section": "Wie wird ein Webhook zugestellt?",
    "anchor": "",
    "text": "Ein Webhook ist eine Anfrage, die die Plattform an eine Adresse Ihrer Wahl schickt, sobald in Ihrer Organisation etwas geschieht — hier der Weg vom Ereignis bis zur Antwort Ihres Servers, und was passiert, wenn er nicht antwortet."
  },
  {
    "id": "webhooks/about-delivery#vom-ereignis-zur-zustellung",
    "path": "/de/webhooks/about-delivery",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "about-delivery",
    "pageTitle": "Wie wird ein Webhook zugestellt?",
    "section": "Vom Ereignis zur Zustellung",
    "anchor": "vom-ereignis-zur-zustellung",
    "text": "Jedes fachliche Ereignis, das im Kontext Ihrer Organisation entsteht, wird mit Ihren Webhook-Endpunkten abgeglichen. Es trifft auf jeden Endpunkt, der aktiv ist und diesen Ereignistyp abonniert hat. Der Abgleich kennt drei Formen und keine weiteren: den genauen Ereignistyp, den ganzen Bereich ( composition.* ) und * für alles. composition.scene.published erreicht damit einen Endpunkt, in dessen Liste dieser Text, composition.* oder * steht. Für jeden passenden Endpunkt entsteht eine eigene Zustellung mit eigener Nummer ( whd_… ). Ihr Rumpf ist die Nutzlast des Ereignisses als JSON. Die Plattform signiert diesen Rumpf mit dem Geheimnis des Endpunkts (HMAC-SHA256) und schickt ihn per POST an Ihre Adresse, zusammen mit den Kopfzeilen X-Signature , X-Event-Type und X-Delivery-Id . Ihr Server hat zehn Sekunden Zeit. Antwortet er mit einem Status 2xx, gilt die Zustellung als zugestellt."
  },
  {
    "id": "webhooks/about-delivery#wenn-der-empfänger-nicht-antwortet",
    "path": "/de/webhooks/about-delivery",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "about-delivery",
    "pageTitle": "Wie wird ein Webhook zugestellt?",
    "section": "Wenn der Empfänger nicht antwortet",
    "anchor": "wenn-der-empfänger-nicht-antwortet",
    "text": "Antwortet Ihr Server mit 5xx, 408 oder 429, oder kommt gar keine Antwort (Netzfehler, Zeitüberschreitung), wertet die Plattform das als vorübergehende Störung und wiederholt: höchstens fünf Versuche, mit Wartezeiten von 2, 4, 8 und 16 Minuten, die ein Takt alle fünf Minuten aufnimmt. Nach dem fünften Fehlschlag ist die Zustellung endgültig gescheitert. Die genauen Werte stehen im Wiederholungsschema . Jede andere 4xx-Antwort (etwa 400, 401 oder 404) gilt als Fehler auf Ihrer Seite, den eine Wiederholung nicht heilt: Die Zustellung scheitert sofort endgültig, ohne weiteren Versuch. Erneut anstoßen lässt sie sich dann nur über die API."
  },
  {
    "id": "webhooks/about-delivery#wenn-ein-endpunkt-dauerhaft-scheitert",
    "path": "/de/webhooks/about-delivery",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "about-delivery",
    "pageTitle": "Wie wird ein Webhook zugestellt?",
    "section": "Wenn ein Endpunkt dauerhaft scheitert",
    "anchor": "wenn-ein-endpunkt-dauerhaft-scheitert",
    "text": "Jeder Fehlschlag erhöht einen Zähler am Endpunkt, jeder Erfolg setzt ihn auf null. Mehr als 20 Fehlschläge in Folge schalten den Endpunkt ab: Er wird inaktiv, erhält keine neuen Zustellungen mehr, und die Plattform verzeichnet das Ereignis notifications.webhook.endpoint.disabled . Ein stündlicher Lauf prüft zusätzlich, ob ein aktiver Endpunkt über der Schwelle liegt. Nach der Behebung schalten Sie ihn im Portal mit Aktivieren wieder ein; das wird bis zu eine Stunde später wirksam. Zwei Sorten Ereignisse erreichen nie einen Endpunkt: die Ereignisse der Zustellung selbst ( notifications.* ), sonst löste jede Zustellung die nächste aus, und Ereignisse ohne Organisationsbezug, etwa aus dem Plattformbetrieb. Ablauf einer Zustellung: Ereignis, Abgleich mit den aktiven Endpunkten, Signieren, POST mit zehn Sekunden Zeitlimit, dann Zugestellt bei 2xx oder Wiederholung bei 5xx, 408, 429 und Netzfehler; andere 4xx scheitern sofort; mehr als 20 Fehlschläge in Folge schalten den Endpunkt ab"
  },
  {
    "id": "webhooks/about-delivery#was-daraus-folgt",
    "path": "/de/webhooks/about-delivery",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "about-delivery",
    "pageTitle": "Wie wird ein Webhook zugestellt?",
    "section": "Was daraus folgt",
    "anchor": "was-daraus-folgt",
    "text": "Antworten Sie sofort mit 2xx und verarbeiten Sie danach. Das Zeitlimit von zehn Sekunden gilt für die ganze Anfrage. Wer erst verarbeitet und dann antwortet, riskiert unnötige Wiederholungen. Entdoppeln Sie nach X-Delivery-Id. Eine Wiederholung trägt dieselbe Nummer wie der erste Versuch. Wer die Nummer speichert, verarbeitet jede Zustellung genau einmal, auch wenn seine erste Antwort unterwegs verloren ging. Antworten Sie mit 4xx nur, wenn die Zustellung wirklich unbrauchbar ist. Eine 4xx beendet die Zustellung endgültig. Ist Ihr System nur gerade nicht bereit, antworten Sie mit 503 oder 429 — dann kommt die Plattform wieder. Prüfen Sie das Zustell-Log , wenn etwas fehlt. Es zeigt je Zustellung den Zustand, den Antwortcode und die Zahl der Versuche."
  },
  {
    "id": "webhooks/about-delivery#weiterführendes",
    "path": "/de/webhooks/about-delivery",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "about-delivery",
    "pageTitle": "Wie wird ein Webhook zugestellt?",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Was gesendet wird und wie wiederholt wird Die Signatur einer Zustellung prüfen Ereignistypen: Organisation, Konten, Abrechnung"
  },
  {
    "id": "webhooks/delivery-and-retries",
    "path": "/de/webhooks/delivery-and-retries",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "delivery-and-retries",
    "pageTitle": "Was gesendet wird und wie wiederholt wird",
    "section": "Was gesendet wird und wie wiederholt wird",
    "anchor": "",
    "text": "Die Anfrage, die Ihr Endpunkt bekommt, die Zustände einer Zustellung, das Wiederholungsschema und die Abschaltung — zum Nachschlagen beim Bau und beim Betrieb eines Empfängers."
  },
  {
    "id": "webhooks/delivery-and-retries#die-anfrage-an-ihren-endpunkt",
    "path": "/de/webhooks/delivery-and-retries",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "delivery-and-retries",
    "pageTitle": "Was gesendet wird und wie wiederholt wird",
    "section": "Die Anfrage an Ihren Endpunkt",
    "anchor": "die-anfrage-an-ihren-endpunkt",
    "text": "Jede Zustellung ist ein POST an die Adresse des Endpunkts. Der Rumpf ist die Nutzlast des Ereignisses als JSON, kompakt serialisiert (ohne Leerzeichen und Zeilenumbrüche). Nur die Nutzlast reist; der Zeitpunkt des Ereignisses, seine Kennung und der Auslöser stehen nicht im Rumpf. Bestandteil Wert Methode POST Content-Type application/json X-Signature HMAC-SHA256 über den rohen Rumpf, mit dem Signatur-Geheimnis des Endpunkts, base64url-kodiert X-Event-Type Der Ereignistyp, zum Beispiel composition.scene.published X-Delivery-Id Die Nummer der Zustellung ( whd_ und 26 Zeichen); eine Wiederholung trägt dieselbe Rumpf Die Nutzlast des Ereignisses als JSON — die Felder aus den Ereignistabellen Zeitlimit 10 Sekunden für Verbindung und Antwort So sieht eine Zustellung des Ereignisses composition.scene.published aus (Rumpf zur Lesbarkeit umgebrochen; tatsächlich kommt er in einer Zeile):"
  },
  {
    "id": "webhooks/delivery-and-retries#die-erwartete-antwort",
    "path": "/de/webhooks/delivery-and-retries",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "delivery-and-retries",
    "pageTitle": "Was gesendet wird und wie wiederholt wird",
    "section": "Die erwartete Antwort",
    "anchor": "die-erwartete-antwort",
    "text": "Ihre Antwort Wirkung Status 2xx binnen 10 Sekunden Zustellung gilt als zugestellt; der Fehlerzähler des Endpunkts springt auf null 5xx, 408 oder 429 Vorübergehender Fehler: Wiederholung nach dem Schema unten Keine Antwort binnen 10 Sekunden, Verbindungsfehler Wie ein vorübergehender Fehler: Wiederholung Jede andere 4xx Endgültiger Fehler: keine Wiederholung, Zustellung endgültig gescheitert Bei einem Fehlschlag wird der Antworttext gekürzt (höchstens 4 kB) im Zustell-Log gespeichert; bei einer erfolgreichen Zustellung wird nur der Statuscode festgehalten. Ein kurzer, sprechender Text hilft Ihnen später beim Nachsehen; nötig ist er nicht."
  },
  {
    "id": "webhooks/delivery-and-retries#zustände-einer-zustellung",
    "path": "/de/webhooks/delivery-and-retries",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "delivery-and-retries",
    "pageTitle": "Was gesendet wird und wie wiederholt wird",
    "section": "Zustände einer Zustellung",
    "anchor": "zustände-einer-zustellung",
    "text": "Zustand in der API Im Portal Bedeutung pending Ausstehend Versand läuft oder steht unmittelbar bevor delivered Zugestellt Ihr Server hat mit 2xx geantwortet; deliveredAt ist gesetzt failed Fehlgeschlagen Vorübergehender Fehler; nextRetryAt nennt den nächsten Versuch dead dead Endgültig gescheitert: fünfter Fehlschlag oder eine 4xx-Antwort; deadAt ist gesetzt Der Weg ist pending → delivered oder failed ; aus failed führt jeder weitere Versuch wieder über pending nach delivered , failed oder dead . Für den Zustand dead führt das Portal keine eigene Beschriftung; das Zustell-Log zeigt dort den Wert selbst. Die Beschriftung Wiederholung läuft ist im Portal angelegt, aber keinem Zustand zugeordnet."
  },
  {
    "id": "webhooks/delivery-and-retries#wiederholungsschema",
    "path": "/de/webhooks/delivery-and-retries",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "delivery-and-retries",
    "pageTitle": "Was gesendet wird und wie wiederholt wird",
    "section": "Wiederholungsschema",
    "anchor": "wiederholungsschema",
    "text": "Die Plattform wiederholt nur bei 5xx, 408, 429, Zeitüberschreitung und Netzfehlern. Je Zustellung gibt es höchstens fünf Versuche. Die Wartezeit nach dem n-ten Fehlschlag beträgt 1 Minute · 2ⁿ, mit ±20 % Streuung. Fällige Wiederholungen nimmt ein Takt alle fünf Minuten auf, bis zu 100 je Lauf — die tatsächliche Wartezeit ist deshalb bis zu fünf Minuten länger als der Sollwert. Versuch Nach Fehlschlag warten Bei erneutem Fehlschlag 1 2 Minuten ±20 % Versuch 2 2 4 Minuten ±20 % Versuch 3 3 8 Minuten ±20 % Versuch 4 4 16 Minuten ±20 % Versuch 5 5 — Endgültig gescheitert ( dead ) Eine 4xx-Antwort außer 408 und 429 beendet die Zustellung sofort, unabhängig vom Versuchszähler."
  },
  {
    "id": "webhooks/delivery-and-retries#erneut-zustellen-von-hand",
    "path": "/de/webhooks/delivery-and-retries",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "delivery-and-retries",
    "pageTitle": "Was gesendet wird und wie wiederholt wird",
    "section": "Erneut zustellen von Hand",
    "anchor": "erneut-zustellen-von-hand",
    "text": "Im Zustell-Log des Endpunkts steht an jeder Zustellung mit Status Fehlgeschlagen die Schaltfläche Erneut zustellen . Für eine Zustellung im Zustand dead zeigt das Portal keine Schaltfläche; sie stoßen Sie über die API an: Die Antwort (Status 201) ist die Zustellung mit dem Ausgang des neuen Versuchs. Der neue Versuch wird mit dem aktuell gültigen Geheimnis des Endpunkts signiert und trägt dieselbe X-Delivery-Id . Ist der Endpunkt inaktiv, wird nichts gesendet; die Antwort trägt die Zustellung unverändert. Die API prüft den Zustand nicht: Auch eine bereits zugestellte Zustellung lässt sich so erneut senden — Ihr Empfänger sieht dann dieselbe Nummer ein zweites Mal."
  },
  {
    "id": "webhooks/delivery-and-retries#abschaltung-eines-endpunkts",
    "path": "/de/webhooks/delivery-and-retries",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "delivery-and-retries",
    "pageTitle": "Was gesendet wird und wie wiederholt wird",
    "section": "Abschaltung eines Endpunkts",
    "anchor": "abschaltung-eines-endpunkts",
    "text": "Jeder Fehlschlag erhöht den Zähler consecutiveFailures des Endpunkts, jeder Erfolg setzt ihn auf null. Liegt der Zähler über 20, wird der Endpunkt inaktiv ( active: false ), und die Plattform verzeichnet notifications.webhook.endpoint.disabled . Ein stündlicher Lauf holt nach, was der Zähler beim Versand verfehlt hat. Ein inaktiver Endpunkt bekommt keine neuen Zustellungen; im Portal steht er auf Inaktiv mit dem Hinweis „letzter Fehler …“. Wieder einschalten: Aktivieren oder PATCH /v1/webhooks/{id} mit {\"active\": true} . Das wirkt nicht sofort: Die Plattform merkt sich je Ereignistyp bis zu eine Stunde, ob ein aktiver Endpunkt ihn abonniert. Nach dem Aktivieren oder nach dem Ergänzen von Ereignissen kann es deshalb bis zu eine Stunde dauern, bis die erste Zustellung eines neu abonnierten Typs ankommt; neu angelegte Endpunkte sind sofort wirksam."
  },
  {
    "id": "webhooks/delivery-and-retries#das-zustell-log-über-die-api",
    "path": "/de/webhooks/delivery-and-retries",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "delivery-and-retries",
    "pageTitle": "Was gesendet wird und wie wiederholt wird",
    "section": "Das Zustell-Log über die API",
    "anchor": "das-zustell-log-über-die-api",
    "text": "Die Antwort ist eine Seite mit items und nextCursor (wenn es weitergeht). Der nächste Aufruf bekommt cursor=<nextCursor> ; limit liegt zwischen 1 und 200, ohne Angabe bei 50. Feld Bedeutung id Nummer der Zustellung — dieselbe wie in X-Delivery-Id endpointId , tenantId Endpunkt und Organisation eventType Der Ereignistyp payloadSignature Die Signatur, die als X-Signature gesendet wurde status pending , delivered , failed oder dead attemptCount Zahl der bisherigen Versuche responseCode , responseBody Antwort Ihres Servers; responseCode ist null , wenn keine kam; responseBody wird nur bei fehlgeschlagenen Versuchen gefüllt (auf 4 kB gekürzt) — nach einer erfolgreichen Zustellung ist es null oder trägt noch den Text des letzten Fehlversuchs nextRetryAt Nächster geplanter Versuch, nur bei failed dispatchedAt , deliveredAt , deadAt , createdAt Zeitpunkte des letzten Versands, der Zustellung, des endgültigen Scheiterns und der Anlage Der gesendete Rumpf selbst steht nicht in der Liste."
  },
  {
    "id": "webhooks/delivery-and-retries#rechte",
    "path": "/de/webhooks/delivery-and-retries",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "delivery-and-retries",
    "pageTitle": "Was gesendet wird und wie wiederholt wird",
    "section": "Rechte",
    "anchor": "rechte",
    "text": "Aktion Recht Rollen Zustell-Log lesen webhook.delivery.list Inhaber, Manager Erneut zustellen webhook.delivery.retry Inhaber Diese Rechte gelten für angemeldete Konten. Ein API-Schlüssel ( ak_… ) kann sie nicht tragen; die Aufrufe brauchen das Zugriffstoken einer Anmeldung, siehe Anmeldung ."
  },
  {
    "id": "webhooks/delivery-and-retries#weiterführendes",
    "path": "/de/webhooks/delivery-and-retries",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "delivery-and-retries",
    "pageTitle": "Was gesendet wird und wie wiederholt wird",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Wie wird ein Webhook zugestellt? Die Signatur einer Zustellung prüfen Wenn keine Zustellung ankommt"
  },
  {
    "id": "webhooks/create-endpoint",
    "path": "/de/webhooks/create-endpoint",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "create-endpoint",
    "pageTitle": "Einen Webhook-Endpunkt anlegen",
    "section": "Einen Webhook-Endpunkt anlegen",
    "anchor": "",
    "text": "Am Ende hat Ihre Organisation einen aktiven Endpunkt, der die gewählten Ereignisse an Ihre https-Adresse bekommt, und Sie haben das Signatur-Secret sicher abgelegt."
  },
  {
    "id": "webhooks/create-endpoint#schritte",
    "path": "/de/webhooks/create-endpoint",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "create-endpoint",
    "pageTitle": "Einen Webhook-Endpunkt anlegen",
    "section": "Schritte",
    "anchor": "schritte",
    "text": ""
  },
  {
    "id": "webhooks/create-endpoint#im-portal",
    "path": "/de/webhooks/create-endpoint",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "create-endpoint",
    "pageTitle": "Einen Webhook-Endpunkt anlegen",
    "section": "Im Portal",
    "anchor": "im-portal",
    "text": "Öffnen Sie Einstellungen › Organisation › Webhooks . Klicken Sie auf Endpunkt anlegen . Der Dialog Webhook-Endpunkt anlegen öffnet sich. Tragen Sie unter Endpunkt-URL die Adresse Ihres Empfängers ein, zum Beispiel https://example.com/hooks/signage . Wählen Sie unter Abonnierte Ereignisse im Feld Ereignis suchen … die Ereignistypen. Das Feld filtert den Katalog, jede Wahl legt sich als Chip über das Feld; mindestens ein Ereignis ist nötig. Klicken Sie auf Anlegen . Der Kasten Signatur-Secret — nur jetzt sichtbar erscheint. Klicken Sie auf Secret kopieren und legen Sie den Wert dort ab, wo Ihr Empfänger ihn liest. Erst dann Verstanden . Der Dialog Webhook-Endpunkt anlegen mit dem Feld Endpunkt-URL (1), der Auswahl Abonnierte Ereignisse samt Suchfeld Ereignis suchen … (2) und der Schaltfläche Anlegen (3) Anlegen wird erst wählbar, wenn eine Adresse steht und mindestens ein Ereignis als Chip in der Auswahl liegt."
  },
  {
    "id": "webhooks/create-endpoint#über-die-api",
    "path": "/de/webhooks/create-endpoint",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "create-endpoint",
    "pageTitle": "Einen Webhook-Endpunkt anlegen",
    "section": "Über die API",
    "anchor": "über-die-api",
    "text": "$TOKEN ist das Zugriffstoken einer Anmeldung als Inhaber — ein API-Schlüssel kann die Webhook-Rechte nicht tragen (siehe Anmeldung ); $SZENERA_API ist die Basis-URL Ihrer Umgebung. Die Antwort (Status 201) enthält den Endpunkt und — nur in dieser Antwort — das Secret: Das Secret ist danach nicht mehr abrufbar; geht es verloren, erneuern Sie es (siehe Optionen). Die Liste events prüft der Server, bevor er den Endpunkt anlegt: Erlaubt sind eine genaue Ereignisart, ein Bereich als composition.* oder * für alles. Ein unbekannter Name ist 422 WebhookEndpoint.EventUnknown und wird in der Antwort genannt — ein Tippfehler soll hier auffallen und nicht als Endpunkt enden, der stumm bleibt. Welche Namen es gibt, liefert GET /v1/webhooks/event-types , siehe Den Ereigniskatalog über die API abrufen ."
  },
  {
    "id": "webhooks/create-endpoint#optionen",
    "path": "/de/webhooks/create-endpoint",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "create-endpoint",
    "pageTitle": "Einen Webhook-Endpunkt anlegen",
    "section": "Optionen",
    "anchor": "optionen",
    "text": "Aufgabe Im Portal Über die API Adresse oder Ereignisse ändern Bearbeiten → Dialog Webhook bearbeiten → Speichern PATCH /v1/webhooks/{id} mit url und/oder events Endpunkt anhalten oder fortsetzen Deaktivieren / Aktivieren PATCH /v1/webhooks/{id} mit {\"active\": false} bzw. {\"active\": true} Secret erneuern Secret rotieren → neuer Kasten Signatur-Secret — nur jetzt sichtbar POST /v1/webhooks/{id}/rotate-secret → Antwort {\"secret\": \"whsec_…\"} Endpunkt entfernen Löschen → Dialog Webhook-Endpunkt löschen? → Löschen DELETE /v1/webhooks/{id} → {\"deleted\": true} Endpunkte auflisten Liste unter Webhook-Endpunkte GET /v1/webhooks?limit=50 (Blättern mit cursor ) Beim Bearbeiten bleibt das Signatur-Geheimnis unverändert. Ein deaktivierter Endpunkt behält Adresse, Ereignisse und Zustell-Historie. Ergänzte Ereignisse und ein wieder eingeschalteter Endpunkt werden bis zu eine Stunde später wirksam; neu angelegte Endpunkte sofort."
  },
  {
    "id": "webhooks/create-endpoint#regeln-für-adresse-und-ereignisse",
    "path": "/de/webhooks/create-endpoint",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "create-endpoint",
    "pageTitle": "Einen Webhook-Endpunkt anlegen",
    "section": "Regeln für Adresse und Ereignisse",
    "anchor": "regeln-für-adresse-und-ereignisse",
    "text": "Nur https, nur öffentlich. Die Adresse beginnt mit https:// ; abgewiesen werden localhost , ::1 , Namen auf .local sowie 127. , 10. , 192.168. , 169.254. und 172.16. bis 172.31. — Status 422, WebhookEndpoint.UrlInvalid , beim Anlegen wie beim Ändern. Mindestens ein Ereignis, bekannte Namen. Erlaubt sind ein genauer Ereignistyp, ein Bereich wie composition.* und * . Ein Tippfehler im Typ wird mit 422 WebhookEndpoint.EventUnknown abgewiesen, ebenso ein Typ, der nicht abonnierbar ist. Das Paket zählt beim Anlegen und beim Erneuern des Secrets. Ohne Webhooks im Paket antwortet das Anlegen mit Status 403, Billing.FeatureNotEntitled ; im Portal sind Endpunkt anlegen und Secret rotieren gesperrt, über die API bleibt POST /v1/webhooks/{id}/rotate-secret möglich. Bestehende Endpunkte laufen weiter. Rechte. Anlegen, Ändern, Erneuern und Löschen brauchen die Rolle Inhaber ( webhook.create , webhook.update , webhook.delete ); die Liste sehen Inhaber und Manager ( webhook.list )."
  },
  {
    "id": "webhooks/create-endpoint#ergebnis-prüfen",
    "path": "/de/webhooks/create-endpoint",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "create-endpoint",
    "pageTitle": "Einen Webhook-Endpunkt anlegen",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "Der Endpunkt steht in der Liste Webhook-Endpunkte mit Status Aktiv . Nach dem nächsten passenden Ereignis zeigt das Zustell-Log eine Zeile mit Status Zugestellt und dem Antwortcode Ihres Servers (etwa „HTTP 200“). Ihr Empfänger hat eine Anfrage mit den Kopfzeilen X-Signature , X-Event-Type und X-Delivery-Id gesehen."
  },
  {
    "id": "webhooks/create-endpoint#wenn-etwas-nicht-klappt",
    "path": "/de/webhooks/create-endpoint",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "create-endpoint",
    "pageTitle": "Einen Webhook-Endpunkt anlegen",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung Status 422, WebhookEndpoint.UrlInvalid Adresse ist nicht https oder zeigt auf ein privates Netz Öffentliche https-Adresse verwenden Status 403, Billing.FeatureNotEntitled ; im Portal sind Endpunkt anlegen und Secret rotieren gesperrt Ihr Paket enthält keine Webhooks Paket wechseln; bestehende Endpunkte lassen sich weiter bearbeiten, abschalten und löschen Status 403, Permission.Denied Ihr Konto hat nicht die Rolle Inhaber, oder Sie rufen mit einem API-Schlüssel auf Als Inhaber anmelden; Zugriffstoken einer Anmeldung statt API-Schlüssel Status 422, WebhookEndpoint.EventUnknown Ein Eintrag in events steht in keiner der drei erlaubten Formen; details.unknownEvents nennt ihn Namen gegen den Ereigniskatalog prüfen Endpunkt aktiv, aber nie eine Zustellung Ereignis tritt nicht ein oder Abonnement noch nicht wirksam Siehe Wenn keine Zustellung ankommt"
  },
  {
    "id": "webhooks/create-endpoint#weiterführendes",
    "path": "/de/webhooks/create-endpoint",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "create-endpoint",
    "pageTitle": "Einen Webhook-Endpunkt anlegen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Die Signatur einer Zustellung prüfen Ereignistypen: Organisation, Konten, Abrechnung Den Ereigniskatalog über die API abrufen Wenn keine Zustellung ankommt"
  },
  {
    "id": "webhooks/verify-signature",
    "path": "/de/webhooks/verify-signature",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "verify-signature",
    "pageTitle": "Die Signatur einer Zustellung prüfen",
    "section": "Die Signatur einer Zustellung prüfen",
    "anchor": "",
    "text": "Am Ende nimmt Ihr Empfänger nur Zustellungen an, die wirklich von der Plattform stammen, verarbeitet jede genau einmal und übersteht einen Wechsel des Secrets."
  },
  {
    "id": "webhooks/verify-signature#schritte",
    "path": "/de/webhooks/verify-signature",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "verify-signature",
    "pageTitle": "Die Signatur einer Zustellung prüfen",
    "section": "Schritte",
    "anchor": "schritte",
    "text": "Lesen Sie den Rumpf als rohe Bytes , bevor irgendetwas ihn als JSON deutet. Die Signatur gilt für genau die Bytes, die gesendet wurden; wer den Rumpf erst einliest und neu serialisiert, ändert womöglich Reihenfolge oder Abstände und bekommt eine andere Signatur. Rechnen Sie HMAC-SHA256 über diese Bytes mit dem Secret als Schlüssel und kodieren Sie das Ergebnis base64url, so wie Node.js es mit digest('base64url') erzeugt. Vergleichen Sie mit X-Signature in konstanter Zeit und nur bei gleicher Länge. Ein gewöhnlicher Zeichenvergleich bricht beim ersten Unterschied ab und verrät über die Laufzeit, wie viele Zeichen stimmen. Verzweigen Sie nach X-Event-Type. Die Kopfzeile nennt den Ereignistyp, ohne dass Sie den Rumpf lesen müssen. Entdoppeln Sie nach X-Delivery-Id. Merken Sie sich jede verarbeitete Nummer. Eine Wiederholung trägt dieselbe Nummer; ist sie bekannt, antworten Sie mit 2xx und tun sonst nichts. Erst jetzt verarbeiten — und schnell mit 2xx antworten. Das Zeitlimit beträgt zehn Sekunden; lange Arbeit gehört hinter die Antwort. So sieht die Prüfung in Node.js aus; es ist das Beispiel, das auch das Portal unter Zustellungen auf Echtheit prüfen zeigt: Der Aufruf im Empfänger: isFromSignage(rawBody, req.headers['x-signature'], process.env.WEBHOOK_SECRET) . Kopfzeilennamen kommen je nach Server in Kleinbuchstaben an; die Plattform sendet X-Signature ."
  },
  {
    "id": "webhooks/verify-signature#optionen",
    "path": "/de/webhooks/verify-signature",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "verify-signature",
    "pageTitle": "Die Signatur einer Zustellung prüfen",
    "section": "Optionen",
    "anchor": "optionen",
    "text": ""
  },
  {
    "id": "webhooks/verify-signature#das-secret-erneuern",
    "path": "/de/webhooks/verify-signature",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "verify-signature",
    "pageTitle": "Die Signatur einer Zustellung prüfen",
    "section": "Das Secret erneuern",
    "anchor": "das-secret-erneuern",
    "text": "Das Secret hat die Form whsec_ plus mindestens 32 Zeichen aus A–Z , a–z , 0–9 , _ und - . Es wird nur beim Anlegen und nach einer Rotation angezeigt; abrufen lässt es sich nicht. Ist es verloren oder soll es gewechselt werden: Stellen Sie Ihren Empfänger so ein, dass er das neue Secret sofort nach der Anzeige übernehmen kann — etwa über eine Konfiguration, die ohne Neustart nachlädt. Klicken Sie im Portal an dem Endpunkt auf Secret rotieren (ohne ein Paket mit Webhooks gesperrt), oder rufen Sie POST /v1/webhooks/{id}/rotate-secret auf — über die API bleibt das auch ohne Paket möglich. Übernehmen Sie das neue Secret aus dem Kasten Signatur-Secret — nur jetzt sichtbar ( Secret kopieren ) beziehungsweise aus der Antwort {\"secret\": \"whsec_…\"} . Ab dem nächsten Versand wird ausschließlich mit dem neuen Secret signiert — auch Wiederholungen älterer Zustellungen. Es gibt keine Übergangsphase, in der beide gelten. Bis Ihr Empfänger das neue Secret kennt, weist er echte Zustellungen ab; die Plattform wiederholt sie nur, wenn Sie in dieser Zeit mit 5xx statt 4xx antworten. Adresse, Ereignisse und Zustell-Historie des Endpunkts bleiben bei der Rotation erhalten."
  },
  {
    "id": "webhooks/verify-signature#ergebnis-prüfen",
    "path": "/de/webhooks/verify-signature",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "verify-signature",
    "pageTitle": "Die Signatur einer Zustellung prüfen",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "Eine echte Zustellung passiert die Prüfung, und im Zustell-Log steht sie als Zugestellt mit Ihrem Antwortcode. Eine Anfrage mit verändertem Rumpf oder ohne gültige X-Signature wird abgewiesen. Eine erneut zugestellte Nummer ( Erneut zustellen im Zustell-Log) löst bei Ihnen keine zweite Verarbeitung aus."
  },
  {
    "id": "webhooks/verify-signature#wenn-etwas-nicht-klappt",
    "path": "/de/webhooks/verify-signature",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "verify-signature",
    "pageTitle": "Die Signatur einer Zustellung prüfen",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung Signatur passt nie Der Rumpf wurde vor der Prüfung verändert (neu serialisiert, Zeichensatz umgewandelt, Leerraum entfernt) Rohe Bytes prüfen, bevor ein JSON-Parser oder ein Rahmenwerk den Rumpf anfasst Signatur passt seit einem bestimmten Zeitpunkt nicht mehr Das Secret wurde rotiert; Ihr Empfänger prüft noch mit dem alten Neues Secret übernehmen; Zustellungen aus der Lücke im Zustell-Log Erneut zustellen Signatur passt, Vergleich schlägt trotzdem fehl Längenprüfung oder Kodierung stimmen nicht (hex statt base64url, Füllzeichen angehängt) Genau die Form aus dem Beispiel verwenden Dieselbe Zustellung wird zweimal verarbeitet Entdopplung fehlt; eine Wiederholung oder ein manuelles Erneut-Zustellen trug dieselbe X-Delivery-Id Nummern speichern und bekannte Nummern mit 2xx quittieren"
  },
  {
    "id": "webhooks/verify-signature#weiterführendes",
    "path": "/de/webhooks/verify-signature",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "verify-signature",
    "pageTitle": "Die Signatur einer Zustellung prüfen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Was gesendet wird und wie wiederholt wird Einen Webhook-Endpunkt anlegen Wenn keine Zustellung ankommt"
  },
  {
    "id": "webhooks/event-types-organization",
    "path": "/de/webhooks/event-types-organization",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-organization",
    "pageTitle": "Ereignistypen: Organisation, Konten, Abrechnung",
    "section": "Ereignistypen: Organisation, Konten, Abrechnung",
    "anchor": "",
    "text": "Die abonnierbaren Ereignistypen aus Organisation, Speicher-Verbindungen, Zugang, Konten, Sitzungen, Abrechnung und Beitritt — zum Nachschlagen, wenn Sie einen Endpunkt anlegen oder eine Zustellung deuten."
  },
  {
    "id": "webhooks/event-types-organization#so-lesen-sie-die-tabellen",
    "path": "/de/webhooks/event-types-organization",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-organization",
    "pageTitle": "Ereignistypen: Organisation, Konten, Abrechnung",
    "section": "So lesen Sie die Tabellen",
    "anchor": "so-lesen-sie-die-tabellen",
    "text": "Ereignistyp ist die Zeichenkette, die Sie beim Endpunkt abonnieren. Sie kommt bei jeder Zustellung in der Kopfzeile X-Event-Type an. Statt einzelner Typen dürfen Sie auch einen ganzen Bereich abonnieren ( composition.* ) oder mit * alles. Mehr Formen gibt es nicht, und alles andere weist der Server ab, siehe Den Ereigniskatalog über die API abrufen . Nutzlast-Kern nennt zwei bis vier der wichtigsten Felder, die im Rumpf der Zustellung stehen. Es ist ein Auszug. Die vollständige Form bestimmt der jeweilige Bereich, und sie kann weitere Felder tragen. Zeitpunkt, Ereigniskennung und Auslöser reisen nicht im Rumpf. Wann es entsteht ist ein Halbsatz zur Einordnung, keine Aussage über Häufigkeit. Das Portal bietet unter Abonnierte Ereignisse genau die Typen dieser Seite und der Seite Ereignistypen: Inhalte, Programme, Geräte, Apps zur Auswahl — dieselbe Liste, die GET /v1/webhooks/event-types liefert. Alles andere lässt sich nicht abonnieren, das Anlegen eines Endpunkts weist es ab. Welche Typen das sind, steht unter Was nie ankommt . Zugestellt wird nur, was im Kontext Ihrer Organisation entsteht."
  },
  {
    "id": "webhooks/event-types-organization#die-ereignistypen",
    "path": "/de/webhooks/event-types-organization",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-organization",
    "pageTitle": "Ereignistypen: Organisation, Konten, Abrechnung",
    "section": "Die Ereignistypen",
    "anchor": "die-ereignistypen",
    "text": ""
  },
  {
    "id": "webhooks/event-types-organization#organisation",
    "path": "/de/webhooks/event-types-organization",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-organization",
    "pageTitle": "Ereignistypen: Organisation, Konten, Abrechnung",
    "section": "Organisation",
    "anchor": "organisation",
    "text": "Lebenszyklus der Organisation, Marke, eigene Adresse und Unternehmens-Anmeldung. tenancy.tenant.suspended und .deleted sind die Auslöser, an denen weitere Bereiche nachziehen. Ereignistyp Nutzlast-Kern Wann es entsteht tenancy.tenant.created tenantId , slug , displayName Eine Organisation wurde angelegt. tenancy.tenant.updated tenantId , changedFields Stammdaten der Organisation wurden geändert. tenancy.tenant.suspended tenantId , reason , suspendedById , deletionDueAt Die Organisation wurde gesperrt, und deletionDueAt nennt den Löschtermin. tenancy.tenant.reactivated tenantId , reactivatedById Eine Sperre wurde aufgehoben. tenancy.tenant.frozen tenantId , reason , frozenById Die Organisation wurde eingefroren. tenancy.tenant.unfrozen tenantId , unfrozenById Das Einfrieren wurde aufgehoben. tenancy.tenant.deletion-warned tenantId , deletesAt , daysLeft Warnung vor der Löschung, daysLeft nennt die Resttage. tenancy.tenant.deleted tenantId , slug , deletedById Die Organisation wurde gelöscht. tenancy.branding.updated tenantId , changedFields Marke (Logo, Farben) wurde geändert. tenancy.custom-domain.verified tenantId , customDomain Eine eigene Portal-Adresse wurde bestätigt. tenancy.custom-domain.removed tenantId , domain Eine eigene Portal-Adresse wurde entfernt. tenancy.sso.configured tenantId , providerId , protocol , status Die Unternehmens-Anmeldung wurde eingerichtet oder geändert. tenancy.sso.disabled tenantId , providerId , protocol Die Unternehmens-Anmeldung wurde abge"
  },
  {
    "id": "webhooks/event-types-organization#speicher-verbindungen",
    "path": "/de/webhooks/event-types-organization",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-organization",
    "pageTitle": "Ereignistypen: Organisation, Konten, Abrechnung",
    "section": "Speicher-Verbindungen",
    "anchor": "speicher-verbindungen",
    "text": "Dauervollmachten auf fremde Speicher-Konten. broken beantwortet die Frage, seit wann keine neuen Dateien mehr kommen. Ereignistyp Nutzlast-Kern Wann es entsteht cloud-connections.connection.connected connectionId , provider , accountLabel , repaired Ein Speicher-Konto wurde verbunden, und repaired unterscheidet Erstverbindung und Reparatur. cloud-connections.connection.disconnected connectionId , provider , revocationAcknowledged Die Verbindung wurde getrennt. cloud-connections.connection.broken connectionId , provider , reason Die Vollmacht trägt nicht mehr. Ab hier steht der Abgleich still. Kein Auslöser im Portal."
  },
  {
    "id": "webhooks/event-types-organization#zugang-rollen-und-freigaben",
    "path": "/de/webhooks/event-types-organization",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-organization",
    "pageTitle": "Ereignistypen: Organisation, Konten, Abrechnung",
    "section": "Zugang: Rollen und Freigaben",
    "anchor": "zugang-rollen-und-freigaben",
    "text": "Rollen der Organisation und Freigaben auf einzelne Ressourcen. Ereignistyp Nutzlast-Kern Wann es entsteht access.role.created roleId , tenantId , name , permissions Eine Rolle wurde angelegt. access.role.updated roleId , changedFields , newPermissions , newVersion Rechte oder Name einer Rolle wurden geändert. access.role.deleted roleId , tenantId , requesterId Eine Rolle wurde gelöscht. access.grant.created grantId , userId , resourceType , resourceId Eine Freigabe auf eine einzelne Ressource wurde erteilt. access.grant.revoked grantId , userId , resourceType , resourceId Eine Freigabe wurde entzogen. access.grant.expired grantId , userId , resourceId , expiredAt Eine befristete Freigabe ist abgelaufen. access.permission-catalog.rebuilt tenantId , trigger , entryCount Der Rechtekatalog der Organisation wurde neu aufgebaut, und trigger nennt den Anlass."
  },
  {
    "id": "webhooks/event-types-organization#konten-schlüssel-und-geräte-identitäten",
    "path": "/de/webhooks/event-types-organization",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-organization",
    "pageTitle": "Ereignistypen: Organisation, Konten, Abrechnung",
    "section": "Konten, Schlüssel und Geräte-Identitäten",
    "anchor": "konten-schlüssel-und-geräte-identitäten",
    "text": "Benutzerkonten, API-Schlüssel, SCIM-Zugänge, Anwendungen mit delegiertem Zugang und die Identität von Geräten. Die Konten und Schlüssel des Betreibers gehören nicht dazu, sie sind nicht abonnierbar (siehe Was nie ankommt ). Ereignistyp Nutzlast-Kern Wann es entsteht identity.user.created userId , tenantId , email , initialRoles Ein Benutzerkonto wurde angelegt. identity.user.updated userId , changedFields , newValues Profildaten eines Kontos wurden geändert. identity.user.role.changed userId , previousRoles , newRoles Die Rollen eines Kontos wurden geändert. identity.user.suspended userId , reason , cascadeSource Ein Konto wurde gesperrt. identity.user.deleted userId , deletedById , pseudonymizationJobId Ein Konto wurde gelöscht, und die Pseudonymisierung ist angestoßen. identity.user.pseudonymized userId , pseudonymId , entriesReplaced Personenbezogene Einträge eines gelöschten Kontos wurden ersetzt. identity.user.mfa.enabled userId , tenantId , enabledAt Der zweite Faktor wurde eingerichtet. identity.user.mfa.disabled userId , tenantId , disabledAt Der zweite Faktor wurde entfernt. identity.user.mfa.recovery-codes.regenerated userId , tenantId , regeneratedAt Neue Wiederherstellungscodes wurden erzeugt. identity.user.password-changed userId , via , changedAt Das Passwort wurde geändert, via nennt den Weg. identity.api-key.minted apiKeyId , name , keyPrefix , scope Ein API-Schlüssel wurde erstellt. identity.api-key.revoked apiKeyId , revokedById , reason Ein API-Schlüssel wu"
  },
  {
    "id": "webhooks/event-types-organization#sitzungen-und-anmeldung",
    "path": "/de/webhooks/event-types-organization",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-organization",
    "pageTitle": "Ereignistypen: Organisation, Konten, Abrechnung",
    "section": "Sitzungen und Anmeldung",
    "anchor": "sitzungen-und-anmeldung",
    "text": "Anmeldungen, Verlängerung von Sitzungen, Einmal-Links und das Handeln des Plattformbetriebs im Namen eines Kontos. sessions.session.theft-detected ist der Alarm bei einem wiederverwendeten Sitzungs-Token. Ereignistyp Nutzlast-Kern Wann es entsteht sessions.login.succeeded sessionId , userId , kind , method Eine Anmeldung ist gelungen, method nennt den Weg. sessions.login.failed email , reason , ipAddress Eine Anmeldung ist gescheitert. sessions.mfa.verified sessionId , userId , via Der zweite Faktor wurde bestätigt. sessions.mfa.failed sessionId , userId , attemptCount Der zweite Faktor wurde falsch eingegeben. sessions.session.refreshed sessionId , familyId , userId Eine Sitzung wurde verlängert (Dauerverkehr). sessions.session.theft-detected sessionId , familyId , replayedHashPrefix Ein bereits verbrauchtes Sitzungs-Token wurde erneut vorgelegt. sessions.session.terminated sessionId , userId , reason Eine Sitzung wurde beendet. sessions.session.all-terminated userId , revokedCount , keptSessionId Alle Sitzungen eines Kontos wurden beendet. sessions.password-reset.requested passwordResetId , subjectKind , purpose Ein Passwort-Zurücksetzen wurde angefordert. sessions.password-reset.completed passwordResetId , userId , subjectKind Ein Passwort wurde über das Zurücksetzen neu gesetzt. sessions.magic-link.requested magicLinkId , email , expiresAt Ein Anmeldelink wurde angefordert. sessions.magic-link.consumed magicLinkId , sessionId , userId Ein Anmeldelink wurde genutzt. sessio"
  },
  {
    "id": "webhooks/event-types-organization#abrechnung",
    "path": "/de/webhooks/event-types-organization",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-organization",
    "pageTitle": "Ereignistypen: Organisation, Konten, Abrechnung",
    "section": "Abrechnung",
    "anchor": "abrechnung",
    "text": "Vertrag, Rechnungen, Zahlungsmittel und die freigeschalteten Funktionen. Ein Paketwechsel meldet sich zweimal: subscription.package-changed sagt, dass der Vertrag auf einem anderen Paket steht (bei jedem Wechsel), und feature-entitlements.changed sagt, dass sich einer der Anzeige-Schalter bewegt hat, und nennt in changedKeys , welcher. Getrennte Ereignisse für Auf- und Abstieg gibt es nicht — die Richtung steht in from und to . Ereignistyp Nutzlast-Kern Wann es entsteht billing.subscription.created subscriptionId , tenantId , status , billingCycle , currentPeriodEnd Ein Vertrag wurde angelegt. billing.subscription.activated subscriptionId , tenantId , from ( trial / past_due ) Der Vertrag ist aktiv, from nennt den vorigen Zustand. billing.subscription.past-due subscriptionId , graceUntil , reason ( payment-failed / trial-ended ) Zahlung ausgeblieben oder Testphase beendet. graceUntil nennt die Frist. billing.subscription.grace-lapsed subscriptionId , tenantId , graceLapsedAt , suspendsAt Die Frist ist verstrichen, suspendsAt nennt die Sperre. billing.subscription.trial-ending subscriptionId , tenantId , trialEndsAt , daysLeft Die Testphase endet bald, daysLeft nennt die Resttage. billing.subscription.suspended subscriptionId , tenantId Der Vertrag wurde ausgesetzt. billing.subscription.canceled subscriptionId , reason Der Vertrag wurde gekündigt. billing.subscription.package-changed tenantId , from (nullable), to , at Der Vertrag steht auf einem anderen Paket. Es kommt bei je"
  },
  {
    "id": "webhooks/event-types-organization#beitritt-und-einrichtung",
    "path": "/de/webhooks/event-types-organization",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-organization",
    "pageTitle": "Ereignistypen: Organisation, Konten, Abrechnung",
    "section": "Beitritt und Einrichtung",
    "anchor": "beitritt-und-einrichtung",
    "text": "Einladungen, Einrichtungsassistent, Bereitstellung einer neuen Organisation, Beitrittsanfragen und Selbst-Registrierung. Ereignistyp Nutzlast-Kern Wann es entsteht onboarding.invitation.sent invitationId , email , defaultRoleId , expiresAt Eine Einladung wurde verschickt. onboarding.invitation.resent invitationId , email , expiresAt Eine Einladung wurde erneut verschickt. onboarding.invitation.revoked invitationId , email , revokedById Eine Einladung wurde zurückgezogen. onboarding.invitation.accepted invitationId , email , acceptedByUserId Eine Einladung wurde angenommen. onboarding.invitation.expired invitationId , tenantId , email Eine Einladung ist abgelaufen. onboarding.setup-step.completed tenantId , step , via Ein Schritt des Einrichtungsassistenten wurde abgeschlossen. onboarding.setup-wizard.completed tenantId , completedAt Der Einrichtungsassistent wurde abgeschlossen. onboarding.tenant.provisioning-started tenantId , slug , provisionedById Die Bereitstellung einer neuen Organisation hat begonnen. onboarding.tenant.provisioning-completed tenantId , subscriptionId , ownerEmail Die Bereitstellung einer neuen Organisation ist abgeschlossen. onboarding.join-request.received joinRequestId , tenantId , email , displayName Eine Beitrittsanfrage ist eingegangen. onboarding.join-request.decided joinRequestId , tenantId , email , decision ( accepted / rejected ), decidedById Eine Beitrittsanfrage wurde entschieden ( accepted oder rejected ). onboarding.self-registration.chang"
  },
  {
    "id": "webhooks/event-types-organization#weiterführendes",
    "path": "/de/webhooks/event-types-organization",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-organization",
    "pageTitle": "Ereignistypen: Organisation, Konten, Abrechnung",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Ereignistypen: Inhalte, Programme, Geräte, Apps Einen Webhook-Endpunkt anlegen Was gesendet wird und wie wiederholt wird"
  },
  {
    "id": "webhooks/event-types-content-devices",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "anchor": "",
    "text": "Die abonnierbaren Ereignistypen aus Medien, Szenen, Programmen, Marktplatz, Flotte, App-Laufzeit, Wiedergabe, Auswertung und Werbung — die Ereignisse, mit denen die meisten Anbindungen beginnen."
  },
  {
    "id": "webhooks/event-types-content-devices#so-lesen-sie-die-tabellen",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "So lesen Sie die Tabellen",
    "anchor": "so-lesen-sie-die-tabellen",
    "text": "Ereignistyp ist die Zeichenkette, die Sie beim Endpunkt abonnieren. Sie kommt bei jeder Zustellung in der Kopfzeile X-Event-Type an. Statt einzelner Typen dürfen Sie auch einen ganzen Bereich abonnieren ( composition.* ) oder mit * alles. Mehr Formen gibt es nicht, und alles andere weist der Server ab, siehe Den Ereigniskatalog über die API abrufen . Nutzlast-Kern nennt zwei bis vier der wichtigsten Felder, die im Rumpf der Zustellung stehen. Es ist ein Auszug. Die vollständige Form bestimmt der jeweilige Bereich, und sie kann weitere Felder tragen. Zeitpunkt, Ereigniskennung und Auslöser reisen nicht im Rumpf. Wann es entsteht ist ein Halbsatz zur Einordnung, keine Aussage über Häufigkeit. Das Portal bietet unter Abonnierte Ereignisse genau die Typen dieser Seite und der Seite Ereignistypen: Organisation, Konten, Abrechnung zur Auswahl — dieselbe Liste, die GET /v1/webhooks/event-types liefert. Alles andere lässt sich nicht abonnieren, das Anlegen eines Endpunkts weist es ab. Welche Typen das sind, steht unter Was nie ankommt . Zugestellt wird nur, was im Kontext Ihrer Organisation entsteht. In den Feldern bezeichnet playerId ein Display (Kennung ply_… ) und deviceId das Gerät, an dem es hängt."
  },
  {
    "id": "webhooks/event-types-content-devices#die-ereignistypen",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Die Ereignistypen",
    "anchor": "die-ereignistypen",
    "text": ""
  },
  {
    "id": "webhooks/event-types-content-devices#medien",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Medien",
    "anchor": "medien",
    "text": "Dateien vom Upload bis zur fertigen Aufbereitung, Ordner und automatische Sammlungen. asset.ready schließt die Aufbereitung der Varianten ab, asset.deleted stößt die Prüfung der Szenen an, die die Datei nutzen. Ereignistyp Nutzlast-Kern Wann es entsteht media-library.asset.upload-initiated assetId , sha256 , sizeBytes , mimeType Ein Upload wurde begonnen. media-library.asset.upload-aborted assetId , abortedById Ein Upload wurde abgebrochen. media-library.asset.uploaded assetId , sha256 , sizeBytes , deduplicated Die Datei ist vollständig, und deduplicated sagt, ob sie schon vorhanden war. media-library.asset.processing-started assetId , tenantId Die Aufbereitung der Datei hat begonnen. media-library.asset.ready assetId , variantKinds Alle Varianten sind erzeugt, und die Datei ist einsatzbereit. media-library.asset.processing-failed assetId , reason Die Aufbereitung ist gescheitert. media-library.asset.updated assetId , updatedById , changedFields Angaben zu einer Datei wurden geändert. media-library.asset.deleted assetId , deletedById Eine Datei wurde gelöscht. Szenen, die sie nutzen, werden geprüft. media-library.asset.restored assetId , restoredById Eine gelöschte Datei wurde wiederhergestellt. media-library.folder.created folderId , path , createdById Ein Ordner wurde angelegt. media-library.folder.moved folderId , oldPath , newPath , affectedDescendants Ein Ordner wurde verschoben. media-library.folder.deleted folderId , path , cascadeAssetCount Ein Ordner wurde gelöscht,"
  },
  {
    "id": "webhooks/event-types-content-devices#szenen-und-vorlagen",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Szenen und Vorlagen",
    "anchor": "szenen-und-vorlagen",
    "text": "Szenen, ihre Versionen und Layout-Vorlagen. scene-version.created ist der Auslöser, an dem Wiedergabe und Programme nachziehen. Ereignistyp Nutzlast-Kern Wann es entsteht composition.scene.created sceneId , tenantId , createdById Eine Szene wurde angelegt. composition.scene.updated sceneId , version , updatedById Eine Szene wurde bearbeitet (jede Änderung). composition.scene.published sceneId , sceneVersionId , versionNumber , contentHash Eine Szene wurde veröffentlicht, contentHash kennzeichnet den Stand. composition.scene.archived sceneId , archivedById , reason Eine Szene wurde archiviert. composition.scene.restored sceneId , restoredById Eine archivierte Szene wurde zurückgeholt. composition.scene.deleted sceneId , deletedById Eine Szene wurde gelöscht. composition.scene-version.created sceneVersionId , versionNumber , contentHash , assetIds Eine neue Version einer Szene ist entstanden, assetIds nennt die verwendeten Dateien. composition.layout-template.created layoutTemplateId , createdById Eine Layout-Vorlage wurde angelegt. composition.layout-template.updated layoutTemplateId , updatedById Eine Layout-Vorlage wurde geändert. composition.layout-template.deleted layoutTemplateId , deletedById Eine Layout-Vorlage wurde gelöscht. composition.integrity-issue.detected issueId , sceneId , type , severity Ein Problem in einer Szene wurde erkannt, etwa eine fehlende Datei. composition.integrity-issue.resolved issueId , sceneId , resolution Ein erkanntes Problem wurde behoben."
  },
  {
    "id": "webhooks/event-types-content-devices#playlisten-und-programme",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Playlisten und Programme",
    "anchor": "playlisten-und-programme",
    "text": "Playlisten, Programme, die Zuweisung an Displays und die Planfassungen. schedule-item.changed und player-program.changed sind bewusst je ein Sammel-Ereignis mit action -Feld. timeline.invalidated nennt in reason den Anlass: timeline-invalidated oder media-source-changed (die Medien-Auswahl einer automatischen Playlist hat sich verschoben). plan-revision.recorded hält den Plan eines Displays für die nächsten 48 Stunden fest. Aus ihm rechnen Auswertung und Werbung die Planzeit und das Soll kommender Stunden. Ereignistyp Nutzlast-Kern Wann es entsteht scheduling.playlist.created playlistId , itemCount , assetIds , createdById Eine Playlist wurde angelegt. scheduling.playlist.updated playlistId , version , assetIds , updatedById Eine Playlist wurde geändert, assetIds nennt die enthaltenen Dateien. scheduling.playlist.deleted playlistId , deletedById Eine Playlist wurde gelöscht. scheduling.schedule.created scheduleId , tenantId , createdById Ein Programm wurde angelegt. scheduling.schedule.updated scheduleId , version , changedFields , updatedById Ein Programm wurde geändert. scheduling.schedule.deleted scheduleId , deletedById Ein Programm wurde gelöscht. scheduling.schedule-item.changed scheduleId , itemId , action ( created / updated / deleted ) Ein Eintrag eines Programms wurde angelegt, geändert oder gelöscht — ein Sammel-Ereignis mit action . scheduling.player-program.changed playerId , action ( assigned / cleared / override-set / override-cleared ) Die Zuweisung an einem D"
  },
  {
    "id": "webhooks/event-types-content-devices#marktplatz-installierte-apps",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Marktplatz: installierte Apps",
    "anchor": "marktplatz-installierte-apps",
    "text": "Apps aus Sicht Ihrer Organisation: Installation, Aktualisierung, Zustimmung und Ablehnung, die Bitten an die Inhaber, die Umstellung am Ende einer Auslauffrist, die Deinstallation und die Löschung der App-Daten danach. Ereignistyp Nutzlast-Kern Wann es entsteht marketplace.app.installed grantId , appId , version , acceptedById Eine App wurde installiert. marketplace.app.install-requested appId , version , requestedById , message Ein Manager bittet die Inhaber, eine App zu installieren. marketplace.app.upgraded grantId , appId , oldVersion , newVersion Eine installierte App wurde auf eine neue Version gehoben: still, nach Ihrer Zustimmung oder am Ende einer Auslauffrist. marketplace.app.pending-reacceptance grantId , appId , newManifestHash , addedPermissionKeys Eine neue Version verlangt neue Rechte, Zugriffe oder Bedingungen, oder die installierte Version wird ausgemustert, und die Zustimmung steht aus. marketplace.app.update-decision-requested appId , fromVersion , toVersion , requestedById , message Ein Manager bittet die Inhaber, über das wartende Update einer App zu entscheiden. marketplace.app.reaccepted grantId , appId , manifestHash , acceptedById Ein Update, das auf Zustimmung wartete, wurde angenommen. marketplace.app.update-declined grantId , appId , installedVersion , declinedVersion Ein Update wurde abgelehnt. Die App bleibt auf installedVersion , bis eine höhere Version als declinedVersion erscheint. marketplace.app.update-withdrawn grantId , appId , installedVe"
  },
  {
    "id": "webhooks/event-types-content-devices#flotte-geräte-displays-standorte-leinwände",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Flotte: Geräte, Displays, Standorte, Leinwände",
    "anchor": "flotte-geräte-displays-standorte-leinwände",
    "text": "Geräte, Displays, Befehle, Schlagwörter, Leinwände, Standorte und Systemaktualisierungen. Es gibt kein Ereignis je Lebenszeichen — player.status-changed ist die Spur für online und offline. Die display-group.* -Ereignisse sind die Leinwände, ihr memberPlayerIds nennt die betroffenen Displays. Die ota-* -Ereignisse tragen die Geräte-Kennung ( deviceId ), weil ein Systemstand am Gerät hängt, nicht am einzelnen Display. Ereignistyp Nutzlast-Kern Wann es entsteht fleet.device.claimed deviceId , tenantId , claimedByUserId , playerIds Ein Gerät wurde der Organisation zugeordnet, playerIds nennt die angelegten Displays. fleet.device.capabilities-changed deviceId , playerIds , capabilities Die gemeldeten Fähigkeiten eines Geräts haben sich geändert. fleet.device.channel-changed deviceId , channel , previousChannel , changedById Der Kanal eines Geräts wurde gesetzt, und es folgt ab jetzt den Freigaben des neuen Kanals. fleet.player.created playerId , deviceId , screenIndex Ein Display wurde angelegt. fleet.player.updated playerId , changedFields Angaben zu einem Display wurden geändert. fleet.player.config-updated playerId , changedConfig Die Konfiguration eines Displays wurde geändert. fleet.player.tagged playerId , tagId Ein Display bekam ein Schlagwort. fleet.player.untagged playerId , tagId Ein Schlagwort wurde vom Display entfernt. fleet.player.status-changed playerId , from , to Der Status eines Displays hat sich geändert ( from → to ) — die Spur für online und offline. fleet.pl"
  },
  {
    "id": "webhooks/event-types-content-devices#app-laufzeit",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "App-Laufzeit",
    "anchor": "app-laufzeit",
    "text": "Datenabrufe und wiederkehrende Jobs installierter Apps. fetch.completed ist der Auslöser, an dem die Wiedergabe neue App-Daten übernimmt. Ereignistyp Nutzlast-Kern Wann es entsteht app-runtime.fetch.completed tenantId , appId , fetchKey , payloadHash Ein Datenabruf einer App ist abgeschlossen. app-runtime.fetch.failed tenantId , appId , fetchKey , error Ein Datenabruf einer App ist gescheitert. app-runtime.cache.invalidated tenantId , appId , fetchKey Zwischengespeicherte App-Daten wurden verworfen. app-runtime.job.scheduled scheduleId , appId , jobKey , cron Ein wiederkehrender App-Job wurde eingeplant. app-runtime.job.executed scheduleId , appId , jobKey , durationMs Ein App-Job wurde ausgeführt. app-runtime.job.failed scheduleId , appId , jobKey , error Ein App-Job ist gescheitert."
  },
  {
    "id": "webhooks/event-types-content-devices#wiedergabe",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Wiedergabe",
    "anchor": "wiedergabe",
    "text": "Der berechnete Wiedergabestand je Display und die Meldungen der Wiedergabe-Software. Protokolliert wird hier nur, was schiefgeht. Die übrigen Ereignisse sind Maschinenverkehr, dessen Menge mit der Zahl der Displays wächst. Ereignistyp Nutzlast-Kern Wann es entsteht playback.snapshot.recomputed playerId , manifestHash , bundleHashes , recomputeReason Der Wiedergabestand eines Displays wurde neu berechnet. playback.snapshot.bundle-changed playerId , bundleKind , newHash , oldHash Ein Bestandteil des Wiedergabestands hat sich geändert. playback.snapshot.stale-detected playerId , validUntil , ageSeconds Der Wiedergabestand eines Displays ist veraltet. playback.snapshot.partial-failure playerId , degradedWidgets Teile des Wiedergabestands konnten nicht erzeugt werden, degradedWidgets nennt sie. playback.snapshot.playlist-item-skipped playerId , skippedItems Einträge einer Playlist wurden übersprungen. playback.snapshot.recompute-failed playerId , recomputeReason , error Die Neuberechnung des Wiedergabestands ist gescheitert. playback.renderer.diagnostics-ingested playerId , errorCount , severityCounts Ein Display hat Diagnosedaten geliefert."
  },
  {
    "id": "webhooks/event-types-content-devices#auswertung",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Auswertung",
    "anchor": "auswertung",
    "text": "Verdichtete Messwerte, Kennzahlen, Berichte, Nachweise und die Verweise für Werbepartner. analytics.proof.issued erreicht Ihren Endpunkt für jeden Nachweis Ihres Direktgeschäfts: für Abschlüsse und Nachträge aus dem nächtlichen Lauf ebenso wie für einen Zwischenstand, den Ihre Organisation anfordert. Nachweise des Werbenetzes gehören keiner Organisation und erreichen keinen Endpunkt. Ereignistyp Nutzlast-Kern Wann es entsteht analytics.metric.aggregated metric , timeBucket , bucketStart , entryCount Messwerte wurden zu einem Zeitfenster verdichtet. analytics.engagement-snapshot.recomputed tenantId , computedAt Die Kennzahlen der Organisation wurden neu berechnet. analytics.report.created reportId , tenantId , ownerUserId Ein Bericht wurde angelegt. analytics.report.updated reportId , changedFields Ein Bericht wurde geändert. analytics.report.deleted reportId , tenantId Ein Bericht wurde gelöscht. analytics.report.scheduled-run-triggered reportId , nextRunAt Ein geplanter Berichtslauf wurde angestoßen. analytics.report.generated generatedReportId , format , rowCount , trigger , window , expiresAt Ein Bericht wurde erzeugt — von Hand, nach Plan oder als einmalige Ausgabe ( trigger ). window nennt die festen Tage, expiresAt das Ende der Abrufbarkeit. analytics.report.generation-failed reportId , error Die Erzeugung eines Berichts ist gescheitert. analytics.retention.archived metric , bucketStart , entryCount Alte Messwerte wurden archiviert. analytics.proof.issued proofDocumentI"
  },
  {
    "id": "webhooks/event-types-content-devices#werbung",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Werbung",
    "anchor": "werbung",
    "text": "Werbepartner, Werbekampagnen mit ihren Buchungen, Motive und ihre Freigabe, die Werbefläche der Displays, das Publikum der Standorte, die Teilnahme am Werbenetz und die Gutschrift über die Werbeerlöse eines Monats. Die Ereignisse sind schmal, den Stand dazu lesen Sie unter /v1/advertising/… nach. Die Ereignisse zu Werbepartnern, Werbekampagnen, Buchungen und Motiven nennen in ownerKind und tenantId , wem die Zeile gehört. Zugestellt werden nur die Zeilen Ihrer Organisation ( ownerKind: tenant ). Werbepartner, Werbekampagnen, Buchungen und Motive des Werbenetzes tragen keine Organisation und erreichen keinen Endpunkt. Ereignistyp Nutzlast-Kern Wann es entsteht advertising.advertiser.created advertiserId , ownerKind , tenantId Ein Werbepartner wurde angelegt. advertising.advertiser.updated advertiserId , ownerKind , tenantId Name, Marken, Branche, Ansprechpartner oder Notiz eines Werbepartners wurden geändert. advertising.advertiser.archived advertiserId , ownerKind , tenantId Ein Werbepartner wurde archiviert. Er bekommt keine neuen Werbekampagnen, Buchungen und Motive, seine Werbekampagnen und Nachweise bleiben lesbar. Was gebucht ist, läuft weiter. advertising.campaign.created campaignId , advertiserId , status Eine Werbekampagne wurde angelegt, status ist draft oder booked . advertising.campaign.updated campaignId , advertiserId , status Name, Zeitraum, Ziel, Preis oder Notiz einer Werbekampagne wurden geändert. Ein Wechsel des Zustands hat sein eigenes Ereignis. advertisin"
  },
  {
    "id": "webhooks/event-types-content-devices#was-nie-ankommt",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Was nie ankommt",
    "anchor": "was-nie-ankommt",
    "text": "Der Katalog unter GET /v1/webhooks/event-types führt 241 der 338 Ereignistypen der Plattform, in 16 Bereichen — genau die Typen dieser Seite und der Seite Ereignistypen: Organisation, Konten, Abrechnung . Die übrigen 97 sind nicht abonnierbar: die Ereignisse der Zustellung selbst und alles, was ohne Bezug zu einer Organisation entsteht. Das Anlegen und Ändern eines Endpunkts weist solche Typen mit 422 WebhookEndpoint.EventUnknown ab, und ein Bereichs-Abo wie ops.* gibt es für diese Bereiche nicht. Das sind: Die Ereignisse der Zustellung selbst. Die zwölf Typen unter notifications.* — In-App-Nachrichten, E-Mail-Versand und die Webhook-Verwaltung ( notifications.webhook.endpoint.created , .endpoint.disabled , .webhook.delivered , .failed , .dead ) — lösen nie einen Webhook aus, sonst löste jede Zustellung die nächste aus. Die Abschaltung eines Endpunkts sehen Sie im Portal am Status Inaktiv , nicht per Webhook. Ereignisse des Plattformbetriebs. Die 16 Typen unter ops.* (Wartungsfenster, Störungen, Systemzustand, Gerätesoftware) entstehen ohne Bezug zu einer Organisation. Der App-Katalog. Die acht Typen unter app-catalog.* (Fassungen, Pakete und Status der Apps im Katalog): Eine Fassung wird einmal für die Plattform veröffentlicht, nicht je Organisation. Was daraus bei Ihnen folgt, melden die marketplace.app.* -Ereignisse. Geräte, bevor sie einer Organisation gehören, und die Technik der Verbindung. fleet.pairing.code-issued und fleet.pairing.code-rotated (Kopplungscodes entsteh"
  },
  {
    "id": "webhooks/event-types-content-devices#womit-sie-anfangen-können",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Womit Sie anfangen können",
    "anchor": "womit-sie-anfangen-können",
    "text": "Vier Abonnements, mit denen sich die meisten Anbindungen prüfen lassen, weil Sie sie im Portal selbst auslösen: composition.scene.published (eine Szene veröffentlichen), scheduling.playlist.updated (eine Playlist ändern), media-library.asset.ready (eine Datei hochladen) und fleet.player.status-changed (ein Display vom Netz trennen)."
  },
  {
    "id": "webhooks/event-types-content-devices#weiterführendes",
    "path": "/de/webhooks/event-types-content-devices",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "event-types-content-devices",
    "pageTitle": "Ereignistypen: Inhalte, Programme, Geräte, Apps",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Ereignistypen: Organisation, Konten, Abrechnung Einen Webhook-Endpunkt anlegen"
  },
  {
    "id": "webhooks/fetch-event-catalog",
    "path": "/de/webhooks/fetch-event-catalog",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "fetch-event-catalog",
    "pageTitle": "Den Ereigniskatalog über die API abrufen",
    "section": "Den Ereigniskatalog über die API abrufen",
    "anchor": "",
    "text": "Der Katalog der abonnierbaren Ereignisse steht nicht nur im Portal: GET /v1/webhooks/event-types liefert ihn als JSON, mit Klartext-Namen, Beschreibung und einem vollständigen Beispiel-Rumpf je Ereignis. Damit prüfen Sie eine Ereignis-Liste, bevor der Server sie abweist, und bauen eine eigene Auswahl, ohne Namen abzutippen."
  },
  {
    "id": "webhooks/fetch-event-catalog#der-aufruf",
    "path": "/de/webhooks/fetch-event-catalog",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "fetch-event-catalog",
    "pageTitle": "Den Ereigniskatalog über die API abrufen",
    "section": "Der Aufruf",
    "anchor": "der-aufruf",
    "text": "Parameter Werte Vorgabe locale de , en de Nur diese beiden Sprachen: Es sind die, in denen die Klartexte vollständig vorliegen. Ein voller Sprachcode verspräche mehr, als es gibt."
  },
  {
    "id": "webhooks/fetch-event-catalog#was-zurückkommt",
    "path": "/de/webhooks/fetch-event-catalog",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "fetch-event-catalog",
    "pageTitle": "Den Ereigniskatalog über die API abrufen",
    "section": "Was zurückkommt",
    "anchor": "was-zurückkommt",
    "text": "Feld Bedeutung items[].type die Zeichenkette, die Sie abonnieren; sie kommt bei jeder Zustellung in X-Event-Type an items[].area der Bereich, also das erste Segment vor dem Punkt — der Wert, auf den ein Bereichs-Abo zeigt items[].label , .areaLabel , .description Klartext für Ihre eigene Oberfläche, in der Sprache aus locale items[].exampleBody der vollständige Rumpf einer Zustellung, nicht ein Ausschnitt; null , wo sich aus dem Vertrag kein Beispiel ableiten lässt — erfunden wird keines areas die Bereiche, in denen es abonnierbare Ereignisse gibt, mit ihrem Klartext headers die Namen der drei Kopfzeilen, die jede Zustellung trägt Der Rumpf einer Zustellung ist die Nutzlast des Ereignisses selbst und nichts darum herum: Ereignisart und Zustell-Kennung stehen in den Kopfzeilen, nicht im Rumpf. Damit ist exampleBody genau das, was Ihr Empfänger sehen wird."
  },
  {
    "id": "webhooks/fetch-event-catalog#wofür-sie-ihn-brauchen",
    "path": "/de/webhooks/fetch-event-catalog",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "fetch-event-catalog",
    "pageTitle": "Den Ereigniskatalog über die API abrufen",
    "section": "Wofür Sie ihn brauchen",
    "anchor": "wofür-sie-ihn-brauchen",
    "text": "Prüfen, bevor der Server abweist. Beim Anlegen und Ändern eines Endpunkts prüft die Plattform jeden Eintrag der Liste events . Erlaubt sind genau drei Formen: Form Bedeutung composition.scene.published genau diese Ereignisart composition.* jede Art des Bereichs composition — der Bereich muss in areas stehen * alles, was abonnierbar ist Alles andere ist 422 WebhookEndpoint.EventUnknown , und die Antwort nennt unter details.unknownEvents die Namen, die sie nicht kennt (bis zu zwanzig). Ein Tippfehler soll bei der Eingabe auffallen und nicht als Endpunkt enden, der jahrelang stumm bleibt, ohne Fehlversuch und ohne Eintrag im Zustellprotokoll. Auswählen ohne Portal. Wer eine eigene Oberfläche baut, holt sich Namen, Bereiche und Beschreibungen von hier, statt sie zu pflegen. Sie altern damit nicht. Ein Beispiel für den Test. exampleBody ist ein gültiger Rumpf: Damit füttern Sie Ihren Empfänger, bevor das Ereignis zum ersten Mal wirklich eintritt."
  },
  {
    "id": "webhooks/fetch-event-catalog#stabilität",
    "path": "/de/webhooks/fetch-event-catalog",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "fetch-event-catalog",
    "pageTitle": "Den Ereigniskatalog über die API abrufen",
    "section": "Stabilität",
    "anchor": "stabilität",
    "text": "Die Operation trägt x-stability: beta . Die Form dieser Liste darf sich noch bewegen, bis sie einen Zyklus ohne Bruch hinter sich hat; die Namen der Ereignisse selbst sind davon nicht betroffen. Was beta zusagt und was nicht, steht unter Stabilitätsklassen und Auslauf ."
  },
  {
    "id": "webhooks/fetch-event-catalog#wenn-etwas-nicht-klappt",
    "path": "/de/webhooks/fetch-event-catalog",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "fetch-event-catalog",
    "pageTitle": "Den Ereigniskatalog über die API abrufen",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung 403 auf den Aufruf Der Ausweis ist ein API-Schlüssel; die Webhook-Rechte sind Sitzungen vorbehalten. Mit einem Konto anmelden, siehe Anmeldung . 422 WebhookEndpoint.EventUnknown Ein Eintrag in events passt in keine der drei Formen. Den genannten Namen gegen items[].type und areas[].area prüfen. Ein erwartetes Ereignis fehlt im Katalog und lässt sich nicht abonnieren Der Katalog führt nur, was im Kontext Ihrer Organisation entsteht. Siehe Was nie ankommt . WebhookEndpoint.EventUnknown steht mit den übrigen Webhook-Codes unter Fehlercodes: Geräte, Wiedergabe, Meldungen, Betrieb ."
  },
  {
    "id": "webhooks/fetch-event-catalog#weiterführendes",
    "path": "/de/webhooks/fetch-event-catalog",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "fetch-event-catalog",
    "pageTitle": "Den Ereigniskatalog über die API abrufen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Einen Webhook-Endpunkt anlegen Ereignistypen: Organisation, Konten, Abrechnung Ereignistypen: Inhalte, Programme, Geräte, Apps"
  },
  {
    "id": "webhooks/troubleshooting",
    "path": "/de/webhooks/troubleshooting",
    "area": "webhooks",
    "areaTitle": "Webhooks",
    "page": "troubleshooting",
    "pageTitle": "Wenn keine Zustellung ankommt",
    "section": "Wenn keine Zustellung ankommt",
    "anchor": "",
    "text": "Die häufigsten Gründe, warum ein Webhook nicht oder doppelt ankommt oder ein Endpunkt sich nicht anlegen lässt — in der Reihenfolge, in der sie auftreten. Symptom Bedeutung Lösung Der Endpunkt ist Aktiv , das Zustell-Log bleibt leer Das abonnierte Ereignis tritt in Ihrer Organisation nicht ein, oder das Abonnement wurde erst vor Kurzem ergänzt — ein geändertes Abonnement greift bis zu eine Stunde später. Einen falsch geschriebenen oder nicht abonnierbaren Typ nimmt das Anlegen gar nicht erst an ( 422 WebhookEndpoint.EventUnknown ) Typ wörtlich mit den Tabellen abgleichen: Organisation, Konten, Abrechnung und Inhalte, Programme, Geräte, Apps ; Ereignis im Portal auslösen und das Log erneut öffnen Der Endpunkt steht auf Inaktiv mit „letzter Fehler …“ Mehr als 20 Fehlschläge in Folge haben den Endpunkt abgeschaltet; er bekommt keine neuen Zustellungen Ursache beim Empfänger beheben, dann Aktivieren (oder PATCH mit {\"active\": true} ); siehe Abschaltung eines Endpunkts Eine Zustellung ist dead , obwohl nur ein Versuch gezählt wurde Ihr Server hat mit 4xx (außer 408 und 429) geantwortet; das gilt als endgültiger Fehler ohne Wiederholung Bei vorübergehenden Problemen mit 503 oder 429 antworten, nicht mit 4xx; die betroffene Zustellung über POST /v1/webhooks/{id}/deliveries/{deliveryId}/retry nachholen (das Portal zeigt Erneut zustellen nur bei Fehlgeschlagen ); siehe Die erwartete Antwort Zustellungen scheitern ohne Antwortcode, responseCode ist null Ihr Server hat nicht binnen 10 S"
  },
  {
    "id": "apps-sdk/index",
    "path": "/de/apps-sdk",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "index",
    "pageTitle": "Apps-SDK",
    "section": "Apps-SDK",
    "anchor": "",
    "text": "Wie eine App gebaut ist — Manifest, Widget, Server-Teil, Datenzugriff, Signatur — und wie sie in den Katalog und auf den Bildschirm kommt."
  },
  {
    "id": "apps-sdk/index#eine-app-bauen",
    "path": "/de/apps-sdk",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "index",
    "pageTitle": "Apps-SDK",
    "section": "Eine App bauen",
    "anchor": "eine-app-bauen",
    "text": "Seite Wann Sie es brauchen Von der Vorlage bis zur Freigabe Der ganze Weg einer neuen App: mit pnpm app:new aus der Vorlage anlegen, Widget, Config, Texte und Bilder schreiben, mit pnpm app:check prüfen, testen und über den Pull Request in den Katalog bis zur Freigabe in Stabil. Eine App im neuen Standard Eine App auf Vorlagen mit Größe, Formate, Passt-Bericht, Flächen und das deklarative Formular umstellen, mit der Wetter-App als Beispiel — dazu Passt-Prüfung, Store-Bilder, alte Configs, Host-Vertrag und die Reihenfolge der nächsten Apps."
  },
  {
    "id": "apps-sdk/index#grundlagen",
    "path": "/de/apps-sdk",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "index",
    "pageTitle": "Apps-SDK",
    "section": "Grundlagen",
    "anchor": "grundlagen",
    "text": "Seite Wann Sie es brauchen Was ist eine App, und wie kommt sie auf den Bildschirm? Vom Manifest über Release, Kanäle und Installation bis zum signierten Bündel auf dem Gerät: der Lebensweg einer App in einem Bild. Das Manifest Jedes Feld des App-Manifests mit Pflicht, Format und Wirkung — von id und version bis legal, unlocks und connects. Widget, Konfiguration und Vorschau defineWidget mit configSchema und noResolver, Formate (useWidgetFormat), Vorschau-Varianten mit Größe für Katalog und Miniaturen sowie Vorschau-Zustände — mit dem Uhr-Beispiel. Widgets konfigurieren Den Widget-Katalog lesen: configSchema als JSON-Schema, Pflichtfelder, Beispiele — und je eingebautem Baustein eine fertige Widget-Instanz."
  },
  {
    "id": "apps-sdk/index#server-teil-und-daten",
    "path": "/de/apps-sdk",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "index",
    "pageTitle": "Apps-SDK",
    "section": "Server-Teil und Daten",
    "anchor": "server-teil-und-daten",
    "text": "Seite Wann Sie es brauchen Server-Teil: Resolver, Fetcher und Jobs Die drei Decorator @WidgetResolver, @Fetcher und @JobHandler, ihre Regeln und die Fähigkeiten, die der Server-Teil einer App über Token bezieht. Datenzugriff, Rechte und Bedingungen dataAccess (reads, external, retention), Rechte für Team und Betreiber, unlocks und connects, der legal-Block: was eine App vor der Installation offenlegt."
  },
  {
    "id": "apps-sdk/index#veröffentlichen",
    "path": "/de/apps-sdk",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "index",
    "pageTitle": "Apps-SDK",
    "section": "Veröffentlichen",
    "anchor": "veröffentlichen",
    "text": "Seite Wann Sie es brauchen Signieren und der Weg in den Katalog Was an einem Release liegt, sha256 und Ed25519-Signatur, die Politik am Gerät, Kanäle canary → beta → stable — und wer heute Releases anlegt. Die Beispiel-Apps Alle 26 mitgelieferten Apps mit Profil und Besonderheit — und die drei Referenzen clock (nur Widget), weather (Fetcher), pharmacies-on-duty (Ressourcen und Job)."
  },
  {
    "id": "apps-sdk/template-to-release",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "Von der Vorlage bis zur Freigabe",
    "anchor": "",
    "text": "Nach dieser Anleitung steht Ihre App als Fassung im Kanal Stabil und damit im Store jeder Organisation, deren Paket sie enthält. Der Weg beginnt mit einem Befehl, der die App aus einer Vorlage anlegt, und endet mit der Bestätigung einer zweiten Person im Ops-Portal. Dazwischen sagt ein zweiter Befehl jederzeit, was noch fehlt."
  },
  {
    "id": "apps-sdk/template-to-release#der-weg-in-einem-bild",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "Der Weg in einem Bild",
    "anchor": "der-weg-in-einem-bild",
    "text": "Die Wörter dieser Seite folgen dem Glossar: Die Fassung ist die Nummer, die eine Organisation liest ( 1.0.0 ), das Release ist dieselbe Fassung im Katalog des Betreibers, und der Kanal sagt, wer sie bekommt."
  },
  {
    "id": "apps-sdk/template-to-release#1-die-app-anlegen",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "1. Die App anlegen",
    "anchor": "1-die-app-anlegen",
    "text": "pnpm app:new <id> legt apps/<id>/ aus der Vorlage an und schreibt sonst nichts: keine Liste, keinen Eintrag in einer Datei der Plattform. Plattform, Katalog, Aufnahmen und Budgets finden die App über ihren Ordner. Die Kennung ist kebab-case und englisch ( door-sign , nicht tuerschild ). Sie gilt für den Ordner, das Paket @szenera-app/<id> , das Manifest, das eine Widget der Vorlage und das Datenbank-Schema apps_<id> (mit Unterstrich statt Bindestrich). Eine vergebene, eine reservierte ( core ) oder eine falsch geformte Kennung lehnt der Befehl ab, mit jedem Grund auf einmal, und legt dann nichts an. Die Namen unter --name (deutsch) und --name-en (englisch) stehen im Manifest. Ohne sie nimmt der Befehl vorerst einen Namen aus der Kennung, die Texte schreiben Sie ohnehin neu. pnpm install nimmt die App in den Arbeitsbereich auf. Danach steht die Lockdatei kompakt da: pnpm exec prettier --write pnpm-lock.yaml bringt sie in die Form des Baums, und pnpm lizenzhinweise schreibt die Hinweise Dritter mit dem neuen Stand der Lockdatei. pnpm --filter @szenera-app/<id> build baut die App. Der Seed des Katalogs liest ihr gebautes Manifest und überspringt sie bis dahin mit Hinweis. Was die Vorlage mitbringt: Teil Dateien Was darin steht Manifest src/manifest/index.ts Kennung und Fassung, Texte auf Deutsch und unter translations auf Englisch, Kategorie, Klasse, Store-Block, compatibility (Wirte, Lagen, Verhalten ohne Netz) und apiCompatibility.rendererHost aus RENDERER_HOST_RANGE Widget sr"
  },
  {
    "id": "apps-sdk/template-to-release#2-widget-config-und-formular-schreiben",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "2. Widget, Config und Formular schreiben",
    "anchor": "2-widget-config-und-formular-schreiben",
    "text": "config.ts ist der Werte-Vertrag des Widgets. Jedes Feld trägt .default() , damit eine leere Config vollständig liest, und das Schema verwirft unbekannte Felder still (kein .strict() ), damit eine ältere Fassung die Config einer neueren liest. Für jede spätere Fassung gilt dieselbe Regel: Felder kommen nur hinzu, mit Vorgabe, und keines verschwindet oder ändert seinen Typ. Die Grundlinie hält das fest (Schritt 6). .describe('Beschriftung|Satz') trägt Beschriftung und Hilfetext eines Felds. Das Formular in config-ui.ts liest beides mit schemaFieldText() aus @szenera/widget-editor/config , so heißt ein Feld überall gleich. Welche Steuerelemente der Editor kennt, sagt die Feldart: Feldart ( kind ) Steuerelement im Editor Wert in der Config text , textarea eine Zeile, mehrere Zeilen Text number , slider Zahl, Schieberegler Zahl select , segmented , checkboxGroup Auswahl, Umschalter, Kästchen ein Wert, mehrere Werte aus options toggle Schalter ja oder nein color Farbwähler #rrggbb date Kalender isoDate() : JJJJ-MM-TT , leer erlaubt time Uhrzeit timeOfDay() : HH:mm dateTime Kalender, Uhrzeit und die Zone der Angabe isoDateTime() : Zeitpunkt mit Versatz timezone Suche über alle Zonen mit deutschen und englischen Namen timezoneName() : der Name der Zone ( Europe/Berlin ), leer für die Zone der Organisation address Straße, PLZ, Ort und Land als ein Feld postalAddress() location Standort des Displays oder fester Ort aus der Stadtsuche, auf Wunsch mit Ersatzort placeSelection() : mode , "
  },
  {
    "id": "apps-sdk/template-to-release#3-die-texte-schreiben",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "3. Die Texte schreiben",
    "anchor": "3-die-texte-schreiben",
    "text": "Die Texte stehen auf Deutsch im Manifest, jede weitere Sprache als Block unter translations , der nur Texte trägt. Englisch ist Pflicht, und defineAppManifest lehnt einen unvollständigen Block mit dem Pfad des fehlenden Texts ab. Dieselben Regeln gelten für jeden Text, den eine Organisation liest, im Store, im Install-Dialog und im Editor: die Sie-Form, kein Semikolon, höchstens ein Gedankenstrich je Text, keine Wörter aus dem Inneren der Plattform und keine Branche in einer App für alle Branchen. Zwei Angaben ordnen die App ein: category bestimmt den Abschnitt im Store und in der Widget-Palette, tier die Klasse ( standard in jedem Paket, pro ab dem Paket Pro, addon eigens gebucht). Der Manifest-Test der App hält beide fest, damit eine verschobene Einordnung nicht still durchläuft. Eine Klasse außer standard braucht zudem ihren Eintrag in der Verkaufsliste EXPECTED_TIERS in services/api/tests/app-tier-classification-sweep.test.ts , etwa 'door-sign': 'pro' : Dort steht, was verkauft wird, und ein Test der Schnittstelle hält jede App dagegen. Fehlt der Eintrag, nennt ihn pnpm app:check in der Zeile Manifest . Eine App mit fremder Quelle oder Pflichtangaben trägt dazu einen Block legal ( Datenzugriff, Rechte und Bedingungen )."
  },
  {
    "id": "apps-sdk/template-to-release#4-symbol-und-aufnahmen",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "4. Symbol und Aufnahmen",
    "anchor": "4-symbol-und-aufnahmen",
    "text": "Das Symbol icon.svg zeichnen Sie selbst. Solange das Symbol der Vorlage mit seiner Marke data-placeholder dort liegt, bleibt die Zeile Store-Bilder rot. Die Aufnahmen entstehen mit dem Werkzeug, das auch die CI benutzt: Es nimmt jede Ansicht unter preview.variants auf, die Hauptansicht ein zweites Mal unter dem Namen aus thumbnailUrl und das Szenenbeispiel, die Hauptansicht in einer typischen Szene. Braucht eine Aufnahme mehr, eine Akzentfarbe der Umgebung oder eine feste Teil-Config, steht es in der package.json unter szenera.screenshots ."
  },
  {
    "id": "apps-sdk/template-to-release#5-mit-pnpm-appcheck-prüfen",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "5. Mit pnpm app:check prüfen",
    "anchor": "5-mit-pnpm-appcheck-prüfen",
    "text": "Der Befehl zeigt je Prüfung eine Zeile, mit jedem Befund darunter. Lokal und in der CI ist es derselbe Befehl, die Prüfung vor dem Bau fährt pnpm app:check --all über alle Apps. Zeile Was sie prüft Manifest Das Manifest lädt und erfüllt den Vertrag. Ordner, Paket und Manifest tragen dieselbe Kennung, Paket und Manifest dieselbe Fassung, jeder Teilpfad unter exports hat seine Quelle, das Schema heißt apps_<id> . Eine Klasse außer standard steht in der Verkaufsliste (Schritt 3). Übersetzungen Deutsch und Englisch sind da. Kompatibilität Wirte, Lagen und Verhalten ohne Netz stehen im Manifest. Store-Bilder Symbol, jede Ansicht, die Hauptansicht und das Szenenbeispiel liegen da, je ein PNG in den Maßen seiner Lage und nicht leer. Texte Jeder Text aus Manifest, Übersetzung und Formular folgt der Sprachregel, und jedes Feld heißt im Formular wie im Schema. Formular Jede Formular-Beschreibung in src/widgets/<ordner>/config-ui.ts hält die Regeln, die kein Typ ausdrückt ( widgetConfigUIIssues ): höchstens zwei Schnellfelder kompakter Art, jede Gruppe einmal und jedes Feld in einer, die es gibt, jede Fläche zur Wahl einmal. Zustände Jedes Widget, das Daten von außen holt, erklärt den Vorschau-Zustand empty ( { id: 'empty', mockData: null } oder die leere Nutzlast), damit das Display die ruhige Fläche zeigt. Eine Ausnahme bis zur eigenen Welle steht mit Begründung in tools/app-kit/src/check/state-exceptions.ts . Grundlinie Test und Grundlinie liegen da, die Config hält sie ein, und die "
  },
  {
    "id": "apps-sdk/template-to-release#6-testen",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "6. Testen",
    "anchor": "6-testen",
    "text": "Die Tests laufen gegen die Quellen, ohne Bau. tests/manifest.test.ts hält Vertrag, Kategorie, Bedingungen, Wirte und Registry, tests/compat.test.ts die Grundlinie und den Host-Bereich, die Tests unter tests/widgets/ Darstellung und Formular. Ändern Sie die Config, schreibt pnpm app-compat --update door-sign das Neue in die Grundlinie. Bricht die Änderung die Regel aus Schritt 2, schreibt der Befehl nichts, und der Test nennt das Feld."
  },
  {
    "id": "apps-sdk/template-to-release#7-der-pull-request",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "7. Der Pull Request",
    "anchor": "7-der-pull-request",
    "text": "Der PR trägt eine Notiz : pnpm changeset nennt die App, und eine Notiz für eine App braucht den Abschnitt ## Kunde mit dem Satz, den eine Organisation zu dieser Fassung liest. Ohne Notiz bekommt die App keine Fassung. Die Prüfung des PR fährt pnpm app:check --all mit der übrigen Statik, und in der vollen Tiefe baut, nimmt auf, packt und prüft sie jede berührte App so, wie der Lieferweg es tut. Signiert und abgelegt wird im PR nichts. Nach dem Merge hebt der Versions-PR die Fassung aus der Notiz, und der Zug setzt den Tag @szenera-app/<id>@<fassung> ."
  },
  {
    "id": "apps-sdk/template-to-release#8-vom-tag-in-den-katalog",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "8. Vom Tag in den Katalog",
    "anchor": "8-vom-tag-in-den-katalog",
    "text": "Zum Tag baut release-app.yml die Fassung genau einmal: Bau, Aufnahmen, das Artefakt <id>-<fassung>.szapp , die Prüfung, die Signatur, die Ablage in der Registry. Dann meldet der Lauf die Fassung Staging und Produktion, beide holen dasselbe Artefakt und legen das Release im Kanal Canary an. Dieselbe Fassung trägt damit überall dieselben Bytes. Was an einem Release liegt und wie die Kanäle wirken, steht unter Signieren und der Weg in den Katalog ."
  },
  {
    "id": "apps-sdk/template-to-release#9-prüfen-und-freigeben-im-ops-portal",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "9. Prüfen und freigeben im Ops-Portal",
    "anchor": "9-prüfen-und-freigeben-im-ops-portal",
    "text": "Öffnen Sie im Ops-Portal Apps , die Zeile der App und den Reiter Releases . Die neue Fassung steht in Canary , Prüfung öffnen führt zur Release-Prüfung. Die Prüfung zeigt links den Store-Eintrag, wie die Organisationen ihn sehen werden, rechts die Zustimmungsfolge, den Vergleich mit der Vorgängerin, „Neu in dieser Fassung“, die Verbreitung und die Prüfliste. Fehlt der Text für die Organisationen, tragen Sie ihn hier ein. Nach Beta hochstufen … dürfen Sie allein. In Beta bekommen die Fassung die Organisationen, die sich für Vorabfassungen eingetragen haben. Nach einigen Tagen in Beta: Nach Stabil hochstufen … mit einer Begründung. Das legt eine Anforderung an, die Fassung bleibt so lange in Beta. Eine zweite Person öffnet die Prüfung derselben Fassung und bestätigt. Erst damit steht die Fassung in Stabil , und alle Organisationen, deren Paket die App enthält, finden sie im Store."
  },
  {
    "id": "apps-sdk/template-to-release#ergebnis-prüfen",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "pnpm app:check <id> --built zeigt jede Zeile mit ✓ . Im Ops-Portal steht die Fassung im Reiter Releases in Stabil , und der Verlauf nennt bei der Freigabe beide Personen. Im Store einer Organisation mit passendem Paket steht die App unter ihrer Kategorie, mit Symbol, Aufnahmen und Szenenbeispiel aus dem Artefakt."
  },
  {
    "id": "apps-sdk/template-to-release#wenn-etwas-nicht-klappt",
    "path": "/de/apps-sdk/template-to-release",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "template-to-release",
    "pageTitle": "Von der Vorlage bis zur Freigabe",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Was Sie sehen Was dahintersteht und was hilft app:new legt nichts an Die Meldung nennt jeden Grund: eine vergebene oder reservierte Kennung, eine Form außerhalb von kebab-case, ein Name mit Anführungszeichen. Den Grund beheben und den Befehl wiederholen. Zeile Manifest : „das Manifest lässt sich nicht laden“ defineAppManifest hat abgelehnt, der Text danach nennt das Feld, eine unvollständige Übersetzung etwa mit ihrem Pfad. Zeile Manifest : „EXPECTED_TIERS … nennt … nicht“ Die App trägt eine Klasse außer standard , und die Verkaufsliste kennt sie nicht. Den Eintrag aus dem Befund in services/api/tests/app-tier-classification-sweep.test.ts übernehmen (Schritt 3). Zeile Notiz : „CHANGELOG.md nennt die Fassung … nicht“ Die Fassung in package.json ist gehoben, ohne dass der Versions-PR sie geschrieben hat. Fassungen hebt nur die Notiz über den Versions-PR, package.json und CHANGELOG.md ändern sich dann gemeinsam. Zeile Grundlinie : „nicht aktuell“ Die Config hat ein Feld, das die Grundlinie nicht kennt. pnpm app-compat --update <id> schreibt es fest. Zeile Grundlinie : „Das neue Feld hat keine Vorgabe“ Ein neues Feld ohne .default() . Eine Config einer früheren Fassung kennt es nicht und liest dann nicht mehr. Zeile Store-Bilder : „zu klein“ Ein Platzhalter der Vorlage oder ein leeres Bild. Die Aufnahme aus Schritt 4 ersetzt ihn. vite build bricht am Deckel Das Bündel ist größer als szenera.bundleBudgets . Erst schlanker bauen, sonst den Deckel mit Grund im Feld note heben. vite "
  },
  {
    "id": "apps-sdk/new-app-standard",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "Eine App im neuen Standard",
    "anchor": "",
    "text": "Ein Widget steht in einer Szene selten bildschirmfüllend: oft als kleine Kachel, hochkant am Rand oder als Leiste. Der Standard sorgt dafür, dass es in jedem Rahmen gut aussieht, dass der Kunde beim Einfügen eine passende Vorlage findet und dass das Einstellungs-Fenster zeigt, was später am Display passiert. Diese Seite führt eine bestehende App Schritt für Schritt dorthin, mit der Wetter-App als durchgehendem Beispiel."
  },
  {
    "id": "apps-sdk/new-app-standard#was-der-standard-verlangt",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "Was der Standard verlangt",
    "anchor": "was-der-standard-verlangt",
    "text": "Baustein Woran man ihn erkennt Schritt Vorlagen Jede Variante trägt eine Startgröße, genau eine ist primary . 1 Formate Die Anordnung folgt Form und Größe des Rahmens, nicht einer Einstellung. 2 Passt-Bericht Was eingeschaltet ist, aber nicht passt, meldet das Widget. 3 Flächen, Symbole, Bausteine Hintergrund aus den gemeinsamen Flächen, Symbole aus dem gemeinsamen Satz, Listen, Laufband, Code, Quelle und leerer Zustand aus den Anzeige-Bausteinen. 4 Formular Deklarativ beschrieben, mit festen Gruppen, Schnellfeldern und Texten aus dem Schema. 5 Eigene Felder Nur dort, wo keine Feldart passt, und dann als einzelnes Feld. 6 Vorschau-Zustände Regen, Nacht oder „keine Daten“ sind im Fenster umschaltbar, ein Widget mit Quelle hat den Zustand empty , und am Display steht nie Editor-Text. 7 Passt-Prüfung Jede Vorlage und jede Randgröße passt, lesbar und ungekürzt, jeder Zustand ist am Display ruhig. 8 Store-Bilder Jede Vorlage im echten Rahmen auf ruhiger Fläche. 9 Alte Configs Ein Seed-Schritt hebt den Bestand, das Schema kennt nur die neuen Felder. 10 Host-Vertrag Das Bündel verlangt die Zusage der Anzeige, die seine Importe decken, und geht erst nach ihr hinaus. 11"
  },
  {
    "id": "apps-sdk/new-app-standard#1-vorlagen-mit-größe-und-primary",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "1. Vorlagen mit Größe und primary",
    "anchor": "1-vorlagen-mit-größe-und-primary",
    "text": "Die Varianten unter preview.variants sind die Vorlagen beim Einfügen, die Bilder im Store und die Kacheln „Vorlage“ im Einstellungs-Fenster, alles aus einer Quelle. Jede trägt eine Startgröße in Bezugspixeln einer 1920 × 1080-Szene und nur den Teil der Config, der sie ausmacht. Genau eine ist primary : Ein Klick auf die App im Editor setzt sie ein. Die Größe ist ein Anfang, kein Käfig, der Kunde zieht den Rahmen danach, wie er will. Aus apps/weather/src/widgets/weather/definition.ts (gekürzt): Bewährt hat sich: eine Vorlage je Form, die die App beherrscht (Kachel, quer, hochkant, Leiste), und die Größe so wählen, dass in ihr alles passt, was sie einschaltet. size.min ist der kleinste Rahmen, in dem das Widget noch etwas Sinnvolles zeigt, jede Vorlage muss mindestens so groß sein. Gestaltungen, die sich nur im Inhalt unterscheiden, sind Vorlagen desselben Widgets, keine zweiten Widgets."
  },
  {
    "id": "apps-sdk/new-app-standard#2-die-anordnung-folgt-dem-rahmen",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "2. Die Anordnung folgt dem Rahmen",
    "anchor": "2-die-anordnung-folgt-dem-rahmen",
    "text": "useWidgetFormat() aus @szenera/widget-foundation/hooks nennt die Form des Rahmens ( band , wide , square , tall ), die Größenklasse ( compact unter 400 Bezugspixeln kurzer Seite, sonst regular ) und die Maße. Die App zeichnet je Form eine Anordnung. Gemessen wird nichts im DOM: Die Plattform kennt den Rahmen, die Anordnung steht deshalb schon beim ersten Zeichnen. Die Schwellen und die Rechnung stehen unter Formate . Die Wetter-App rechnet die Anordnung als reine Funktion ( runtime/layout.ts ) und ruft sie aus der Laufzeit auf. Aus apps/weather/src/widgets/weather/runtime.tsx (gekürzt): Drei Regeln, die jede Anordnung einhält: Alle Maße wachsen mit dem Rahmen. Gerechnet wird in Bezugspixeln, Schrift, Abstände und Eckenradius sind Anteile davon. So sieht dieselbe Vorlage in einer 4K-Szene aus wie in Full HD. Eine feste Wegfall-Folge. Was in einem Rahmen keinen Platz hat, fällt in einer Reihenfolge weg, die die App einmal festlegt. Beim Wetter bleiben Ort, Temperatur und Wetterlage immer, dann fallen Tage, Stunden, Höchst- und Tiefstwert. In der Spalte ( tall ) kommen die Stunden erst ab der 1,7-fachen Höhe dazu und fallen deshalb zuerst weg. Breiter Ziehen nimmt nie Inhalt weg. Bei gleicher Höhe blendet ein breiterer Rahmen nie mehr aus als ein schmalerer, auch nicht an der Grenze zweier Formen. Ein Test der Anordnung hält das fest. Die Anordnung als reine Funktion zu schreiben lohnt sich: Ihre Tests laufen ohne Browser und decken jede Form und jede Grenze ab."
  },
  {
    "id": "apps-sdk/new-app-standard#3-der-passt-bericht",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "3. Der Passt-Bericht",
    "anchor": "3-der-passt-bericht",
    "text": "Blendet das Widget einen eingeschalteten Inhalt aus, meldet es die Teile mit den Schlüsseln der Config-Felder, die sie einschalten. Der Editor sagt dann unter der Vorschau und am Schalter, warum ein Inhalt fehlt, statt dass er wie ein Fehler wirkt. Die Wetter-App gibt die Liste aus ihrer Anordnung weiter: Gemeldet wird bei jeder Anordnung die vollständige Liste, leer, sobald alles zu sehen ist, und immer, ohne zu fragen, wo das Widget läuft: Auf dem Display bleibt die Meldung ohne Wirkung."
  },
  {
    "id": "apps-sdk/new-app-standard#4-flächen-symbole-und-bausteine",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "4. Flächen, Symbole und Bausteine",
    "anchor": "4-flächen-symbole-und-bausteine",
    "text": "Der Hintergrund kommt aus den gemeinsamen Flächen in @szenera/widget-foundation/widget-ui/tokens : light , dark , glass und transparent ( WIDGET_SURFACE_IDS , aufgelöst mit resolveWidgetSurface(id) , das Hintergrund und Schriftfarbe liefert). Eine App darf eigene Flächen ergänzen, das Wetter hat condition , einen Verlauf je Wetterlage. Eigene Hex-Farben im Formular entfallen. Das Feld der Config ist eine Auswahl der Kennungen: Symbole kommen aus dem gefüllten Satz ConditionIcon in @szenera/widget-foundation/widget-ui (Sonne, heiter, Wolke, Regen, Schnee, Gewitter, Nebel, Mond), nicht aus eigenen Grafiken der App. Gestaltet wird schlicht: keine Karten in Karten, keine Rahmen um Kacheln, die Hausschrift Inter. Was mehrere Apps brauchen, baut keine App selbst nach. Die Anzeige-Bausteine in @szenera/widget-foundation/widget-ui tragen keinen eigenen Stil, sie nehmen Fläche, Schrift und Maße der App: PagedList , sobald eine Anzeige eine Folge gleichartiger Einträge zeigt, deren Zahl sie nicht bestimmt (Termine, Gerichte, Schlagzeilen). Die Liste blättert im festen Takt, statt die Schrift unter die Mindestgröße zu drücken. QuietState , immer wenn Daten fehlen. Am Display die Fläche und höchstens ein neutraler Satz, im Editor die Erklärung mit dem nächsten Schritt. CompanionQr , wann immer ein Code erscheint. Er hält Ruhezone, Kontrast und Modulgröße, die Beschriftung schiebt ihn nie aus seiner Ruhezone. MarqueeBand für eine einzelne Zeile in einer Leiste, die länger ist als die Leis"
  },
  {
    "id": "apps-sdk/new-app-standard#5-das-formular-deklarativ",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "5. Das Formular deklarativ",
    "anchor": "5-das-formular-deklarativ",
    "text": "Das Einstellungs-Fenster baut sein Formular aus einer Beschreibung ( defineWidgetConfigUI aus @szenera/apps-sdk/config ), die die App in ihrem Authoring-Bündel registriert. Die Gruppen folgen einer festen Folge, damit sich ein Kunde in jeder App zurechtfindet: Quelle, Inhalt, Aussehen, Erweitert (eingeklappt). Vorlage und Platzierung ergänzt der Editor selbst. Aus apps/weather/src/widgets/weather/config-ui.ts (gekürzt): Texte aus dem Schema. Beschriftung und Hilfetext stehen einmal in .describe('Beschriftung|Hilfetext') und kommen über schemaFieldText ins Formular. Ein Feld heißt im Editor so wie im Schema, das ein Assistent liest. Schnellfelder. quickFields nennt höchstens zwei kompakte Felder, die an der schwebenden Leiste über dem Widget stehen, beim Wetter der Hintergrund. Die Vorlage steht dort ohnehin. Ort. Die Feldart location gehört zu placeSelection('display') im Schema: „Standort des Displays“ oder „Fester Ort“, mit fallback: true dazu der Ersatzort für Displays, deren Standort keine bekannte Lage hat, im Ort-Feld unter „Standort des Displays“. Wer den Standort liest, erklärt den Lesebereich player-location und nennt ihn in seinen Bedingungen, siehe Datenzugriff, Rechte und Bedingungen . Fläche. Die Feldart surface zeigt Kacheln mit einem Farbmuster je Fläche, damit man sie erkennt, bevor man sie wählt. Sichtbarkeit. showWhen: { field, equals } (oder in , mit not: true umgekehrt) blendet ein Feld oder eine ganze Gruppe aus, solange es nichts bewirkt, etwa eine Farbe"
  },
  {
    "id": "apps-sdk/new-app-standard#6-eigene-felder",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "6. Eigene Felder",
    "anchor": "6-eigene-felder",
    "text": "Passt keine Feldart, registriert die App ein einzelnes Feld, nie ein ganzes Formular: So erreichen Verbesserungen des Fensters jede App, ohne dass sie nachzieht. Die Wetter-App bringt nur die Ortssuche selbst mit und hängt sie an den Pfad location.place . Aus apps/weather/src/authoring/index.ts : Das Feld bekommt Wert, Änderungs-Rückruf und den Formular-Kontext (Rahmen, Sprache, Displays der Organisation) als Eigenschaften und rendert nur sich selbst. Beschriftung, Gruppe und Hilfetext kommen weiter aus der Beschreibung."
  },
  {
    "id": "apps-sdk/new-app-standard#7-vorschau-zustände",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "7. Vorschau-Zustände",
    "anchor": "7-vorschau-zustände",
    "text": "preview.states sind benannte Beispiel-Nutzlasten, die das Fenster neben „Echte Daten“ zum Umschalten anbietet. So sieht man Regen, Nacht oder „keine Daten“, ohne auf das Wetter zu warten: Die Nutzlasten stehen neben den Demo-Daten der App und folgen demselben Schema wie die echte Nutzlast. Ein Zustand, den die Anzeige nicht kennt, fällt in ihren Tests auf, nicht erst beim Kunden. Ein Widget mit Quelle hat den Zustand empty. Er zeigt das Widget ohne Daten ( mockData: null ), pnpm app:check verlangt ihn. Beim Wetter: Nie Editor-Text am Display. Leer, gestört oder unkonfiguriert heißt am Display eine ruhige Fläche und höchstens ein neutraler Satz. Wer vor dem Bildschirm steht, kann nichts einstellen, und für den Laden wirkt eine Anleitung wie ein Fehler. Die Erklärung mit dem nächsten Schritt gehört in den Editor: Die App fragt useWidgetMode() oder nimmt den Baustein QuietState , der beides trennt. Das Wetter zeigt am Display ohne Daten nur „Wetterdaten vorübergehend nicht verfügbar“, im Editor dazu, ob ein Ort, ein Standort oder nur die Daten fehlen. Die Passt-Prüfung rendert jeden Zustand im Anzeige-Modus und meldet einen Satz an den Bearbeiter (Schritt 8 )."
  },
  {
    "id": "apps-sdk/new-app-standard#8-die-passt-prüfung",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "8. Die Passt-Prüfung",
    "anchor": "8-die-passt-prüfung",
    "text": "Die Passt-Prüfung rendert jede Vorlage in ihrem Rahmen und meldet, was übersteht, angeschnitten oder ganz verdeckt ist, Zeilen, die ein kürzender Kasten durch die Buchstaben schneidet, Texte, die einander überdecken, Schrift unter der Mindestgröße und was das Widget im eigenen Rahmen ausblendet. Sie steht als Gate im Pull Request. Für eine App im neuen Standard misst sie mehr als die Vorlagen: Der Randgrößen-Satz. Mit size.min bekommt jedes Widget die Rahmen, an denen Anordnungen brechen: die Mindestgröße, je Form den kleinsten erlaubten Rahmen und den größten in der Szene, die Leiste als volle Breite in Mindesthöhe, die Spalte als volle Höhe in Mindestbreite. Ausblenden ist dort erlaubt, abschneiden und zu kleine Schrift nicht. Beim Wetter (Mindestgröße 160 × 90) sind das acht Rahmen, darunter die Kachel 161 × 131, die Leiste 1920 × 90 und die Spalte 160 × 1080. Die Mindestschrift. Inhalt nie unter 12, Quellen- und Standzeilen nie unter 10 Bezugspixeln. Eine Anordnung, die Platz braucht, lässt Inhalt weg oder blättert, statt die Schrift zu drücken. Die Quellenzeile trägt data-fit-meta (der Baustein SourceCredit ist als ganzer Text markiert). Ganze Texte. Uhrzeit, Datum, Preis, Pflichtangaben und Telefon tragen data-fit-whole . Ein solcher Text wird nie gekürzt, weder mit „…“ vom Kasten noch von einem Skript. Die Zustände am Display. Jeder Vorschau-Zustand im Anzeige-Modus, empty und error in jeder Vorlage, ohne Satz an den Bearbeiter. Die Bilder eines Rahmens mit Befund lieg"
  },
  {
    "id": "apps-sdk/new-app-standard#9-store-bilder",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "9. Store-Bilder",
    "anchor": "9-store-bilder",
    "text": "Die Bilder im Store zeigen jede Vorlage im echten Rahmen auf einer ruhigen Fläche, Name und Maß darunter, kleine Vorlagen vergrößert. Sie entstehen mit demselben Renderer wie die Anzeige: Das Werkzeug schreibt apps/<id>/thumbnails/<widget>--<variante>.png je Vorlage, eine Kopie der primary -Vorlage als <widget>.png und das Szenenbeispiel. pnpm app:check prüft danach, ob jedes Bild da und nicht leer ist. Die Bilder gehören in denselben Commit wie die Vorlagen."
  },
  {
    "id": "apps-sdk/new-app-standard#10-alte-configs",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "10. Alte Configs",
    "anchor": "10-alte-configs",
    "text": "Gespeicherte Szenen tragen Configs früherer Fassungen. Sie werden nicht beim Lesen umgedeutet, sondern einmal durch einen Seed-Schritt in die neue Form geschrieben, Bestand und Szenen-Vorlagen gleichermaßen. Danach kennt das Schema nur die neuen Felder, und Formular, Vorlagen-Erkennung, Schnittstelle und Assistenten sehen denselben Stand wie die Anzeige. Die Abbildung ist eine reine Funktion mit Tests über jede Kombination der alten Werte. Beim Wetter: Ein gesetzter Ort wird zum festen Ort, die Tage sind an, wenn die alte Config Tage zeigte ( forecastDays über 0 und layout nicht current-only ), ein transparenter Hintergrund bleibt transparent , jeder andere wird dark . Alte Schlüssel bleiben im JSON stehen. Eine Organisation, die das Update noch nicht angenommen hat, läuft mit der älteren Fassung weiter und liest sie. Das neue Schema verwirft unbekannte Schlüssel still. Neue Felder haben eine Vorgabe , damit auch eine Config ohne sie gültig ist. Die Grundlinie tests/compat-baseline.json der App wird mit pnpm app-compat --update <id> fortgeschrieben, sobald die alten Felder das Schema verlassen. Szenen-Vorlagen und Demo-Quellen stehen in der neuen Form. Eine Vorlage, die alte Felder schreibt, erzeugt den Fall, den der Seed-Schritt gerade aufgelöst hat."
  },
  {
    "id": "apps-sdk/new-app-standard#11-host-vertrag-und-freigabe",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "11. Host-Vertrag und Freigabe",
    "anchor": "11-host-vertrag-und-freigabe",
    "text": "Formate, Passt-Bericht, Flächen und Symbole kamen mit der Fassung 1.1.0 des Host-Vertrags in die Anzeige. Der Bau einer App schreibt nach dist/renderer-host.json , welche Namen das Bündel aus der Anzeige bezieht, und setzt die Zusage im Manifest ( apiCompatibility.rendererHost ) auf die niedrigste Fassung, die diese Importe deckt. Das Wetter verlangt deshalb ^1.1.0 , eine App, die keinen der neuen Namen nutzt, bleibt bei ^1.0.0 . Eine Anzeige mit älterer Zusage weist ein Bündel, das mehr verlangt, als host-incompatible ab. Daraus folgt die Reihenfolge der Freigabe: zuerst die Anzeige mit der neuen Zusage durch ihre Kanäle, und erst wenn die Geräte sie angenommen haben, die App. Fire TV und Web-Display laden die Anzeige aus der Cloud, ein Raspberry Pi holt sie über die Fernwartung. Vor der Freigabe nach Produktion steht die Sichtprüfung auf Staging am echten Gerät: Pi, Fire TV und Web-Display, hell und dunkel, jede Vorlage. Liest die neue Fassung Daten, denen die Organisation noch nicht zugestimmt hat, wartet ihr Update auf diese Zustimmung, und bis dahin läuft die ältere Fassung weiter."
  },
  {
    "id": "apps-sdk/new-app-standard#die-nächsten-apps",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "Die nächsten Apps",
    "anchor": "die-nächsten-apps",
    "text": "Die Wetter-App ist die erste App im Standard. Die Reihenfolge der übrigen richtet sich nach dem Nutzen für Kunden und nach dem, was die Passt-Prüfung und der Blick auf die Store-Bilder heute zeigen: Reihenfolge App Warum jetzt 1 Öffnungszeiten Jeder Standort jeder Branche zeigt sie. Die Anzeige ist überschaubar, ihre vier Vorlagen passen schon. Als zweite App prüft sie den Leitfaden mit wenig Risiko, bevor eine große App folgt. 2 Notdienst-Apotheken Für Apotheken Pflicht im Schaufenster und das Kerngeschäft der Branche. Sie trägt die beiden offenen Ausnahmen der Passt-Prüfung: Hochkant überdecken sich Titel und Plakette mit der Notdienst-Nummer, in der Vorlage karte verschwindet die Beschriftung einer Marke unter der Liste. Wie das Wetter gewinnt sie am meisten vom Standort des Displays: eine Szene für alle Filialen, jede zeigt ihre Notdienste. 3 Nachrichten mit Marken-Apps und Ticker Ein Code für sieben Apps (Tagesschau, BBC, BILD, Deutschlandfunk, NDR, SPIEGEL, RSS), der Hebel ist groß. Die Laufzeile wächst heute nicht mit der Höhe ihres Bandes, und der Ticker kappt auf ganzer Bühne die Unterlängen seiner Meldungen. Beide Leisten folgen danach derselben Form band . 4 Menükarte Gastronomie und Bäckereien, oft hochkant am Tresen. Die Vorlagen passen, eine Anordnung je Form fehlt ihr wie jeder App außer dem Wetter. 5 Kalender Das Wochenraster skaliert seine Schrift kaum und wird in kleinen Rahmen unlesbar. Der Hintergrund ist als Vorgabe transparent, im Store-Bild ist der Rahm"
  },
  {
    "id": "apps-sdk/new-app-standard#weiterführendes",
    "path": "/de/apps-sdk/new-app-standard",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "new-app-standard",
    "pageTitle": "Eine App im neuen Standard",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Widget, Konfiguration und Vorschau — jedes Feld der Widget-Definition, Formate, Passt-Bericht und Vorschau-Zustände im Detail. Von der Vorlage bis zur Freigabe — der ganze Weg einer neuen App, die mit pnpm app:new schon im Standard beginnt. Datenzugriff, Rechte und Bedingungen — der Lesebereich player-location und was eine App vor der Installation offenlegt. Signieren und der Weg in den Katalog — Kanäle und Freigabe einer Fassung."
  },
  {
    "id": "apps-sdk/about-apps",
    "path": "/de/apps-sdk/about-apps",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "about-apps",
    "pageTitle": "Was ist eine App, und wie kommt sie auf den Bildschirm?",
    "section": "Was ist eine App, und wie kommt sie auf den Bildschirm?",
    "anchor": "",
    "text": "Eine App ist eine fachliche Erweiterung der Plattform: ein Manifest, höchstens ein Widget und bei Bedarf ein Server-Teil. Diese Seite zeigt ihren Lebensweg vom Manifest bis zum Widget in der Szene — und sagt, was davon heute wem offensteht. Eine App startet keinen eigenen Prozess. Sie ist ein Paket, das die Plattform lädt und das sich in vier Flächen zeigt: dem Manifest mit Identität, Widgets, Rechten, Datenzugriff und Signaturpolitik; einem optionalen Server-Teil mit Resolver, Fetcher und Jobs; dem runtime -Bündel, das der Player lädt; und dem authoring -Bündel mit dem Konfigurationsformular für den Szenen-Editor. Das Manifest ist die einzige verbindliche Beschreibung: Was dort nicht steht, gibt es für Katalog, Portal und Gerät nicht. Jede App trägt höchstens ein Widget — die App ist die Einheit, die eine Organisation installiert, bewertet und wieder entfernt; mehrere Darstellungen löst ein Widget über seine Vorschau-Varianten und ein Konfigurationsfeld. Wie viel Maschinerie eine App trägt, ordnet sie in drei Stufen. Ein Teil der mitgelieferten Apps rendert allein aus seiner Konfiguration und hat keinen Server-Teil ( clock , webpage , notice-board , countdown , countup , world-clock , word-clock , ticker , qr-code , youtube , stream , embed ). weather , rss , tagesschau und calendar ziehen mit einem Fetcher aus fremden Quellen, die Marken-Apps bbc , bild , deutschlandfunk , ndr und spiegel erben Server-Teil und Widget von tagesschau . Eigene Ressourcen in einer eigenen App-D"
  },
  {
    "id": "apps-sdk/about-apps#was-daraus-folgt",
    "path": "/de/apps-sdk/about-apps",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "about-apps",
    "pageTitle": "Was ist eine App, und wie kommt sie auf den Bildschirm?",
    "section": "Was daraus folgt",
    "anchor": "was-daraus-folgt",
    "text": "Releases legt der Plattformbetrieb an. Der Katalog kennt keine Einreichung und keine Prüf-Warteschlange; Anlegen, Hochstufen, Zurückstufen und Ausmustern sind Betreiberarbeit. Apps entstehen heute im Quellbaum der Plattform. Das SDK @szenera/apps-sdk ist ein Paket dieses Quellbaums und nicht auf npm veröffentlicht; es reist nicht mit dem Release-Zug der Dienste, sondern hat seinen eigenen Takt. Neue Fassungen bleiben verträglich. Felder einer Widget-Konfiguration kommen nur mit Vorgabe hinzu und verschwinden nie, denn eine Fassung muss die Konfigurationen ihrer Vorgängerinnen lesen. Der Server-Teil einer App läuft in genau einer Fassung und bedient jede, die ein Display noch spielt, deshalb ändern sich seine Routen, Ausgaben und Antworten nur additiv, und jede Route nimmt weiter an, was eine frühere Fassung schickt. Beides hält ein Test in jeder App gegen eine festgeschriebene Grundlinie. Einen Einreichungsweg für Apps Dritter gibt es nicht. Die folgenden Seiten beschreiben den Vertrag, gegen den die mitgelieferten Apps geschrieben sind — nicht ein Verfahren, das Sie heute durchlaufen könnten. Die Nutzersicht steht im Handbuch. Installieren, Rechte und Bedingungen aus Sicht der Organisation: Apps im Handbuch ."
  },
  {
    "id": "apps-sdk/about-apps#weiterführendes",
    "path": "/de/apps-sdk/about-apps",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "about-apps",
    "pageTitle": "Was ist eine App, und wie kommt sie auf den Bildschirm?",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Das Manifest — jedes Feld mit Pflicht, Format und Wirkung. Signieren und der Weg in den Katalog — Bündel, Signatur, Kanäle. Apps im Handbuch — die Sicht der Organisation."
  },
  {
    "id": "apps-sdk/manifest",
    "path": "/de/apps-sdk/manifest",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "manifest",
    "pageTitle": "Das Manifest",
    "section": "Das Manifest",
    "anchor": "",
    "text": "Das Manifest ist die einzige verbindliche Beschreibung einer App: defineAppManifest im SDK, die Prüfung beim Laden und die Ablage im Katalog lesen exakt dieselbe Form. Die Tabellen nennen jedes Feld mit Pflicht, Format und Wirkung; Beispiele stehen darunter."
  },
  {
    "id": "apps-sdk/manifest#die-felder",
    "path": "/de/apps-sdk/manifest",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "manifest",
    "pageTitle": "Das Manifest",
    "section": "Die Felder",
    "anchor": "die-felder",
    "text": "Feld Pflicht Format Wirkung id ja Kleinbuchstaben mit Bindestrichen ( ^[a-z][a-z0-9]*(-[a-z0-9]+)*$ ); core ist reserviert und wird abgelehnt Kanonische Kennung der App; muss dem Katalog-Eintrag entsprechen. version ja Semver ( 1.0.0 ) Die Fassung, die ein Release einfriert. displayName ja 1–60 Zeichen Name im Store und im Portal. description ja max. 300 Zeichen Kurzbeschreibung im Store. category ja zeit · information · web-video · gestaltung · geschaeft Die eine Einordnung: Filter im Store und Abschnitt im App-Bereich des Szenen-Editors. Widgets tragen keine eigene. tier Vorgabe standard standard · pro · premium · addon Klasse: standard trägt im Store keine Marke, pro die Marke Pro , addon die Marke Add-on und wird zugebucht; premium trägt heute keine App. vertical nein pharmacy Branchen-Facette des Store; übergreifende Apps lassen es weg. publisher nein { name: 1–80 Zeichen } Herausgeber, den das Store-Listing zeigt. schemaName ja ^apps_[a-z][a-z0-9_]+$ Name der App-Datenbank: apps_<id> , Bindestriche der Kennung werden zu Unterstrichen ( qr-code → apps_qr_code ). Pflicht auch ohne Datenbank. apiCompatibility ja { appsSdk: Semver-Bereich, apiVersion?: string, rendererHost?: Semver-Bereich } appsSdk gilt dem Server-Teil: Der Loader hält die laufende SDK-Version dagegen, passt sie nicht, lädt die App nicht. rendererHost nennt den Host-Vertrag der Anzeige, den runtime- und authoring-Bündel brauchen, und gehört in jedes Manifest mit Widget: Anzeige und Szenen-Editor laden ein "
  },
  {
    "id": "apps-sdk/manifest#der-block-compatibility",
    "path": "/de/apps-sdk/manifest",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "manifest",
    "pageTitle": "Das Manifest",
    "section": "Der Block compatibility",
    "anchor": "der-block-compatibility",
    "text": "defineAppManifest verlangt den Block im Typ und zur Laufzeit: Ohne ihn wirft es mit einem Satz, der die drei Felder nennt. Jede Liste nennt einen Wert höchstens einmal. Feld Werte Bedeutung hosts szenera-os · fire-tv · tizen · webos · web-display , mindestens einer Die Wirte, auf denen die App läuft: SzeneraOS auf dem eigenen Gerät, die App für Fire TV und Android, die Apps für Samsung Tizen und LG webOS, ein Browser ohne eigenen Wirt. orientations landscape · portrait , mindestens eine Die Lagen des Bildschirms, quer und hochkant, in denen die App taugt. offline full · last-known · none Was die App ohne Netz zeigt: full weiter alles, weil sie allein aus ihrer Konfiguration und dem Vorrat des Geräts rendert (im Store „statisch“), last-known den Stand, den der Server zuletzt in den Schnappschuss des Displays gelegt hat, none nichts, weil sie live aus dem Netz lädt."
  },
  {
    "id": "apps-sdk/manifest#der-block-translations",
    "path": "/de/apps-sdk/manifest",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "manifest",
    "pageTitle": "Das Manifest",
    "section": "Der Block translations",
    "anchor": "der-block-translations",
    "text": "Die Texte des Manifests stehen auf Deutsch, der Pflichtsprache. Jede weitere Sprache steht unter translations als eigener Block, der nur Texte trägt: displayName und description , store (Einzeiler, Langtext, Funktionen und Einrichtungsschritte in der Reihenfolge der deutschen), widgets je Kennung mit Name, Beschreibung und variants je Ansicht, resources je Kennung, permissions je Schlüssel, dataAccess.external in der Reihenfolge der Dienste und legal.disclaimerSummary . Kennungen, Werte und Regeln stehen nie in einem Block. Ein Block ist vollständig oder gar nicht da: defineAppManifest prüft ihn mit appManifestTranslationIssues() und wirft beim Bauen, mit der Stelle beim Namen. Der Store antwortet in der Sprache, die das Portal erbittet, und fällt auf Deutsch zurück. Englische Begriffe folgen den Portalen: display , nie screen , die Leinwand heißt video wall . pnpm app:check <id> prüft die Texte beider Sprachen nach der Hausregel, in der Zeile Texte ."
  },
  {
    "id": "apps-sdk/manifest#der-legal-block",
    "path": "/de/apps-sdk/manifest",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "manifest",
    "pageTitle": "Das Manifest",
    "section": "Der legal-Block",
    "anchor": "der-legal-block",
    "text": "Feld Pflicht Bedeutung disclaimerKey ja Schlüssel der Fassung, auf die sich eine Zustimmung bezieht ( <gruppe>.bedingungen.v1 ). Eine inhaltliche Änderung des Hinweises hebt ihn ( v1 → v2 ). disclaimerSummary ja, max. 500 Zeichen Kurzfassung in Klartext; Installationsdialog, Store-Seite und Widget-Fußzeile zeigen sie unverändert. Sie stellt die Fassung dar, eine neue Formulierung bei gleichem Schlüssel verlangt keine Zustimmung. termsPath nein Pfad des Volltexts auf der öffentlichen Website ( ^\\/[a-z0-9-]+(\\/[a-z0-9-]+)*$ , ≤ 200 Zeichen, z. B. /app-bedingungen/notdienst ). Ein Pfad, keine Adresse — das Portal setzt seine Basis davor. requireAcceptanceAt Vorgabe install install : Zustimmung bei der Installation und danach mit jeder neuen Fassung des Hinweises. upgrade : Zustimmung bei jeder neuen Fassung der App, auch bei gleichem Hinweis. never : keine Zustimmung. Ein neuer disclaimerKey macht ein Release zustimmungspflichtig: Sobald die neue Fassung als stable veröffentlicht ist, steht die Installation auf Update wartet . Wer den Hinweis inhaltlich ändert, hebt deshalb den Schlüssel, im selben Arbeitsgang wie Kurzfassung, Volltext und Fassung der App. Eine neu formulierte Kurzfassung bei gleichem Schlüssel übernimmt die Plattform still. Eine Manifest-Änderung ohne Versionssprung erreicht bestehende Installationen nicht."
  },
  {
    "id": "apps-sdk/manifest#was-nicht-im-manifest-steht",
    "path": "/de/apps-sdk/manifest",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "manifest",
    "pageTitle": "Das Manifest",
    "section": "Was nicht im Manifest steht",
    "anchor": "was-nicht-im-manifest-steht",
    "text": "Fetcher und Jobs. Sie deklariert eine App als Decorator an Klassen ihres Server-Teils ( @Fetcher , @JobHandler , @WidgetResolver ); die Plattform entdeckt sie beim Start. Das Manifest bleibt reines Datenformat. Ob ein Ladefehler den Start abbricht. Das ist eine Eigenschaft der Installation und steht in der Server-Konfiguration des Betriebs; critical im Manifest ist nur Bericht."
  },
  {
    "id": "apps-sdk/manifest#beispiel-das-manifest-der-notdienst-app-gekürzt",
    "path": "/de/apps-sdk/manifest",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "manifest",
    "pageTitle": "Das Manifest",
    "section": "Beispiel: das Manifest der Notdienst-App (gekürzt)",
    "anchor": "beispiel-das-manifest-der-notdienst-app-gekürzt",
    "text": ""
  },
  {
    "id": "apps-sdk/manifest#formprüfung-und-querregeln",
    "path": "/de/apps-sdk/manifest",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "manifest",
    "pageTitle": "Das Manifest",
    "section": "Formprüfung und Querregeln",
    "anchor": "formprüfung-und-querregeln",
    "text": "defineAppManifest prüft die Form gegen das Schema, wendet Vorgabewerte an ( tier , critical , criticalForTenant , signing ), verlangt den Block compatibility und wirft bei jedem Formfehler. Drei Regeln, die sich aus mehreren Feldern zugleich ergeben, prüft erst die Plattform beim Laden: Das appId jedes Widgets muss der id des Manifests gleichen, jede Ressource muss das Präfix der App tragen, und keine zwei Widgets dürfen dieselbe Kennung haben. Ein Manifest, das dem Schema widerspricht, weist die Release-Anlage mit Manifest.ValidationFailed (HTTP 422) ab."
  },
  {
    "id": "apps-sdk/manifest#weiterführendes",
    "path": "/de/apps-sdk/manifest",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "manifest",
    "pageTitle": "Das Manifest",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Widget, Konfiguration und Vorschau — die Widget-Definition im Feld widgets . Datenzugriff, Rechte und Bedingungen — dataAccess , permissions , unlocks , connects und legal im Detail. Signieren und der Weg in den Katalog — was aus signing beim Release und am Gerät folgt."
  },
  {
    "id": "apps-sdk/widget-and-preview",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Widget, Konfiguration und Vorschau",
    "anchor": "",
    "text": "defineWidget baut die Widget-Definition, die im Manifest unter widgets steht: Konfigurationsschema, Größen und die Vorschau-Daten, aus denen Katalog, Miniaturen und die Vorlagen des Editors entstehen. Die Tabellen nennen jedes Feld; das Uhr-Beispiel zeigt sie im Zusammenhang."
  },
  {
    "id": "apps-sdk/widget-and-preview#die-felder-der-widget-definition",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Die Felder der Widget-Definition",
    "anchor": "die-felder-der-widget-definition",
    "text": "Feld Pflicht Format Bedeutung id ja Kleinbuchstaben und Ziffern mit Bindestrichen, 2–100 Zeichen Der Widget-Typ; unter diesem Schlüssel registrieren runtime- und authoring-Bündel ihre Komponenten. appId ja App-Kennung Muss der id des Manifests gleichen (Querregel beim Laden). name ja 1–60 Zeichen Name in der Editor-Palette. description ja max. 300 Zeichen Kurztext in Palette und Store. configSchema ja Zod-Schema; jedes Feld mit .default() und .describe() Der Vertrag der Widget-Konfiguration. defineWidget leitet daraus das JSON-Schema ab, das der Katalog ausliefert — siehe Feldtexte und Beispiele . noResolver Vorgabe false boolean true : rein statisch, rendert allein aus der Konfiguration. false : verlangt einen @WidgetResolver im Server-Teil — fehlt er, lädt die App nicht. size.min nein { w, h } in ganzen Bezugspixeln einer 1920 × 1080-Szene Der kleinste Rahmen, in dem das Widget noch etwas Sinnvolles zeigt; der Editor lässt die Griffe nicht darunter ziehen. Jede Vorlage muss mindestens so groß sein. preview.thumbnailUrl ja Pfad Miniatur in Editor-Palette und Kachel. preview.defaultConfig nein Konfiguration Startbelegung neu platzierter Instanzen; zugleich die Grundlage jeder Vorschau-Variante. preview.mockData nein Nutzlast oder Konfiguration Beispieldaten für die Vorschau; bei statischen Widgets eine repräsentative Konfiguration. preview.variants Vorgabe [] siehe unten Die Darstellungen, die das Widget beherrscht, mit size zugleich die Vorlagen im Editor. preview.states nei"
  },
  {
    "id": "apps-sdk/widget-and-preview#feldtexte-und-beispiele",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Feldtexte und Beispiele",
    "anchor": "feldtexte-und-beispiele",
    "text": "defineWidget übersetzt das Zod-Schema beim Laden des Manifests einmal in ein JSON-Schema-Dokument nach Draft 2020-12 — das ist es, was GET /v1/widget-catalog ausliefert und was ein fremder Client gegen seine Konfiguration prüft. Zwei Dinge steuern Sie dabei bewusst: Die Feldtexte. JSON-Schema kennt je Feld zwei Texte — title als kurze Überschrift, description als Satz dazu —, Zod kennt nur einen. Beide stehen deshalb in EINEM .describe() , getrennt durch einen senkrechten Strich: Ohne Strich wird der ganze Text zur description , und das Feld bekommt keine Überschrift. Ein Feld ohne .describe() steht ohne Erklärung im Katalog — für jeden, der ohne den Editor arbeitet, ist es dann so gut wie unbeschriftet. Die Beispiele. examples füllt das SDK selbst: die preview.defaultConfig und je Vorschau-Variante dieselbe Konfiguration mit der Teil-Konfiguration der Variante darüber. Jedes Beispiel wird gegen das Zod-Schema geprüft — bricht eines die eigene Regel, schlägt der Bau der App fehl und nennt das Feld. Ein Widget ohne preview.defaultConfig fällt auf preview.mockData zurück; hat es beides nicht, lässt es sich nicht bauen. Was das Dokument am Ende sagt, steht unter Widgets konfigurieren ."
  },
  {
    "id": "apps-sdk/widget-and-preview#vorschau-varianten",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Vorschau-Varianten",
    "anchor": "vorschau-varianten",
    "text": "preview.variants ist die einzige Quelle dafür, was eine App zeigen kann: Die Store-Galerie rendert daraus ihre Ansichten, der Bildgenerator leitet daraus ab, welche Miniaturen er aufnimmt, und der Editor bietet die Varianten mit Größe als Vorlagen an. Eine Layout-Option, die hier nicht steht, gibt es im Store nicht. Feld Pflicht Format Bedeutung id ja ^[a-z][a-z0-9-]{1,31}$ , eindeutig je Widget Zugleich das Namenssuffix des Bildes: /apps/<appId>/thumbnails/<widgetId>--<variantId>.png . label ja 1–40 Zeichen Beschriftung der Abbildung („Karten“, „Nächste groß“). description nein max. 160 Zeichen Ein Satz, wofür die Darstellung taugt. config nein Teil-Konfiguration Der Unterschied zu preview.defaultConfig ; ein Widget ohne Wahlmöglichkeit deklariert genau eine Variante ohne config . size nein { w, h } in ganzen Bezugspixeln einer 1920 × 1080-Szene Die Startgröße der Vorlage: Fügt der Editor die Variante ein, bekommt das Widget diesen Rahmen. Ohne size ist die Variante nur Abbildung. aspect Vorgabe 16:9 , bei size abgeleitet 16:9 · 9:16 Bildschirm-Lage der Aufnahme; quer ist der Regelfall. Mit size folgt sie der Größe (quer, solange der Rahmen mindestens so breit wie hoch ist), defineWidget setzt sie, und eine widersprechende Angabe bricht den Bau. primary Vorgabe false boolean Genau eine Variante je Widget, das Varianten trägt; sie wird die Miniatur in Palette und Kachel und die Vorlage, die ein Klick einfügt. Der Vertrag weist null oder zwei kanonische Varianten ab."
  },
  {
    "id": "apps-sdk/widget-and-preview#größen-an-varianten",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Größen an Varianten",
    "anchor": "größen-an-varianten",
    "text": "Jede Variante bekommt eine size , die zu ihrem Zweck passt: eine Uhr als Kachel ( 600 × 300 ), ein Nachrichtenband als Leiste ( 1920 × 120 ), ein Aushang hochkant ( 600 × 800 ), eine Kartenansicht breit ( 1280 × 720 ). Die Größe ist der Rahmen, mit dem ein Klick die Vorlage in eine 1920 × 1080-Szene setzt. Auf einer anderen Bühne rechnet der Editor sie über die kurze Seite um. Eine Variante für stehende Displays bekommt eine hohe Größe statt aspect: '9:16' , die Lage folgt ihr. Höher als 1080 Bezugspixel ist keine Vorlage, ein Hochkant-Rahmen ist also eine Spalte in der Szene und kein ganzer Bildschirm. Die Miniatur einer Variante mit Größe zeigt das Widget 1 : 1 in diesem Rahmen, mittig auf einer ruhigen hellen Fläche, mit Name und Maß darunter. Das Bild bleibt 1920 × 1080, bei hoher Größe 1080 × 1920. Eine Variante ohne Größe füllt die Bühne ihrer Lage. Die Passt-Prüfung rendert jede Variante jeder App in ihrem Rahmen und schlägt fehl, wenn dort etwas übersteht, ein Text angeschnitten oder zu klein ist oder das Widget über den Passt-Bericht einen eingeschalteten Inhalt ausblendet. Sie läuft als Gate im Pull Request und lokal so: Gemessen wird die Tinte eines Textes, nicht sein Zeilenkasten. Die Regeln im Einzelnen: Regel Befund Überstand Ein Element, das selbst zeichnet (Bild, Fläche, Linie), geht über den Rahmen. Angeschnitten oder verdeckt Ein Text ragt aus dem Rahmen oder aus einem Kasten, der seinen Überlauf kappt, oder verschwindet ganz dahinter (die letzten Zeilen ein"
  },
  {
    "id": "apps-sdk/widget-and-preview#vorschau-zustände",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Vorschau-Zustände",
    "anchor": "vorschau-zustände",
    "text": "preview.states zeigt im Einstellungs-Fenster, wie das Widget in einer bestimmten Lage aussieht, ohne dass sie gerade eintritt: Regen, Nacht, keine Daten, veraltet. Der Editor bietet die Zustände als Umschalter neben „Echte Daten“ an. Feld Pflicht Format Bedeutung id ja ^[a-z][a-z0-9-]{1,31}$ , eindeutig je Widget Kennung des Zustands. label ja 1–40 Zeichen Beschriftung am Umschalter („Regen“, „Nacht“). description nein max. 160 Zeichen Ein Satz, was der Zustand zeigt. mockData ja Nutzlast oder Konfiguration Die Beispieldaten des Zustands, in derselben Form wie preview.mockData . Ein Schema der Nutzlast gibt es nicht. defineWidget prüft deshalb die Form: reine JSON-Daten (keine Funktion, kein Datum, keine Zahl ohne Wert) und dieselbe Art wie preview.mockData (Objekt, Liste oder Einzelwert). Übersetzt wird die Beschriftung unter translations.<sprache>.widgets.<widget>.states.<zustand>.label , wie die einer Variante. Der Zustand empty ist Pflicht für jedes Widget mit Quelle ( noResolver: false ), pnpm app:check verlangt ihn in der Zeile „Zustände“. Er zeigt das Widget ohne Daten, bei einem Daten-Widget mit mockData: null (keine Nutzlast, wie vor dem ersten Abruf oder nach einem gescheiterten). Apps, die ihren leeren Zustand erst in ihrer eigenen Welle neu zeichnen, stehen bis dahin in tools/app-kit/src/check/state-exceptions.ts . Nie Editor-Text am Display. Leer, gestört oder unkonfiguriert heißt am Display eine ruhige Fläche und höchstens ein neutraler Satz, die Erklärung mit d"
  },
  {
    "id": "apps-sdk/widget-and-preview#vorlagen-im-editor",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Vorlagen im Editor",
    "anchor": "vorlagen-im-editor",
    "text": "Eine Variante mit size ist im Szenen-Editor eine Vorlage. Ihr Inhalt ist preview.defaultConfig mit der Teil-Konfiguration der Variante flach darüber ( { ...defaultConfig, ...variant.config } ), genau wie im Bildgenerator. Was der Store zeigt, fügt der Editor deshalb so ein. Weg Was passiert Klick auf die Kachel im App-Bereich Die kanonische Vorlage ( primary ) kommt in ihrer Startgröße mittig auf die Bühne. Ist ein Slot der Layout-Vorlage frei, füllt das Widget ihn mit der Vorlage, deren Form am besten zum Slot passt: in einer Spalte die Hochkant-Vorlage, in einer Leiste die Leiste. Pfeil an der Kachel „ einfügen“ zeigt alle Vorlagen als Live-Miniaturen im echten Seitenverhältnis, mit Name, Form und Größe. Ein Klick fügt genau diese ein. Verweilen auf der Kachel Die Vorschau zeigt die kanonische Vorlage in ihrem Rahmen, nicht bühnenfüllend. Ziehen auf die Bühne Startgröße der kanonischen Vorlage um die Drop-Stelle, der freie Slot unter dem Zeiger ist hervorgehoben. Leiste am gewählten Widget „Vorlage“ trennt, was zum Rahmen passt (gleiche Form, nur der Inhalt wechselt), von dem, was einen neuen Rahmen setzt (mittig um den alten Mittelpunkt). Im Slot wechselt nur der Inhalt. Einstellungs-Fenster Kacheln je Vorlage, die aktive markiert, „angepasst“, sobald der Inhalt abweicht. Zurücksetzen führt auf die aktive Vorlage. Den Rahmen setzt eine gewählte Vorlage erst beim Übernehmen, zusammen mit der Config in einem Schritt. Drei Regeln, die eine App beim Entwerfen ihrer Vorlagen ke"
  },
  {
    "id": "apps-sdk/widget-and-preview#ein-widget-je-app-vorlagen-statt-zweiter-widgets",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Ein Widget je App, Vorlagen statt zweiter Widgets",
    "anchor": "ein-widget-je-app-vorlagen-statt-zweiter-widgets",
    "text": "Keine App trägt zwei Widgets. Die App ist die Einheit, die eine Organisation installiert und wieder entfernt — ein Bündel unverwandter Widgets ließe sich nicht bewerten. Was mehrere Darstellungen braucht, löst das über die Vorschau-Varianten und ein Konfigurationsfeld: Eine Variante mit size ist eine Vorlage. Sie legt die Startgröße des Rahmens fest und mit ihrer Teil-Konfiguration den Inhalt, den der Kunde beim Einfügen bekommt. Wie der Inhalt innerhalb des Rahmens aussieht, entscheiden Rahmen und Konfiguration — die Form des Rahmens liest das Widget über useWidgetFormat() , die Wahl des Kunden steht in einem Konfigurationsfeld wie composition bei der Uhr. Ein Widget mit noResolver: false wird auch ohne Player aufgelöst, etwa für die Vorschau im Editor; der Resolver bekommt dann playerId: null und muss diesen Fall verkraften."
  },
  {
    "id": "apps-sdk/widget-and-preview#formate-wie-ein-widget-seinen-rahmen-nutzt",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Formate: wie ein Widget seinen Rahmen nutzt",
    "anchor": "formate-wie-ein-widget-seinen-rahmen-nutzt",
    "text": "Ein Widget steht in einer Szene selten bildschirmfüllend, oft klein, hochkant oder als Leiste. Es ordnet sich deshalb nach dem Rahmen, den die Szene ihm gibt, und zwar nach einer Rechnung für alle Apps. Die Hooks stehen in @szenera/widget-foundation/hooks : Hook Liefert Wofür useWidgetFormat(options?) { shape, size, aspect, shortSideRef, width, height } Form und Größenklasse des Rahmens; die App zeichnet je Form eine Anordnung. useWidgetGeometry() { x, y, width, height, stage: { width, height } } Die aufgelöste Box in Bühnenpixeln und die Bühne, ohne DOM-Messung. useFitReport() { reportHidden(parts) } Meldet dem Editor, welche eingeschalteten Teile im Rahmen gerade fehlen. Die Form folgt dem Seitenverhältnis Breite : Höhe: Form Seitenverhältnis Typischer Platz band ab 3,2 : 1 Leiste am Rand der Szene wide 1,3 bis 3,2 liegender Kasten square 0,78 bis 1,3 Kachel tall unter 0,78 Spalte Beim Ziehen der Griffe im Editor wechselt die Form erst, wenn das Verhältnis 4 % hinter der Schwelle liegt. So springt ein Widget nicht bei jeder Zeigerbewegung zwischen zwei Anordnungen. Die Größe misst die kurze Seite des Rahmens in Bezugspixeln einer Bühne mit kurzer Seite 1080: kurze Seite × 1080 / kurze Seite der Bühne . Unter 400 Bezugspixeln ist der Rahmen compact , sonst regular . Eine 4K-Szene hat doppelt so viele Pixel, ihr Widget ist deshalb nicht größer: 600 Pixel auf einer 2160 hohen Bühne sind 300 Bezugspixel. Wer feiner stufen will, übergibt eigene Stufen, etwa useWidgetFormat({ siz"
  },
  {
    "id": "apps-sdk/widget-and-preview#anzeige-bausteine",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Anzeige-Bausteine",
    "anchor": "anzeige-bausteine",
    "text": "Was mehrere Apps brauchen, steht einmal in @szenera/widget-foundation/widget-ui . Die Bausteine tragen keinen eigenen Stil, sie nehmen Fläche, Schrift und Maße der App. Sie kamen mit der Fassung 1.2.0 des Host-Vertrags in die Anzeige, eine App, die einen davon nutzt, verlangt deshalb ^1.2.0 (siehe Host-Vertrag und Freigabe ). Baustein Wofür Wann nehmen PagedList Einträge auf Seiten statt schrumpfender Schrift: Die Liste hält die Mindestschrift ein, verteilt, was darüber nicht passt, auf Seiten im festen Takt von zehn Sekunden mit Seitenanzeige „1/3“ und zeigt bei motion: frozen die erste Seite. Gemessen wird jeder Eintrag in einer unsichtbaren Messschicht. Sobald eine Anzeige eine Folge gleichartiger Einträge zeigt, deren Zahl die App nicht bestimmt: Termine, Gerichte, Schlagzeilen, Notdienste. Eine feste Zahl von Teilen ordnet die App selbst an. QuietState Am Display die Fläche und höchstens ein neutraler Satz, im Editor die Erklärung mit dem nächsten Schritt. Immer, wenn eine Anzeige keine Daten hat: Quelle leer, Abruf gescheitert, Pflichtfeld fehlt. Der Zustand empty zeigt genau diesen Baustein. CompanionQr Ein Code mit Beschriftung, der auf Displays scannt: Ruhezone von vier Modulen, eine Codefläche, die nie durchsichtig ist, Kontrast der Module, ganze Bildpunkte je Modul, Fehlerkorrektur M. Die Beschriftung schiebt den Code nie aus seiner Ruhezone. Wann immer eine App einen Code zeigt, als Ziel eines Links, eines Wegs oder eines Gäste-WLANs. MarqueeBand Das Laufband: Nah"
  },
  {
    "id": "apps-sdk/widget-and-preview#die-zwei-bündel",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Die zwei Bündel",
    "anchor": "die-zwei-bündel",
    "text": "Bündel Einstieg Was es registriert runtime (Player) registerWidgets(registry) Je Widget-Typ die Render-Komponente, lazy geladen — registry.register('clock', lazy(() => import('../widgets/clock/runtime'))) . authoring (Editor) registerAuthoring(registry) Je Widget-Typ das Konfigurationsformular — registry.registerConfigForm('clock', ClockConfigForm) . Der Renderer lädt das runtime-Bündel vom Gerät und lässt es seine Widgets in die Registry eintragen; scheitert der Import, meldet er widget.bundle.load-failed , statt die Szene abzubrechen. Das authoring-Bündel lädt der Szenen-Editor des Portals."
  },
  {
    "id": "apps-sdk/widget-and-preview#beispiel-die-uhr-gekürzt",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Beispiel: die Uhr (gekürzt)",
    "anchor": "beispiel-die-uhr-gekürzt",
    "text": "Das Konfigurationsschema dazu ist ein Zod-Objekt, in dem jedes Feld eine Vorgabe und einen Text trägt — etwa variant: z.enum(['digital', 'analog']).default('digital').describe('Variante|„digital\" ist die Zahlen-Uhr, „analog\" ein gezeichnetes Zifferblatt.') . Unbekannte Schlüssel verwirft das Schema still, damit ältere gespeicherte Szenen weiter parsen."
  },
  {
    "id": "apps-sdk/widget-and-preview#weiterführendes",
    "path": "/de/apps-sdk/widget-and-preview",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "widget-and-preview",
    "pageTitle": "Widget, Konfiguration und Vorschau",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Das Manifest — wo die Widget-Definition eingetragen wird. Server-Teil: Resolver, Fetcher und Jobs — der Resolver, den noResolver: false verlangt. Die Beispiel-Apps — Kategorie, Klasse und Bedingungen jeder App. Signieren und der Weg in den Katalog — wie die fertige Definition über GET /v1/widget-catalog wieder herauskommt. Widgets konfigurieren — wie ein Client das erzeugte Schema liest und seine Konfiguration dagegen prüft."
  },
  {
    "id": "apps-sdk/configure-widgets",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Widgets konfigurieren",
    "anchor": "",
    "text": "Der Widget-Katalog liefert zu jedem Widget ein vollständiges JSON-Schema seiner Konfiguration: jedes Feld mit Überschrift und Erklärung, die Pflichtfelder benannt und mindestens eine fertige Beispiel-Konfiguration. Wer eine Szene ohne den Editor baut, liest daraus die Regeln, statt sie zu raten — und prüft seine Konfiguration dagegen, bevor er sie schreibt."
  },
  {
    "id": "apps-sdk/configure-widgets#den-katalog-abrufen",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Den Katalog abrufen",
    "anchor": "den-katalog-abrufen",
    "text": "Die Antwort trägt unter entries je Widget einen Eintrag: Feld Bedeutung appId Die App, zu der das Widget gehört — oder core bei den sechs eingebauten Bausteinen. version Die Fassung der App, aus der die Definition stammt; bei den eingebauten Bausteinen fest 0.0.0 . definition.id Der Widget-Typ. Genau dieser Wert steht später in widgetType der Widget-Instanz. definition.name , definition.description Name und Kurztext, wie sie auch die Auswahl im Editor zeigt. definition.configSchema Das JSON-Schema der Konfiguration — der Gegenstand dieser Seite. definition.size.min Der kleinste Rahmen in Bezugspixeln einer 1920 × 1080-Szene, in dem das Widget noch etwas Sinnvolles zeigt. definition.preview.defaultConfig Die Konfiguration, mit der eine frisch eingefügte Instanz startet. definition.preview.variants Die Darstellungen, die das Widget beherrscht, je mit einer Teil-Konfiguration und, als Vorlage, mit einer Startgröße ( size ). definition.preview.states Benannte Beispiel-Nutzlasten für die Vorschau im Editor (Regen, Nacht, keine Daten). Für eine Konfiguration ohne Bedeutung. Der Katalog spiegelt den jeweils neuesten freigegebenen Stand einer App. Eine Organisation, die eine ältere Fassung installiert hat, kann geringfügig davon abweichen; maßgeblich ist dann die Fassung, die in appVersionPin der Instanz steht."
  },
  {
    "id": "apps-sdk/configure-widgets#was-im-configschema-steht",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Was im configSchema steht",
    "anchor": "was-im-configschema-steht",
    "text": "Das configSchema ist ein JSON-Schema-Dokument nach Draft 2020-12. Es beschreibt genau das Objekt, das später als config in der Widget-Instanz steht. Schlüssel Bedeutung $schema Die Fassung der Spezifikation: https://json-schema.org/draft/2020-12/schema . type Immer object — eine Widget-Konfiguration ist nie eine Liste und nie ein blanker Wert. title , description Name und Kurztext des Widgets, damit das Dokument auch für sich allein verständlich bleibt. properties Die Felder der Konfiguration, je Feld ein eigener Knoten. required Die Feldnamen, ohne die die Konfiguration unvollständig ist. Häufig leer: Felder mit Vorgabe sind nicht Pflicht. examples Mindestens eine vollständige Konfiguration, die Sie unverändert übernehmen können. Jeder Knoten unter properties trägt Klartext und, wo es sie gibt, die Grenzen des Werts: Schlüssel Bedeutung title Kurze Überschrift des Feldes, wie sie auch im Editor über dem Bedienelement steht. description Ein Satz dazu, was das Feld bewirkt. type string , number , integer , boolean , array oder object . enum Die zulässigen Werte eines Auswahlfeldes — andere weist die Prüfung ab. default Der Wert, der gilt, wenn das Feld fehlt. minimum , maximum , minLength , maxLength , pattern Die Grenzen des Werts; pattern steht meist bei Farben ( #rrggbb ) und Kennungen. properties , items Bei verschachtelten Objekten und Listen: dieselben Regeln eine Ebene tiefer. Ein Ausschnitt aus dem Baustein „Text“:"
  },
  {
    "id": "apps-sdk/configure-widgets#vier-regeln-die-man-dem-schema-nicht-ansieht",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Vier Regeln, die man dem Schema nicht ansieht",
    "anchor": "vier-regeln-die-man-dem-schema-nicht-ansieht",
    "text": "Pflicht heißt: es gibt keine Vorgabe. In required steht nur, was die Plattform nicht selbst einsetzen kann — meistens der Inhalt. Alles andere trägt ein default , und ein weggelassenes Feld nimmt genau diesen Wert an. Unbekannte Felder werden verworfen, nicht abgelehnt. Ein Feld, das das Schema nicht kennt, macht eine Konfiguration nicht ungültig; es verschwindet beim Speichern. Das hält Szenen lesbar, die vor einem Feld-Umbau entstanden sind — verlässt sich aber niemand darauf, dass ein Tippfehler auffällt. Die Beispiele sind vollständig und geprüft. Jeder Eintrag in examples ist eine Konfiguration, die das Schema erfüllt: die Start-Konfiguration und je Vorschau-Variante eine weitere. Der Bau einer App bricht ab, wenn ein Beispiel seine eigene Regel bricht. Eine Konfiguration überlebt neue Fassungen. Eine App fügt Felder nur mit Vorgabe hinzu, und keines verschwindet oder ändert seinen Typ. Eine gespeicherte Konfiguration bleibt deshalb gültig, wenn die Organisation die App aktualisiert, und eine ältere Fassung, die ein Display noch spielt, liest auch eine Konfiguration mit Feldern einer neueren."
  },
  {
    "id": "apps-sdk/configure-widgets#zwei-werte-mit-fester-form-ort-und-fläche",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Zwei Werte mit fester Form: Ort und Fläche",
    "anchor": "zwei-werte-mit-fester-form-ort-und-fläche",
    "text": "Zwei Feldarten der Editor-Formulare schreiben Werte, deren Form überall gleich ist. Wer eine Konfiguration selbst baut, findet sie im configSchema als Objekt bzw. Zeichenkette wieder: Wert Form Bedeutung Ort ( placeSelection() ) { \"mode\": \"display\" | \"fixed\", \"place\": Ort | null, \"fallback\": Ort | null } , ein Ort ist { \"displayName\", \"countryCode\", \"latitude\", \"longitude\" } display nimmt den Standort des Displays, das die Szene spielt, fixed den Ort unter place . fallback ist der Ersatzort für Displays, deren Standort keine bekannte Lage hat. Vorgabe: display , beide Orte null . Fläche eine Kennung wie light , dark , glass , transparent oder eine eigene der App Der Hintergrund des Widgets. Die zulässigen Werte nennt das Schema des Felds. Wie der Editor diese und alle anderen Felder zeigt, gliedert und an die Leiste am Widget stellt, beschreibt die Formular-Beschreibung der App ( config-ui.ts , Typ WidgetConfigUI ). Sie gehört zum Editor, nicht zum Katalog: Das configSchema bleibt die eine Regel für jeden, der eine Konfiguration schreibt."
  },
  {
    "id": "apps-sdk/configure-widgets#die-sechs-eingebauten-bausteine",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Die sechs eingebauten Bausteine",
    "anchor": "die-sechs-eingebauten-bausteine",
    "text": "Text, Bild, Video, Form, Grafik und Dokument gehören zur Grundausstattung: Sie werden nicht installiert, brauchen keine Zustimmung und stehen jeder Organisation offen. Im Katalog erscheinen sie wie die Widgets einer App — mit der reservierten Kennung core und dem festen Versions-Pin 0.0.0 . widgetType Name Wofür text Text Ein Text aus der Konfiguration, ohne Markup und ohne Netzwerkzugriff. image Bild Ein Bild aus der Medienbibliothek, wahlweise mit langsamer Bildbewegung. video Video Ein Video aus der Medienbibliothek, stumm und in Schleife. shape Form Rechtecke, Kreise, Sterne, Pfeile und Linien — gezeichnet aus Zahlen. graphic Grafik Ein Motiv aus der eingebauten Bibliothek, einfärbbar und spiegelbar. document Dokument Ein PDF aus der Medienbibliothek als Seitenbilder: eine Seite, nacheinander oder zwei nebeneinander. Bild, Video und Dokument verweisen auf die Medienbibliothek statt auf eine Adresse: assetRef trägt die Kennung der Datei und die Fassungen, die die Anzeige braucht. Ohne assetRef zeigt das Widget einen gestalteten Platzhalter — das ist ein gültiger Zustand und kein Fehler. Beim Dokument bleibt assetRef.variantSet leer: Seine Seitenbilder reisen immer mit, je Seite eine Zeile page-<n> in der Medien-Liste des Snapshots. Welche Seiten das Widget zeigt, steht in pages : numbers sind die gewählten Seiten, of die Seitenzahl der Datei beim Wählen. Fehlt pages , zeigt das Widget alle Seiten außer leeren. Hat die Datei später eine andere Seitenzahl als of , meldet die"
  },
  {
    "id": "apps-sdk/configure-widgets#je-baustein-eine-fertige-widget-instanz",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Je Baustein eine fertige Widget-Instanz",
    "anchor": "je-baustein-eine-fertige-widget-instanz",
    "text": "So sieht ein Eintrag in document.widgets einer Szene aus. id ist eine neue Kennung im Format wgi_<ULID> , frame die Box in Pixeln der Szenenfläche, zIndex die Stapelreihenfolge. Text Bild Video Form Grafik Dokument Kürzer geht es auch: Weil jedes Feld außer content eine Vorgabe trägt, ist { \"content\": \"Willkommen\" } bereits eine gültige Text-Konfiguration. Die ausgeschriebene Fassung oben ist die, die der Editor speichert."
  },
  {
    "id": "apps-sdk/configure-widgets#eine-konfiguration-prüfen-bevor-sie-sie-schreiben",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Eine Konfiguration prüfen, bevor Sie sie schreiben",
    "anchor": "eine-konfiguration-prüfen-bevor-sie-sie-schreiben",
    "text": "Nehmen Sie das configSchema des gewünschten Widgets und geben Sie es einem Prüfer für Draft 2020-12. Der Ertrag ist eine feldgenaue Meldung, statt einer abgelehnten Anfrage. Drei Dinge nimmt Ihnen der Prüfer nicht ab: Ob es die Datei gibt, auf die assetRef zeigt. Das beantwortet die Medienbibliothek. Ob der Widget-Typ zu einer installierten App gehört. Widgets einer nicht installierten App weist die Plattform beim Veröffentlichen ab; die eingebauten Bausteine stehen immer offen. Ob die Konfiguration Sinn ergibt. Ein QR-Code mit leerer Adresse ist schema-gültig und zeigt trotzdem nur einen Platzhalter."
  },
  {
    "id": "apps-sdk/configure-widgets#das-formular-im-editor",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Das Formular im Editor",
    "anchor": "das-formular-im-editor",
    "text": "Dieser Abschnitt richtet sich an App-Autoren: Er beschreibt, wie der Szenen-Editor aus der Formular-Beschreibung einer App ( config-ui.ts ) das Einstellungs-Fenster und die Leiste am Widget baut. Für eine Konfiguration über die Schnittstelle ändert er nichts."
  },
  {
    "id": "apps-sdk/configure-widgets#die-rangfolge",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Die Rangfolge",
    "anchor": "die-rangfolge",
    "text": "Das Einstellungs-Fenster nimmt die Formular-Beschreibung der App, wenn sie eine hinterlegt ( registry.registerConfigUI(widgetType, ui) ). Nur ohne sie greift ein eigenes Formular der App ( registerConfigForm ), ohne beides ein aus dem Schema abgeleitetes. Deklarativ zuerst, damit gemeinsame Verbesserungen (Gliederung, eingeklappte Gruppen, Hinweise, Ort-Feld) jede App erreichen, ohne dass sie ihr Formular nachzieht. Eine App im neuen Standard registriert deshalb kein ganzes Formular mehr, sondern höchstens einzelne Felder."
  },
  {
    "id": "apps-sdk/configure-widgets#eigene-felder-registerconfigfield",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Eigene Felder: registerConfigField",
    "anchor": "eigene-felder-registerconfigfield",
    "text": "Was keine Feldart abbildet (eine Stadtsuche, eine Auswahl aus eigenen Daten), bringt die App als einzelnes Bedienelement mit: Der Schlüssel ist der Punkt-Pfad des Felds ( city ) oder eines Teils davon ( location.place ist die Ortsauswahl des Ort-Felds location ). Das Formular rendert Beschriftung, Hilfetext, Pflicht-Stern und Fehler um das Bedienelement herum. Es bekommt: Prop Inhalt value , onChange Der Wert an diesem Schlüssel und der Weg, ihn zu schreiben. field Die deklarierte Beschreibung des Felds. config Die ganze Config des Entwurfs, nur zum Lesen. error Die Meldung, wenn der Entwurf hier gegen das Schema verstößt. context Rahmen des Widgets in Bühnenpixeln, seine Form und Größenklasse ( band , wide , square , tall , dieselbe Einordnung wie useWidgetFormat ), Sprache und Zeitzone der Organisation und ihre Displays (Name, Standortname, ob der Standort Koordinaten hat). apiFetch Der Transport der App für ihre eigenen Routen, wie bei einem eigenen Formular."
  },
  {
    "id": "apps-sdk/configure-widgets#feldarten-mit-eigenem-bedienelement",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Feldarten mit eigenem Bedienelement",
    "anchor": "feldarten-mit-eigenem-bedienelement",
    "text": "Feldart Im Fenster An der Leiste location Wahl „Standort des Displays“ oder „Fester Ort“ (ohne Display-Bezug mit allowDisplay: false nur der feste Ort). Beim Display-Bezug ein Satz, welche Displays keinen Standort mit Anschrift haben, mit fallback: true ein Ersatzort. Die Ortsauswahl ist die der App ( <feld>.place ), sonst eine einfache Eingabe aus Name, Land, Breite und Länge. nicht kompakt, nur im Fenster surface Kacheln mit Farbmuster und Beschriftung, eine aktiv. Auswahl mit Muster Gruppe mit collapsed: true Aufklapper, zu beim Öffnen, offen, solange darin ein Fehler steht. – required: true Pflicht-Stern an der Beschriftung. Ob ein Wert fehlt, entscheidet das Schema. –"
  },
  {
    "id": "apps-sdk/configure-widgets#schnellfelder-quickfields",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Schnellfelder: quickFields",
    "anchor": "schnellfelder-quickfields",
    "text": "Höchstens zwei Felder kompakter Art ( color , segmented , select , slider , toggle , surface ) nennt quickFields , in der Reihenfolge der Leiste. Andere Arten und mehr als zwei Felder lässt die Leiste weg, defineWidgetConfigUI() meldet das schon beim Bau. Neben den Schnellfeldern zeigt die Leiste bei einem Widget mit mehreren Vorlagen die Vorlagen-Auswahl ( Widget, Konfiguration und Vorschau )."
  },
  {
    "id": "apps-sdk/configure-widgets#der-passt-bericht-schlüssel-sind-feldnamen",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Der Passt-Bericht: Schlüssel sind Feldnamen",
    "anchor": "der-passt-bericht-schlüssel-sind-feldnamen",
    "text": "Blendet die Laufzeit einen eingeschalteten Inhalt aus, weil der Rahmen zu klein ist, meldet sie ihn mit useFitReport().reportHidden(parts) . Die Teile heißen wie die Felder der Config , die den Inhalt einschalten, etwa ['showDaily'] . Der Editor zeigt dann unter der Vorschau „In diesem Rahmen ausgeblendet: Nächste Tage.“ mit der Beschriftung des Felds aus der Formular-Beschreibung und unter dem Schalter einen Satz, warum er nichts bewirkt. Ein Teil ohne passendes Feld erscheint mit seinem Schlüssel."
  },
  {
    "id": "apps-sdk/configure-widgets#weiterführendes",
    "path": "/de/apps-sdk/configure-widgets",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "configure-widgets",
    "pageTitle": "Widgets konfigurieren",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Widget, Konfiguration und Vorschau — wie eine App ihre Widget-Definition schreibt, aus der dieses Schema entsteht. Signieren und der Weg in den Katalog — die beiden Kataloge über alle Apps und ihre Abfragen. Datenzugriff, Rechte und Bedingungen — was eine App vor der Installation offenlegt."
  },
  {
    "id": "apps-sdk/backend",
    "path": "/de/apps-sdk/backend",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "backend",
    "pageTitle": "Server-Teil: Resolver, Fetcher und Jobs",
    "section": "Server-Teil: Resolver, Fetcher und Jobs",
    "anchor": "",
    "text": "Der Server-Teil einer App ist ein Modul aus Klassen, die die Plattform beim Start in einen eigenen Container lädt. Drei Decorator machen daraus Verhalten — @WidgetResolver , @Fetcher und @JobHandler —, und alles, was der Server-Teil von der Plattform braucht, bezieht er über Token. Diese Seite nennt die Verträge, Regeln und Grenzwerte."
  },
  {
    "id": "apps-sdk/backend#was-der-server-teil-ist",
    "path": "/de/apps-sdk/backend",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "backend",
    "pageTitle": "Server-Teil: Resolver, Fetcher und Jobs",
    "section": "Was der Server-Teil ist",
    "anchor": "was-der-server-teil-ist",
    "text": "Nur Daten-Apps exportieren einen Server-Teil; Apps ohne Verhalten lassen ihn weg, statt ein leeres Modul zu pflegen. Die Plattform importiert das Modul nicht, sondern hebt seine Klassen in einen Container je App, in dem die Token gebunden sind. Eigene Routen des Server-Teils liegen unter /v1/apps/<id>/… . Zwei Apps können sich nie gegenseitig Klassen injizieren; ein Server-Teil sieht nur die Fläche des SDK. Alle drei Decorator hinterlegen nur eine Markierung an der Klasse. Die Plattform liest sie beim Start, registriert Resolver, legt Worker für Fetcher und Jobs an und verdrahtet sie mit dem Snapshot-Bau. Je Klasse gilt genau eine Markierung je Sorte. Die Klasse trägt zusätzlich die Injektions-Markierungen des Plattform-Containers ( @Injectable an der Klasse, @Inject(Token) an jedem Konstruktor-Parameter) — ohne sie kann der Container die Klasse nicht erzeugen."
  },
  {
    "id": "apps-sdk/backend#widgetresolver",
    "path": "/de/apps-sdk/backend",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "backend",
    "pageTitle": "Server-Teil: Resolver, Fetcher und Jobs",
    "section": "@WidgetResolver",
    "anchor": "widgetresolver",
    "text": "Je Widget-Typ registriert eine App genau einen Resolver. Beim Snapshot-Bau ruft die Plattform resolve(ctx) mit der bereits validierten Konfiguration und erhält eine render-fertige Nutzlast — oder null . Der Resolver der Wetter-App, gekürzt um Fehlerbehandlung und Zeitfenster-Rechnung. Er ruft die Quelle nie selbst: Er bestimmt den Ort (fest gewählt oder der Standort des Displays aus ctx.location ), liest über den Fetcher-Cache ( APP_FETCHER_CACHE ) und reicht den Fetcher nur als Lader für den Fehltreffer hinein. Zwei Details aus der ungekürzten Fassung: Ein Fehler des Anbieters oder der Infrastruktur wird als weather.resolve.failed protokolliert und weitergeworfen, damit der Snapshot-Bau ihn als Resolver-Fehler ausweist statt als „kein Ergebnis“. Und bei einem Cache-Treffer verankern die Zeitfenster am ursprünglichen Abrufzeitpunkt ( cachedAt ), nicht an „jetzt“ — zeitzusagen aus @szenera/apps-sdk/backend rechnet validUntil und cacheUntil daraus. Der Kontext (WidgetResolverContext): Feld Typ Bedeutung tenantId string Die Organisation, für die aufgelöst wird. playerId string oder null Der Player; null bei der Vorschau auf Anfrage (etwa im Editor), solange sie für kein bestimmtes Display auflöst. widgetInstanceId string Die Instanz in der Szene. widgetType string Der Widget-Typ. appVersionPin string Die in der Szenen-Version fixierte App-Version, z. B. 1.2.0 . config unknown Bereits gegen das Konfigurationsschema validiert. traceId string Kennung für die Nachverfolgung. mapTile"
  },
  {
    "id": "apps-sdk/backend#fetcher",
    "path": "/de/apps-sdk/backend",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "backend",
    "pageTitle": "Server-Teil: Resolver, Fetcher und Jobs",
    "section": "@Fetcher",
    "anchor": "fetcher",
    "text": "@Fetcher markiert eine Methode, die eine fremde Quelle abruft. Die Plattform legt dafür einen eigenen Worker an, der genau einen Lauf gleichzeitig ausführt, damit begrenzte fremde Quellen geschont bleiben. Option Format Bedeutung key Muster mit App-Präfix, z. B. weather:{citySlug} Schlüssel im Cache; das erste Segment vor dem Doppelpunkt ist die App-Kennung. defaultValidity ISO-8601-Dauer, z. B. PT30M Wie lange die Daten als frisch gelten. defaultCacheUntil ISO-8601-Dauer, z. B. PT2H Wie lange der Cache sie im Notfall weiter ausliefert. Die Methode gibt FetcherResult<TPayload> zurück: payload , validUntil , cacheUntil und source (Herkunft, z. B. api:openweather ). Geplante Läufe und das Auffrischen über die API rufen eine Fetcher-Methode ohne Argumente auf. Deklariert sie Pflichtparameter, führt die Plattform sie als reinen Cache-Lader: geplante Läufe und Auffrischen lehnen sie ab, aufgerufen wird sie nur von der App selbst — aus getOrFetch im Resolver oder aus einem Job, der die Argumente kennt. Periodisches Abrufen ist deshalb Sache eines @JobHandler . getOrFetch prüft auf jedem Weg, auch beim Treffer im Cache, ob die Organisation die App aktiv installiert hat; sonst antwortet die Plattform mit App.NotInstalled (HTTP 409). Die beiden Zeitfenster ergeben drei Lagen: Lage Was passiert jetzt ≤ validUntil ausliefern, sonst nichts validUntil < jetzt ≤ cacheUntil sofort ausliefern und im Hintergrund auffrischen jetzt > cacheUntil der Aufrufer wartet synchron auf die Quelle Scheit"
  },
  {
    "id": "apps-sdk/backend#jobhandler",
    "path": "/de/apps-sdk/backend",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "backend",
    "pageTitle": "Server-Teil: Resolver, Fetcher und Jobs",
    "section": "@JobHandler",
    "anchor": "jobhandler",
    "text": "@JobHandler markiert eine Methode als wiederkehrenden, plattformweiten Job. Die Plattform legt einen Zeitplan an, den ein Cron abarbeitet, und ruft die Methode ohne Organisation im Kontext auf; der Handler fächert selbst über die installierten Organisationen. Option Format Bedeutung key string, z. B. pharmacies-on-duty-crawl-all-locations Eindeutig innerhalb der App; ohne App-Präfix. cron 5 Felder ( min hour dom month dow ), z. B. */30 * * * * Der Zeitplan. namespace string, Vorgabe App-Kennung Aus namespace leitet die Plattform die App ab — die mitgelieferten Apps setzen ihn gleich der App-Kennung. timeout ISO-8601-Dauer, z. B. PT5M Hartes Limit je Lauf. concurrency Zahl, Vorgabe 1 Gleichzeitige Läufe. Die Organisationen liefert listTenantsForApp() am Job-Queue-Token — nur die mit aktiver Installation dieser App; eine fremde App lässt sich nicht aufzählen. Ein key , der bereits als Fetcher registriert ist, wird mit Warnung übersprungen."
  },
  {
    "id": "apps-sdk/backend#die-token",
    "path": "/de/apps-sdk/backend",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "backend",
    "pageTitle": "Server-Teil: Resolver, Fetcher und Jobs",
    "section": "Die Token",
    "anchor": "die-token",
    "text": "Der Server-Teil bezieht jede übergreifende Fähigkeit über ein Token; die Plattform bindet dahinter eine Umsetzung, die alles automatisch mit der App-Kennung abgrenzt. Drei Token tragen Kontext, die übrigen Dienste. Token Was Sie bekommen Abgrenzung durch die Plattform APP_ID Die App-Kennung — APP_MANIFEST Das geparste Manifest — APP_VERSION Die Version aus dem Manifest — Token der App-Datenbank (im SDK nach dem Datenbankwerkzeug benannt) Die App-Datenbank: models , $transaction(fn) Nur die eigenen Tabellen im Schema apps_<id> ; ohne eigenes Schema schlägt der Zugriff fehl. APP_PERMISSION_GUARD assert(actor, action, resource?) , canAct(…) Rechteschlüssel mit der App-Kennung vorangestellt. APP_REQUEST_SCOPE traceId , correlationId , actorId , tenantId , isPlatformContext Nur lesend. APP_ERROR_FACTORY businessException , notFound , validationFailed , conflict , integrationFailed Liefert den Fehler, den Sie werfen. APP_EVENT_BUS emit(tx, eventName, payload) , contentChanged(tx, { tenantId, locationIds? }) Ereignisname app.<id>.<eventName> ; die Transaktion übergeben Sie explizit. contentChanged ist das feste Ereignis app.<id>.content.changed , siehe Geänderte Daten an die Displays melden . APP_LOGGER info , warn , error , debug , child Namensraum app.<id> . APP_CLOCK now() , nowMs() Zeitquelle, in Tests austauschbar. APP_CACHE get , set , invalidate Schlüssel app:<id>:… . APP_FETCHER_CACHE getOrFetch(key, fetcher, { ttl, staleWindow? }) , invalidate Read-Through-Cache; die Organi"
  },
  {
    "id": "apps-sdk/backend#appguardedfetch-ist-pflicht-für-nutzeradressen",
    "path": "/de/apps-sdk/backend",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "backend",
    "pageTitle": "Server-Teil: Resolver, Fetcher und Jobs",
    "section": "AppGuardedFetch ist Pflicht für Nutzeradressen",
    "anchor": "appguardedfetch-ist-pflicht-für-nutzeradressen",
    "text": "Sobald ein Server-Teil eine Adresse abruft, die jemand im Portal eingetragen hat (Feed, Kalender, Nachrichtenquelle aus einer Widget-Konfiguration), muss er fetchText nehmen — nie das globale fetch . Der Guard lässt nur http: und https: durch, prüft alle aufgelösten Adressen gegen die Sperrliste privater und reservierter Bereiche, wiederholt die Prüfung bei jeder Weiterleitung, kappt die Antwort im Strom und erzwingt ein Zeitlimit. Vorgaben: timeoutMs: 10000 , maxBytes: 2097152 (2 MiB), maxRedirects: 3 . Jeder Fehlschlag trägt einen maschinenlesbaren reason : invalid-url , blocked-scheme , blocked-address , dns-failure , too-many-redirects , too-large , timeout , http-error , network-error . isGuardedFetchError erkennt ihn strukturell, sodass ein Resolver den Grund als Klartext in seine Nutzlast heben kann. Für einen fest im Code verdrahteten Anbieter (etwa die Wetter-API) ist der Guard nicht gedacht — dort wählt keine Nutzereingabe das Ziel."
  },
  {
    "id": "apps-sdk/backend#geänderte-daten-an-die-displays-melden",
    "path": "/de/apps-sdk/backend",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "backend",
    "pageTitle": "Server-Teil: Resolver, Fetcher und Jobs",
    "section": "Geänderte Daten an die Displays melden",
    "anchor": "geänderte-daten-an-die-displays-melden",
    "text": "Ändert sich etwas, das ein Widget schon zeigt, meldet der Server-Teil das mit contentChanged . Die Plattform baut dann die Displays der Organisation neu, deren Programm die App enthält, und stößt die an, deren Inhalt sich wirklich geändert hat. Die Displays zeigen die Änderung nach wenigen Sekunden. Ohne Meldung kommt sie erst an, wenn der Snapshot aus einem anderen Grund neu entsteht, im schlimmsten Fall nach Stunden. Regel Warum In derselben Transaktion wie die Änderung Die Meldung geht über die Outbox: Sie kommt mindestens einmal an, aber nie für eine Änderung, die zurückgerollt wurde. Ohne eigene Datenbank null statt der Transaktion Eine App, die ihre Daten im Dateispeicher hält, meldet gleich nach dem Schreiben mit contentChanged(null, { tenantId }) . Die Meldung geht dann allein in die Outbox. Scheitert sie, bleibt die Änderung, und die Displays sehen sie mit der nächsten Frist ihrer Nutzlast. Nach Ändern und Löschen, nicht nach dem Anlegen Ein Resolver liest Daten über die Kennung, die ein Widget gewählt hat. Auf einen neuen Eintrag zeigt noch keines, und wer ihn in eine Szene nimmt, veröffentlicht sie, was den Neubau ohnehin auslöst. tenantId immer angeben Jobs laufen ohne Organisation im Kontext. Im Kontext einer Organisation lehnt die Plattform eine fremde ab. locationIds nur, wenn die Änderung ausschließlich an diesen Standorten sichtbar wird Die Kennungen sind Standorte der Organisation, nicht Datensätze der App. Kann ein Widget die Daten auch auf einem Display an"
  },
  {
    "id": "apps-sdk/backend#installieren-und-deinstallieren-die-lebenszyklus-punkte",
    "path": "/de/apps-sdk/backend",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "backend",
    "pageTitle": "Server-Teil: Resolver, Fetcher und Jobs",
    "section": "Installieren und Deinstallieren: die Lebenszyklus-Punkte",
    "anchor": "installieren-und-deinstallieren-die-lebenszyklus-punkte",
    "text": "Eine mit @AppLifecycle() markierte Klasse des Server-Teils darf onInstall(tenantId) und onUninstall(tenantId) tragen — höchstens eine Klasse je App, und beide Punkte müssen idempotent sein, weil eine Neuinstallation ihren Bestand wieder vorfindet. Wirft onInstall , gilt die Installation als gescheitert und die Organisation sieht den Fehler. Wirft onUninstall , bleibt die Deinstallation wirksam und der Fehler landet im Protokoll."
  },
  {
    "id": "apps-sdk/backend#ohne-laufende-plattform-testen",
    "path": "/de/apps-sdk/backend",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "backend",
    "pageTitle": "Server-Teil: Resolver, Fetcher und Jobs",
    "section": "Ohne laufende Plattform testen",
    "anchor": "ohne-laufende-plattform-testen",
    "text": "createMockAppContainer aus @szenera/apps-sdk/testing liefert je Token eine In-Memory-Umsetzung samt Aufzeichnung; mockWidgetResolverContext baut einen synthetischen Kontext, zunächst ohne location . Eine App mit player-location setzt ihn mit mockWidgetLocation() oder null ."
  },
  {
    "id": "apps-sdk/backend#weiterführendes",
    "path": "/de/apps-sdk/backend",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "backend",
    "pageTitle": "Server-Teil: Resolver, Fetcher und Jobs",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Datenzugriff, Rechte und Bedingungen — was dataAccess.reads an APP_TENANT_DATA bindet. Widget, Konfiguration und Vorschau — das Widget, das der Resolver bedient. Die Beispiel-Apps — welche App welche Bausteine trägt."
  },
  {
    "id": "apps-sdk/data-access",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "Datenzugriff, Rechte und Bedingungen",
    "anchor": "",
    "text": "Vier Blöcke des Manifests legen offen, was eine App mit Daten tut, wer sie im Team bedienen darf, was sie im Portal aufsperrt und welchen Bedingungen eine Organisation zustimmt: dataAccess , permissions , unlocks mit connects und legal . Die Tabellen nennen je Block Format, Grenzwerte und die Wirkung im Portal."
  },
  {
    "id": "apps-sdk/data-access#vier-blöcke-vier-fragen",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "Vier Blöcke, vier Fragen",
    "anchor": "vier-blöcke-vier-fragen",
    "text": "Block Frage der Organisation Wo das Portal es zeigt dataAccess Was tut die App mit meinen Daten — vor der Installation und danach? Abschnitt Daten & Zugriff der Store-Seite und der Installationsdialog permissions Wer in meinem Team darf die App bedienen? Rollen & Rechte — eingeklappt am Ende der Store-Seite, ausgeschrieben im Installationsdialog unlocks , connects Was schaltet die App im Portal frei, welches fremde Konto braucht sie? Schaltet im Portal frei legal Welchen Bedingungen stimme ich zu? Wichtiger Hinweis im Installationsdialog, Store-Seite, Fußzeile des Widgets Die Blöcke sind bewusst getrennt: permissions beschreibt, wer im Team die App bedienen darf — nicht, worauf die App zugreift . Das Portal sagt es der Organisation bei den Rechten der App: „Sie steuern, wer im Team die App bedienen darf.“ Worauf die App zugreift, steht getrennt unter Daten & Zugriff ."
  },
  {
    "id": "apps-sdk/data-access#dataaccess",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "dataAccess",
    "anchor": "dataaccess",
    "text": "Fehlt der Block, gilt die zurückhaltendste Lesart: Die App liest nichts, ruft nichts ab und behält ihre eigenen Daten beim Deinstallieren. Er ist deshalb nur dort nötig, wo es etwas zu sagen gibt. Feld Format Bedeutung Beschriftung im Portal reads Liste aus tenant-profile , tenant-branding , media-library , team , player-location , fleet-locations , location-hours ; Vorgabe leer Bestehende Bestände der Organisation, die die App liest. Liest vorhandene Daten — je Bestand Stammdaten der Organisation , Logo der Organisation , Mediathek , Team und Konten , Standort der Displays , Alle Standorte mit Anschrift und Lage , Öffnungszeiten der Standorte mit Feiertagen writes Liste, max. 8 Einträge, je Eintrag <bestand>:create-<sache> , ≤ 60 Zeichen; Vorgabe leer Schreib-Fähigkeiten auf fremde Bestände. Ändern und Löschen gibt es nicht; eine App kann nur beitragen. Fügt Daten hinzu — „Ändern oder Löschen ist ausgeschlossen. Eine App kann nur beitragen.“ external Liste, max. 8 Einträge mit name (1–60), purpose (1–200) und optional sends (≤ 200) Fremde Dienste, die die App erreicht: wen, wozu, und was dabei hinausgeht. Verbindungen nach außen — „Übertragen wird: …“ oder „Es werden keine Daten Ihrer Organisation übertragen.“ retention kept oder deleted ; Vorgabe kept Verbleib der App-eigenen Daten beim Deinstallieren. Beim Deinstallieren — mit den Namen der resources : „Standorte und Dienstpläne bleiben erhalten. Sie können sie danach endgültig löschen lassen.“ oder „Mit der Deinstallation"
  },
  {
    "id": "apps-sdk/data-access#der-standort-des-displays-player-location",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "Der Standort des Displays: player-location",
    "anchor": "der-standort-des-displays-player-location",
    "text": "Eine Szene läuft oft in allen Filialen einer Organisation. Mit player-location zeigt ein Widget an jedem Display dessen Ort, etwa das Wetter vor der Tür oder die Ortszeit, ohne dass jemand eine Szene je Filiale pflegt. Der Standort ist das, was die Organisation im Portal am Display als Standort hinterlegt hat. Er kommt nicht über APP_TENANT_DATA , sondern im Kontext des Widget-Resolvers als ctx.location , weil nur dort feststeht, für welches Display gerade aufgelöst wird: Feld Inhalt id Kennung des Standorts ( loc_… ), etwa um ihn mit Daten der App zu verbinden name Name des Standorts, wie die Organisation ihn führt („Filiale Altona“) city , countryCode Ort und Ländercode (ISO 3166-1, DE ) der Anschrift, je null ohne Anschrift. Straße und Postleitzahl werden nie übergeben. latitude , longitude Koordinaten des Standorts, gemeinsam null ohne Koordinaten timeZone Zeitzone des Standorts ( Europe/Berlin ) hours nur mit location-hours , siehe Öffnungszeiten Die Koordinaten stammen aus der Geokodierung der Anschrift und sind gebäudegenau. Wer sie an einen fremden Dienst schickt, rundet sie auf das, was der Zweck braucht, und sagt es in sends und in den Bedingungen: Die Wetter-App schickt zwei Nachkommastellen, etwa einen Kilometer, dieselbe Zelle wie ihr Cache-Schlüssel. Drei Zustände muss ein Resolver unterscheiden: ctx.location Wann fehlt Die App nennt player-location nicht unter reads , oder die Organisation hat dem Bereich mit der installierten Fassung noch nicht zugestimmt. Nur"
  },
  {
    "id": "apps-sdk/data-access#öffnungszeiten-location-hours",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "Öffnungszeiten: location-hours",
    "anchor": "öffnungszeiten-location-hours",
    "text": "Öffnungszeiten sind Stammdaten des Standorts. Die Organisation pflegt sie im Portal am Standort (Reiter Öffnungszeiten ), über PATCH /v1/locations/{id} oder das MCP-Werkzeug set_location_opening_hours , und jede App, die sie zeigt, liest dieselben. Der Bereich ist von player-location getrennt, weil Öffnungszeiten keine Ortsangabe sind: Eine App, die nur den Ort braucht, bekommt sie nicht, und eine eigene Zustimmung bleibt verständlich. Mit location-hours trägt jeder Standort, den die App liest ( ctx.location und location() , mit fleet-locations auch locations() ), zusätzlich hours : Feld Inhalt region Bundesland nach ISO 3166-2 ( DE-HH ) oder null . Ohne Bundesland gibt es keine Feiertage. weekly Wochenplan, je Wochentag ( mon … sun ) die Fenster { opens, closes } . Liegt closes nicht nach opens , endet das Fenster am Folgetag, gleiche Uhrzeiten heißen 24 Stunden. specialDays die Sondertermine, die den Zeitraum berühren, { startDate, endDate, label, closed, windows } holidays die gesetzlichen Feiertage im Zeitraum, { date, id, name } ; id ist stabil ( easter-monday ), damit eine Anzeige den Namen in ihrer Sprache nennen kann from , until der Zeitraum: 21 Tage ab dem heutigen Tag in der Zone des Standorts hours fehlt ohne den Bereich oder ohne Zustimmung, null heißt: Die App darf lesen, aber am Standort sind keine Zeiten gepflegt. Was an einem Tag gilt, rechnet openingDayOf aus @szenera/contracts/fleet mit der Rangfolge Sondertermin vor Feiertag (geschlossen) vor Wochenplan. Ä"
  },
  {
    "id": "apps-sdk/data-access#alle-standorte-fleet-locations",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "Alle Standorte: fleet-locations",
    "anchor": "alle-standorte-fleet-locations",
    "text": "Ein Abruf, der vorher wissen muss, für welche Orte er Daten holt, liest das Verzeichnis der Standorte: APP_TENANT_DATA.locations(tenantId) liefert alle Standorte der Organisation nach Namen geordnet, mit voller Anschrift ( street , postalCode , city , countryCode ), Koordinaten und Zeitzone, mit location-hours samt hours . Ein Resolver darf nicht nach außen, also braucht ein Job die Orte vorab, der Notdienst etwa die Postleitzahlen. Wer die Anschrift an einen fremden Dienst schickt, nennt in dataAccess.external genau das, was hinausgeht. Im Test liefert createMockAppContainer({ locationsMock: [...] }) die Standorte für location() und locations() ."
  },
  {
    "id": "apps-sdk/data-access#permissions",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "permissions",
    "anchor": "permissions",
    "text": "Die Installation trägt jedes Recht in den Rechte-Katalog der Organisation ein. Zwei Listen, dieselbe Form: Liste Wer sie trägt defaultRoles tenant Das Team der Organisation Rollen, die das Recht mit der Installation erhalten, z. B. ['OWNER', 'MANAGER', 'MEMBER'] platform Der Plattformbetrieb leer ( [] ) — kein Teammitglied erhält das Recht Jeder Eintrag ist ein Quartett: Feld Format Bedeutung key <ns>.<verb> oder <ns>.<resource>.<verb> ( ^[a-z][a-z0-9-]*(\\.[a-z][a-z0-9-]*)+$ ), z. B. pharmacies-on-duty.location.read Der Schlüssel, den der Server-Teil über APP_PERMISSION_GUARD prüft; die Plattform stellt ihm apps:<id>: voran. label Text, mindestens 1 Zeichen Beschriftung im Rechte-Katalog und im Installationsdialog. description Text, mindestens 1 Zeichen Ein Satz, was das Recht erlaubt. defaultRoles Liste von Rollen Startbelegung; die Organisation kann sie im Team-Bereich ändern. Der Helfer definePermission(key) prüft nur das Format eines Schlüssels und gibt ihn unverändert zurück; die mitgelieferten Apps schreiben die Quartette direkt ins Manifest. Neue Rechte in einer neuen Fassung übernimmt die Plattform nicht still: Sobald die Fassung als stable veröffentlicht ist, steht die Installation auf Update wartet , die Widgets der App lassen sich im Szenen-Editor nicht einfügen, und der Reiter Update der App zeigt die hinzugekommenen Rechte mit Annehmen und Ablehnen . Ein Aufruf, der die App vorher nutzt, scheitert mit Grant.PendingReacceptance (HTTP 409); eine Zustimmung zu einer"
  },
  {
    "id": "apps-sdk/data-access#unlocks-und-connects",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "unlocks und connects",
    "anchor": "unlocks-und-connects",
    "text": "Eine App muss keine Fläche mitbringen. unlocks und connects beschreiben den zweiten Sinn einer App: nicht etwas liefern, sondern etwas aufsperren. Die Funktion lebt in der Plattform; die App ist das Tor davor. Feld Format Katalog der Plattform Löst Zustimmung aus? unlocks max. 8 Schlüssel, kleingeschrieben mit Bindestrichen, ≤ 40 Zeichen video-walls ( Leinwände ), library-places ( Orte in der Mediathek ) Nein — die Zeile gibt der Organisation etwas, sie nimmt ihr nichts und liest nichts von ihr. connects max. 4 Schlüssel, kleingeschrieben mit Bindestrichen, ≤ 40 Zeichen heute microsoft-graph Die Verbindung selbst stellt die Organisation im Portal her; Erwerb, Erneuerung und Widerruf der Vollmacht liegen einmal in der Plattform. Die App sieht weder Vollmacht noch Zugriffs-Wort. Ein Schlüssel, der nicht im Katalog steht, lässt die App nicht laden — sonst könnte eine App eine Funktion oder Anbindung versprechen, hinter der nichts steht. Beide Felder werden ausgelassen statt leer gesetzt: Ein leeres Feld verschöbe den kanonischen Hash jeder bestehenden Fassung. Die beiden Tor-Apps zeigen die Muster: video-wall trägt unlocks: ['video-walls'] , onedrive trägt unlocks: ['library-places'] und connects: ['microsoft-graph'] . Der Store-Eintrag zeigt die Freischaltung im Reiter Daten & Zugriff unter Schaltet im Portal frei . Eine App, die nur eine Kernfunktion der Plattform aufsperrt, steht im Store nicht als App: video-wall erscheint dort als Kachel Kernfunktion · Leinwand ( coreFeatur"
  },
  {
    "id": "apps-sdk/data-access#legal",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "legal",
    "anchor": "legal",
    "text": "Feld Pflicht Bedeutung disclaimerKey ja Schlüssel der Fassung, auf die sich eine Zustimmung bezieht ( <gruppe>.bedingungen.v1 ). Eine inhaltliche Änderung des Hinweises hebt ihn ( v1 → v2 ). disclaimerSummary ja, max. 500 Zeichen Kurzfassung in Klartext; Installationsdialog ( Wichtiger Hinweis ), Store-Eintrag und Widget-Fußzeile zeigen sie unverändert. Sie stellt die Fassung dar, eine neue Formulierung bei gleichem Schlüssel verlangt keine Zustimmung. termsPath nein Pfad des Volltexts auf der öffentlichen Website, ≤ 200 Zeichen, nur Kleinbuchstaben, Ziffern, Bindestriche und Schrägstriche ( /app-bedingungen/<gruppe> ). Das Portal setzt seine Basis davor und verlinkt ihn als Vollständige Bedingungen der App . Ohne termsPath zeigt der Dialog allein die Kurzfassung. requireAcceptanceAt Vorgabe install install : Zustimmung bei der Installation und danach mit jeder neuen Fassung des Hinweises. upgrade : Zustimmung bei jeder neuen Fassung der App, auch bei gleichem Hinweis. never : keine Zustimmung. Die mitgelieferten Apps tragen install . Apps mit gleichem Rechtsthema zeigen auf dasselbe Blatt. Die mitgelieferten Apps verwenden sieben Gruppen: Gruppe ( termsPath ) Apps /app-bedingungen/fremdinhalte webpage , youtube , stream , embed /app-bedingungen/eigene-inhalte notice-board , ticker , qr-code , menu-board /app-bedingungen/wetter weather /app-bedingungen/nachrichten rss , tagesschau , bbc , bild , ndr , spiegel , deutschlandfunk /app-bedingungen/verbindungen calendar , onedrive"
  },
  {
    "id": "apps-sdk/data-access#den-stand-einer-installation-abfragen",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "Den Stand einer Installation abfragen",
    "anchor": "den-stand-einer-installation-abfragen",
    "text": "Was aus Manifest, Rechten und Bedingungen geworden ist, liest eine Organisation über zwei Routen ihres eigenen Bestands. Beide verlangen einen Ausweis der Organisation, aber kein eigenes Recht : Eine gültige Anmeldung genügt, und ein API-Schlüssel erreicht sie unabhängig davon, welche Rechte in seinem scope stehen. Ein Betreiber-Ausweis erreicht sie nicht — beide antworten immer über genau eine Organisation und weisen einen Ausweis ohne Organisation mit 403 Permission.Denied ab. Route Was die Antwort enthält GET /v1/me/apps Unter apps je Installation eine Zeile: grantId (Kennung tag_… ), appId , installedVersion , installedAt , dazu die Anzeigefelder der installierten Fassung ( displayName , tagline , iconUrl , coverUrl , category , tier , bundleSizeBytes ), die Datenbereiche als resources und sceneCount — in wie vielen Szenen die App tatsächlich steckt. Drei Zustände stehen nebeneinander: status ( active oder pending-reacceptance ), platformStatus ( active , beta , disabled ) und effect mit status ( active , locked ) und reason ( ok , pro-required , premium-required , booking-required , exempt ). Deinstallierte Apps fehlen in der Liste, abgeschaltete und nicht mehr im Paket enthaltene bleiben darin stehen, sie sind Bestand. Name, Einzeiler und Datenbereiche stehen in der Sprache aus locale oder Accept-Language , wo die Fassung sie trägt. GET /v1/me/apps/{appId}/installed Die Seite der installierten App: entry beschreibt die installierte Fassung, scenes die Szenen, die sie ve"
  },
  {
    "id": "apps-sdk/data-access#was-die-organisation-sieht",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "Was die Organisation sieht",
    "anchor": "was-die-organisation-sieht",
    "text": "Der Installationsdialog trägt den Untertitel „Prüfen Sie, was die App mit Ihren Daten tut, und bestätigen Sie die Installation.“ Er zeigt den Block Daten & Zugriff , führt darunter die Rechte als Rollen & Rechte mit ihrer Anzahl, zeigt die Kurzfassung der Bedingungen unter Wichtiger Hinweis , verlinkt den Volltext als Vollständige Bedingungen der App und verlangt das Häkchen Ich habe die Rechte und die Bedingungen der App gelesen und stimme ihnen zu. , bevor Installieren wirkt. Dieselben Angaben stehen schon im Eintrag der App im Store, im Reiter Daten & Zugriff . Wer abwägt, ob er die App überhaupt will, soll ihre Bedingungen vor dem Klick lesen können. Der Reiter nennt, welche eigenen Bereiche die App anlegt (aus resources ), welche Bestände sie liest, was sie hinzufügt, welche Verbindungen sie nach außen hält und was dabei übertragen wird und was beim Deinstallieren mit ihren Daten geschieht. Darunter stehen Datenschutz mit den Empfängern aus dataAccess.external und die Kurzfassung der Bedingungen mit dem Verweis auf den Volltext, den das Portal aus legal.termsPath setzt. Was das Manifest auslässt, sagt das Portal ausdrücklich („Diese App greift auf keine Ihrer bestehenden Daten zu.“), statt die Karte wegzulassen. Den Eintrag liefert GET /v1/me/apps/store/{appId} als StoreEntryView , fertig entschieden: die Lage der App für die Organisation, genau eine Handlung und den Grund mit seinen Zahlen. Die Sätze dazu bildet das Portal, gerechnet wird nur im Server. Die Sicht der Or"
  },
  {
    "id": "apps-sdk/data-access#weiterführendes",
    "path": "/de/apps-sdk/data-access",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "data-access",
    "pageTitle": "Datenzugriff, Rechte und Bedingungen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Das Manifest — die vier Blöcke im Zusammenhang aller Felder. Widgets konfigurieren — was der Widget-Katalog über die Konfiguration eines Widgets preisgibt, und wie ein Client sie prüft. Was ist eine App, und wie kommt sie auf den Bildschirm? — die Zustände einer Installation im Lebensweg. Apps im Handbuch — die Sicht der Organisation."
  },
  {
    "id": "apps-sdk/signing-and-catalog",
    "path": "/de/apps-sdk/signing-and-catalog",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "signing-and-catalog",
    "pageTitle": "Signieren und der Weg in den Katalog",
    "section": "Signieren und der Weg in den Katalog",
    "anchor": "",
    "text": "Ein Release friert Version, Manifest und Bytes einer App ein und wandert durch die Kanäle bis stable . Diese Seite nennt, was an einem Release liegt, wie eine Fassung als signiertes Artefakt in den Katalog kommt, die Regeln der Kanäle und die Aufrufe und Befehle, mit denen der Plattformbetrieb den Katalog führt. Jede Fassung entsteht genau einmal: Die CI baut sie aus ihrem Tag als signiertes, unveränderliches Artefakt <appId>-<version>.szapp mit Manifest, den Bündeln für Anzeige und Editor, Bildern und Texten für den Store und den Release-Notizen, und jede Umgebung holt dieses eine Artefakt aus der Registry. Dieselbe Fassung trägt damit in jeder Umgebung dieselben Bytes."
  },
  {
    "id": "apps-sdk/signing-and-catalog#was-an-einem-release-liegt",
    "path": "/de/apps-sdk/signing-and-catalog",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "signing-and-catalog",
    "pageTitle": "Signieren und der Weg in den Katalog",
    "section": "Was an einem Release liegt",
    "anchor": "was-an-einem-release-liegt",
    "text": "Im Objektspeicher liegt eine Fassung unter platform/apps/<appId>/<version>/ : Ablage Inhalt bundle.zip Das Artefakt unverändert. Seine sha256 ist der Digest seiner Schicht in der Registry. manifest.json Das Manifest der Fassung in kanonischem JSON — der Katalog speichert es eingebettet, sein Hash ist der manifestHash . runtime/ Die entpackten Dateien des runtime-Bündels, das der Player lädt. authoring/ Das entpackte authoring-Bündel mit dem Konfigurationsformular für den Szenen-Editor, falls die App eines trägt. store/ Symbol und Aufnahmen für den Store. Am Release stehen dazu sizeBytes und sha256 (64 hexadezimale Zeichen in Kleinschreibung) der abgelegten Bytes und die Herkunft: source ( registry für ein Artefakt aus der Registry, seed-placeholder für eine Zeile ohne Bytes, die der Seed ohne Zugang zur Registry angelegt hat), unter artifact der Hash des Index, der Release-Schlüssel, dessen Signatur geprüft ist, die Signatur selbst und der Digest in der Registry, dazu importedAt und importedById . Manifest und Bytes sind nach dem Holen unveränderlich; das einzige nachträglich änderbare Feld sind die Notizen. Sie stehen unter notes je Zielgruppe — customer („Neu in dieser Fassung“), operator und developer — und je Sprache ( de-DE , en-US , …), wie der Index des Artefakts sie mitbringt. Daneben trägt jedes Release weiter die eine Notiz von früher, releaseNotes (max. 2000 Zeichen): den Text für den Betrieb in de-DE , sonst den für Kunden. Geräte und Clients erreichen eine Fassun"
  },
  {
    "id": "apps-sdk/signing-and-catalog#die-beiden-kataloge-über-alle-apps",
    "path": "/de/apps-sdk/signing-and-catalog",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "signing-and-catalog",
    "pageTitle": "Signieren und der Weg in den Katalog",
    "section": "Die beiden Kataloge über alle Apps",
    "anchor": "die-beiden-kataloge-über-alle-apps",
    "text": "Neben der einzelnen Fassung liefert die Schnittstelle zwei Übersichten über den ganzen Katalog. Beide nehmen je App die geltende Fassung im Kanal stable — die mit dem jüngsten Eintritt dorthin, nicht die höchste Nummer — und zerlegen deren Manifest: die eine in Widget-Definitionen, die andere in Ressourcentypen. Der Widget-Katalog führt zusätzlich die sechs eingebauten Bausteine (Text, Bild, Video, Form, Grafik, Dokument); sie gehören zu keiner App und tragen deshalb die reservierte Kennung core . Was nur in canary oder beta steht, kommt in keiner von beiden vor, und beide antworten unabhängig davon, was eine Organisation installiert hat: Sie beschreiben das Angebot, nicht den Bestand. Route Was die Antwort enthält GET /v1/widget-catalog Unter entries je Widget ein Eintrag mit appId , version und der vollständigen definition : id , name , description , das configSchema als JSON-Schema mit Feldtexten und Beispielen (siehe Widgets konfigurieren ), size , noResolver und der preview -Block mit defaultConfig , mockData , thumbnailUrl , variants und states . Die beiden Tor-Apps ohne Widget tragen nichts bei; die sechs eingebauten Bausteine stehen unter appId: core mit darin. GET /v1/resource-type-catalog Unter entries je Ressourcentyp ein Eintrag mit appId , version und der definition : id , slug , name , description , listEndpoint , authoringPath und die drei Schemata baseSchema , createSchema und updateSchema . Nur Apps mit resources im Manifest tragen etwas bei — unter den mitge"
  },
  {
    "id": "apps-sdk/signing-and-catalog#signatur-die-aussage-über-die-fassung",
    "path": "/de/apps-sdk/signing-and-catalog",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "signing-and-catalog",
    "pageTitle": "Signieren und der Weg in den Katalog",
    "section": "Signatur: die Aussage über die Fassung",
    "anchor": "signatur-die-aussage-über-die-fassung",
    "text": "Signiert wird in der CI, nie auf einem Server: Die CI unterschreibt die Aussage „diese App, diese Fassung, dieses Manifest, dieser Index“ mit dem Ed25519-Release-Schlüssel jeder Welt ( app-release-staging für Entwicklung und Staging, app-release-prod für Produktion). Beim Holen prüft die Schnittstelle die Signatur ihrer eigenen Welt gegen deren öffentlichen Schlüssel. Ohne gültige Signatur ihrer Welt kommt keine Fassung in den Katalog, einen Rückfall auf die sha256 allein gibt es nicht. Wer ein Release holt, signiert nichts. Am Gerät prüft die Fernwartung jedes Artefakt, bevor sie etwas entpackt: Der sha256-Wert des geladenen Archivs muss dem am Release entsprechen, dann prüft sie die signierte Aussage gegen den Anker app-release-<welt> im Abbild, danach Manifest und jede Datei gegen den Index, und sie entpackt nur runtime/ , abgesichert gegen Pfade, die aus dem Zielordner hinausführen. Fehlt der Anker, lehnt sie jedes Artefakt ab. Ein Archiv ohne Index ist kein Artefakt, und die Fernwartung nimmt es nicht an. Die Angabe signing im Manifest ändert daran nichts, sie bleibt nur, weil sie in den Manifesten veröffentlichter Fassungen steht."
  },
  {
    "id": "apps-sdk/signing-and-catalog#ein-release-holen",
    "path": "/de/apps-sdk/signing-and-catalog",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "signing-and-catalog",
    "pageTitle": "Signieren und der Weg in den Katalog",
    "section": "Ein Release holen",
    "anchor": "ein-release-holen",
    "text": "Ein Release entsteht durch den Import eines Artefakts aus der Registry. Die CI meldet jede Fassung, die sie abgelegt hat, Staging und Produktion. Die Fassungen der Registry listet die Schnittstelle nicht auf: Wer eine Fassung von Hand holt, nennt ihre Version selbst. POST /v1/platform/app-catalog/imports mit dem Rumpf { \"appId\": \"…\", \"version\": \"…\" } holt die Fassung. Optional nennt digest den Digest des OCI-Manifests ( sha256:… ), den der Aufrufer erwartet; die CI schickt ihn immer mit. Die Schnittstelle prüft Manifest und Schicht in der Registry, dann das Archiv: jede Datei gegen ihre Prüfsumme im Index, das Manifest gegen das Schema und seinen Hash, Kennung und Fassung, und die Signatur ihrer Welt. Erst danach legt sie die Bytes ab und das Release im Kanal canary an. Die Antwort ist { \"release\": …, \"created\": true } mit 201 , wenn die Fassung neu im Katalog steht. Lag sie dort schon mit demselben Artefakt, antwortet der Aufruf mit 200 und created: false , der Aufruf lässt sich also wiederholen. Einen Platzhalter ohne Bytes mit demselben Manifest füllt der Import, Kanal und Freigaben der Zeile bleiben. Die Notiz des Seeds, die nur den Platzhalter beschreibt, ersetzt er dabei durch die Notizen aus dem Index, außer jemand hat sie geändert. Dabei zieht der Import die Notizen nach: Fehlt einer Fassung, die er schon im Katalog antrifft, eine Zielgruppe oder Sprache, die der Index ihres Artefakts trägt, ergänzt er sie und hält das als notes-updated mit seinem Akteur fest. So beko"
  },
  {
    "id": "apps-sdk/signing-and-catalog#die-kanäle",
    "path": "/de/apps-sdk/signing-and-catalog",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "signing-and-catalog",
    "pageTitle": "Signieren und der Weg in den Katalog",
    "section": "Die Kanäle",
    "anchor": "die-kanäle",
    "text": "Ein neues Release startet immer in canary ; den Startkanal wählt niemand. Jeder Aufstieg geht genau eine Stufe weiter, ein Sprung ist nicht möglich. Nach stable hebt keine Person allein: Eine Person fordert an, eine zweite bestätigt, auch bei der Rücknahme. Welche Fassung in stable gilt, entscheidet der jüngste Eintritt dorthin, nicht die höchste Nummer. Kanal Beschriftung im Betreiber-Portal Bedeutung canary Canary Frisch geholt — erster Erprobungs-Kanal. beta Beta Erweiterte Erprobung. stable Stabil Die für Organisationen empfohlene Fassung. deprecated Veraltet Zurückgestuft, aber noch nutzbar. retired Ausgemustert Außer Betrieb; Endzustand mit Auslauffrist. Aufruf Rumpf Regel POST …/releases/{id}/promote { \"toChannel\": \"beta\" } , nach stable mit \"reason\": \"…\" canary → beta hebt immer sofort. Nach stable hebt der Aufruf sofort, wenn die Plattform keine zweite Person verlangt ( releaseApproval.apps auf off , die Vorgabe). Verlangt sie eine ( stable ), legt der Aufruf mit reason (10–500 Zeichen) eine Anfrage an, Antwort { \"approval\": … } statt { \"release\": … } , ohne reason antwortet er AppRelease.SecondPersonRequired (HTTP 409). Je Fassung und Vorgang wartet höchstens eine Anfrage ( ReleaseApproval.AlreadyOpen , HTTP 409), und sie gilt sieben Tage. Alles andere: Channel.InvalidTransition (HTTP 409). POST …/releases/{id}/restore { \"reason\": \"…\" } , Pflicht (10–500 Zeichen) Nimmt eine Fassung aus deprecated oder eine abgelöste in stable dorthin zurück, sofort oder, wenn die Pl"
  },
  {
    "id": "apps-sdk/signing-and-catalog#die-historie-einer-app",
    "path": "/de/apps-sdk/signing-and-catalog",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "signing-and-catalog",
    "pageTitle": "Signieren und der Weg in den Katalog",
    "section": "Die Historie einer App",
    "anchor": "die-historie-einer-app",
    "text": "Jede Bewegung eines Release steht als Eintrag in der Historie seiner App: Anlage, Holen aus der Registry, jede Freigabe in einen Kanal, Zurückstufen, Rücknahme, Ausmustern, Auslauffristen, geänderte Notizen und jeder Schritt einer Anforderung an eine zweite Person ( promotion-requested , promotion-approved , promotion-withdrawn , promotion-expired ). Hat eine zweite Person bestätigt, ist sie der Akteur, und requestedById nennt die Person, die angefordert hat. Jeder Eintrag nennt Kanal von und nach, den Zeitpunkt und den Akteur — einen Menschen im Betrieb ( operator mit Kennung), die CI ( ci ), den Seed einer Umgebung ( seed ), die Plattform selbst ( auto ), die Stunde null ( zero-hour ) oder legacy für das, was vor der Historie geschah und aus den Zeitpunkten der Releases rekonstruiert ist. Aufruf Für wen Was er liefert GET /v1/platform/app-catalog/apps/{appId}/history Betrieb ( platform.app-catalog.list ) jeden Eintrag, neueste zuerst, in Seiten mit limit und cursor GET /v1/apps/{appId}/history?locale=en-US jede angemeldete Organisation die Fassungen, die stable erreicht haben, jüngste zuerst: Kennung ( releaseId , dieselbe wie an Installation, Nachweis und Update-Entscheidung), Nummer, seit wann sie allen offensteht ( stableSince ), „Neu in dieser Fassung“ in der erbetenen Sprache oder ersatzweise auf Deutsch ( note , null ohne Text) und ob sie ausgemustert ist. Für eine App, die der Betrieb für die Organisation abgeschaltet hat, antwortet der Aufruf mit App.NotFound (HTTP "
  },
  {
    "id": "apps-sdk/signing-and-catalog#als-befehl-der-cli",
    "path": "/de/apps-sdk/signing-and-catalog",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "signing-and-catalog",
    "pageTitle": "Signieren und der Weg in den Katalog",
    "section": "Als Befehl der CLI",
    "anchor": "als-befehl-der-cli",
    "text": "Die CLI bildet den Lebenszyklus nach dem Holen in neun Befehlen ab; das Holen selbst kennt sie nicht — die Umgebung gleicht sich alle zehn Minuten mit der Registry ab, die CI beschleunigt es mit dem Aufruf oben. Die Anfragen an eine zweite Person führt die Gruppe approval ( list , approve , withdraw ). Befehl Zweck app-release list Die Releases des App-Katalogs, filterbar mit --app und --channel . app-release set-notes Release-Notizen ändern. app-release promote Nach beta oder stable heben mit --channel . Nach stable hebt der Befehl sofort, oder er legt eine Anfrage an eine zweite Person an, wenn die Plattform sie verlangt, --reason ist dann Pflicht. app-release restore Die Rücknahme einer veralteten oder abgelösten Fassung nach stable, --reason ist Pflicht. Je nach Einstellung sofort oder als Anfrage. app-release demote Der Weg zurück, wenn ein stabiles Release doch nicht stabil war: --channel=deprecated — der einzige erlaubte Schritt, von stable aus. app-release retire Ein Release außer Betrieb nehmen; --reason und --grace=regular|security|immediate sind Pflicht, --advisory trägt den Sicherheitsvermerk und gehört genau zu --grace=security . app-release grace-period Die Auslauffrist verlängern ( --months oder --until=<jjjj-mm-tt> ) oder sofort beenden ( --immediately mit --reason ) — genau eines davon. app-release delete-artifact Die Bytes einer Fassung löschen, --yes ist Pflicht. app-release cleanup Die Bytes abgelöster Fassungen einer App aufräumen, --dry-run oder --yes , "
  },
  {
    "id": "apps-sdk/signing-and-catalog#was-die-organisation-davon-sieht",
    "path": "/de/apps-sdk/signing-and-catalog",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "signing-and-catalog",
    "pageTitle": "Signieren und der Weg in den Katalog",
    "section": "Was die Organisation davon sieht",
    "anchor": "was-die-organisation-davon-sieht",
    "text": "Neue stabile Fassung ohne neue Rechte und ohne geänderte Bedingungen: Bestehende Installationen übernehmen sie automatisch; die früheren Entscheidungen im Reiter Update der App zeigen den Eintrag still übernommen . Neue stabile Fassung mit neuen Rechten oder geänderten Bedingungen: Die Installation steht auf Update wartet ; die Widgets der App lassen sich im Szenen-Editor nicht einfügen, bis die Organisation im Reiter Update annimmt (siehe Datenzugriff, Rechte und Bedingungen ). Veröffentlichen einer Szene: Hält ein Widget eine Fassung fest, die es nicht gibt, die ausgemustert und deren Auslauffrist vorbei ist, oder gehört es zu einer App, die die Organisation nicht installiert hat, weist die Plattform das Veröffentlichen ab und nennt den Grund in einem Satz. Eine ausgemusterte Fassung in laufender Frist ist ein Hinweis. Ausgemustertes Release: Für neue Installationen nicht mehr wählbar. Wer genau diese Fassung installiert hat, bekommt die Nachfolgerin, die niedrigste stabile Fassung darüber. Verlangt sie nichts Neues und hat die Organisation sie nicht abgelehnt, übernimmt die Plattform sie sofort. Sonst steht die Installation auf Update wartet , mit dem Ende der Auslauffrist als Frist, und danach stellt die Plattform um. Ohne Nachfolgerin läuft die App unverändert bis zum Fristende weiter."
  },
  {
    "id": "apps-sdk/signing-and-catalog#was-es-heute-nicht-gibt",
    "path": "/de/apps-sdk/signing-and-catalog",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "signing-and-catalog",
    "pageTitle": "Signieren und der Weg in den Katalog",
    "section": "Was es heute nicht gibt",
    "anchor": "was-es-heute-nicht-gibt",
    "text": "Der Katalog ist rein kuratorisch: Die CI baut und signiert jede Fassung, der Plattformbetrieb holt sie in den Katalog und steuert ihren Lebenszyklus. Es gibt keine Entwickler-Einreichung, keine Prüf-Warteschlange und keine Aussage über Prüfzeiten oder Kriterien. Die 26 mitgelieferten Apps entstehen im Quellbaum der Plattform, werden aus ihrem Tag gebaut, als Artefakt signiert und in jede Umgebung geholt. Wer eine App vorbereitet, tut das mit dem Plattformbetrieb — die Seiten dieses Bereichs beschreiben den Vertrag, gegen den sie geschrieben wird."
  },
  {
    "id": "apps-sdk/signing-and-catalog#weiterführendes",
    "path": "/de/apps-sdk/signing-and-catalog",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "signing-and-catalog",
    "pageTitle": "Signieren und der Weg in den Katalog",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Was ist eine App, und wie kommt sie auf den Bildschirm? — der Lebensweg im Bild. Das Manifest — signing und die übrigen Felder. Befehle für den Plattformbetrieb — app-release und app-status im Werkzeug szenera-ops ."
  },
  {
    "id": "apps-sdk/example-apps",
    "path": "/de/apps-sdk/example-apps",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "example-apps",
    "pageTitle": "Die Beispiel-Apps",
    "section": "Die Beispiel-Apps",
    "anchor": "",
    "text": "Die mitgelieferten Apps sind die realen Konsumenten des Vertrags aus diesem Bereich. Die erste Tabelle ordnet jede App nach Profil, Bausteinen des Server-Teils, Ressourcen und Signatur; die zweite nennt Kategorie, Klasse und die Gruppe der Bedingungen. Drei Apps sind die Referenzen, an denen sich eine neue App orientiert."
  },
  {
    "id": "apps-sdk/example-apps#drei-profile-drei-referenzen",
    "path": "/de/apps-sdk/example-apps",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "example-apps",
    "pageTitle": "Die Beispiel-Apps",
    "section": "Drei Profile, drei Referenzen",
    "anchor": "drei-profile-drei-referenzen",
    "text": "Profil Was dazukommt Referenz Seiten Nur Widget Ein Widget mit noResolver: true , runtime- und authoring-Bündel, kein Server-Teil, keine Ressourcen, keine App-Datenbank. clock — die kleinste vollständige App: ein Manifest, ein Widget, zwei Bündel. Widget, Konfiguration und Vorschau Plus Fetcher Ein Server-Teil mit @WidgetResolver und @Fetcher gegen eine fremde Quelle; keine App-Datenbank. weather — die Stadt lebt in der Widget-Konfiguration, der Fetcher holt Lage und Vorhersage in den Cache. Server-Teil: Resolver, Fetcher und Jobs Plus Ressourcen und Datenbank Ressourcentypen, die die Organisation im Portal pflegt, ein eigenes Schema apps_<id> , bei Bedarf ein @JobHandler . pharmacies-on-duty — trägt jeden Baustein des SDK: Resolver, Fetcher, Job alle 30 Minuten, zwei Ressourcen, legal , signing: 'required' . Das Manifest , Datenzugriff, Rechte und Bedingungen Die Stufen sind nicht streng kumulativ: menu-board trägt Ressourcen und eigenes Schema, holt aber nichts aus dem Netz — „fremde Quelle“ und „eigene Daten“ sind zwei unabhängige Bausteine. pharmacies-on-duty ist die eine App, die beide trägt. Eine neue App klont die Struktur der nächstähnlichen Referenz."
  },
  {
    "id": "apps-sdk/example-apps#alle-apps",
    "path": "/de/apps-sdk/example-apps",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "example-apps",
    "pageTitle": "Die Beispiel-Apps",
    "section": "Alle Apps",
    "anchor": "alle-apps",
    "text": "Die fünf Marken-Apps bbc , bild , ndr , spiegel und deutschlandfunk erben Widget, Server-Teil und Darstellung von tagesschau ; sie tragen nur ein eigenes Manifest und ihre Identität (Kennung, Beschriftung, Akzentfarbe, Thema, Schrift). Die Tor-Apps onedrive und video-wall bringen kein Widget mit. App Widget Profil Bausteine des Server-Teils Ressourcen / Datenbank Signatur Besonderheit clock („Uhr“) clock nur Widget — — optional Referenz für das Profil „nur Widget“; Zeitzone je Instanz übersteuerbar. webpage („Webseite“) iframe nur Widget — — optional Einzige App mit Abruf einer fremden Adresse ohne Server-Teil: der Player lädt die Seite selbst. notice-board („Aushang“) rich-text nur Widget — — optional Sicheres Autoren-Markup: ein Markdown-Teil wird zu einem typisierten Block-Modell, nie zu HTML-Text. countdown („Countdown“) countdown nur Widget — — optional Zählt auf einen Ziel-Zeitpunkt herunter; Ziel, Einheiten und Abgelaufen-Text stehen in der Konfiguration. countup („Tage-Zähler“) countup nur Widget — — optional Zählt seit einem Ereignis hoch und hebt Meilensteine hervor. world-clock („Weltzeit“) world-clock nur Widget — — optional Mehrere Zeitzonen nebeneinander als Liste gleichrangiger Kacheln. word-clock („Wort-Uhr“) word-clock nur Widget — — optional Zeit als Satz statt als Zahl, Raster-Darstellung mit Sprachwahl. ticker („Lauftext“) ticker nur Widget — — optional Einzige App mit Dauerbewegung; auf eingefrorenen Flächen ein Standbild der ersten Meldung. qr-code („QR-"
  },
  {
    "id": "apps-sdk/example-apps#kategorie-klasse-und-bedingungen",
    "path": "/de/apps-sdk/example-apps",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "example-apps",
    "pageTitle": "Die Beispiel-Apps",
    "section": "Kategorie, Klasse und Bedingungen",
    "anchor": "kategorie-klasse-und-bedingungen",
    "text": "category ist die eine Ordnungsachse der Plattform: Sie bestimmt den Filter im Store und den Abschnitt der Apps in der Palette des Szenen-Editors. tier ist die Klasse: standard trägt im Store keine Marke, pro die Marke Pro , addon die Marke Add-on und wird zugebucht. Wie eine App ihren Inhalt in ihrem Rahmen zeichnet, entscheidet ein Konfigurationsfeld, bei pharmacies-on-duty etwa vier layout -Werte, bei clock drei composition -Werte. App category tier legal -Gruppe clock zeit standard — countdown zeit standard — countup zeit standard — world-clock zeit standard — word-clock zeit standard — weather information standard wetter rss information pro nachrichten tagesschau information pro nachrichten bbc , bild , ndr , spiegel , deutschlandfunk information pro nachrichten calendar information pro verbindungen webpage web-video standard fremdinhalte youtube web-video standard fremdinhalte stream web-video standard fremdinhalte embed web-video standard fremdinhalte notice-board gestaltung pro eigene-inhalte ticker gestaltung standard eigene-inhalte qr-code gestaltung standard eigene-inhalte pharmacies-on-duty geschaeft pro notdienst opening-hours geschaeft pro oeffnungszeiten menu-board geschaeft pro eigene-inhalte onedrive geschaeft standard verbindungen video-wall geschaeft addon — Apps ohne tier im Manifest tragen die Vorgabe standard . Was die Vorschau-Varianten einer App im Store bewirken, steht unter Widget, Konfiguration und Vorschau ."
  },
  {
    "id": "apps-sdk/example-apps#was-hier-nicht-steht",
    "path": "/de/apps-sdk/example-apps",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "example-apps",
    "pageTitle": "Die Beispiel-Apps",
    "section": "Was hier nicht steht",
    "anchor": "was-hier-nicht-steht",
    "text": "Text, Bild, Video, Form, Grafik und Dokument sind keine Apps, sondern Kern-Widgets: Sie tragen die reservierte Kennung core , erscheinen nicht im Store, brauchen keine Installation und durchlaufen weder Release noch Signatur. Wer ein Widget baut, das ohne Datenquelle, ohne Rechte und ohne eigenes Produktversprechen auskommt, baut vermutlich einen Grundbaustein und keine App. Die Nutzersicht jeder App — was sie zeigt, wie sie eingerichtet wird, welche Bedingungen sie mitbringt — steht im Handbuch: Apps im Handbuch ."
  },
  {
    "id": "apps-sdk/example-apps#weiterführendes",
    "path": "/de/apps-sdk/example-apps",
    "area": "apps-sdk",
    "areaTitle": "Apps-SDK",
    "page": "example-apps",
    "pageTitle": "Die Beispiel-Apps",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Widget, Konfiguration und Vorschau — Formate, Vorschau-Varianten und Vorschau-Zustände am Uhr-Beispiel. Server-Teil: Resolver, Fetcher und Jobs — die Bausteine der Fetcher- und Daten-Apps. Apps im Handbuch — die Sicht der Organisation je App."
  },
  {
    "id": "changes/index",
    "path": "/de/changes",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "index",
    "pageTitle": "Änderungen",
    "section": "Änderungen",
    "anchor": "",
    "text": "Wo Änderungen an der API entstehen, was ein Bruch ist und wie Sie die Spezifikation gegen Ihren Stand vergleichen — ohne Versprechen, die es nicht gibt."
  },
  {
    "id": "changes/index#nachverfolgen",
    "path": "/de/changes",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "index",
    "pageTitle": "Änderungen",
    "section": "Nachverfolgen",
    "anchor": "nachverfolgen",
    "text": "Seite Wann Sie es brauchen Stabilitätsklassen und Auslauf Was stable, beta und internal zusagen, wie lange zwischen Ankündigung und Abschaltung liegt und woran Ihr Client einen Auslauf ohne Zutun erkennt. Release-Züge, Notizen und Abkündigungen Wie ein Release-Zug entsteht, wie seine Notizen aufgebaut sind, was am Release hängt — und was für Abkündigungen heute gilt. Pakete und ihre Fassungen Welche Fassung von API-Client, Apps-SDK, CLI und Verträgen gilt und was sich in jeder Fassung für Entwickler geändert hat. Die Spezifikation gegen Ihren Stand vergleichen openapi.json datiert ablegen, bei jedem Zug neu laden, Pfade, Pflichtfelder und Fehlercodes vergleichen — Seite und eigene Umgebung nebeneinander."
  },
  {
    "id": "changes/stability-and-sunset",
    "path": "/de/changes/stability-and-sunset",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "stability-and-sunset",
    "pageTitle": "Stabilitätsklassen und Auslauf",
    "section": "Stabilitätsklassen und Auslauf",
    "anchor": "",
    "text": "Jede Operation der Spezifikation trägt eine Klasse, und eine auslaufende Operation kündigt sich in ihren eigenen Antworten an. Diese Seite sagt, was stable , beta und internal zusagen, wie lange zwischen Ankündigung und Abschaltung liegt und woran ein Client den Auslauf erkennt, ohne dass jemand eine Seite liest."
  },
  {
    "id": "changes/stability-and-sunset#die-drei-klassen",
    "path": "/de/changes/stability-and-sunset",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "stability-and-sunset",
    "pageTitle": "Stabilitätsklassen und Auslauf",
    "section": "Die drei Klassen",
    "anchor": "die-drei-klassen",
    "text": "Jede Operation trägt die Erweiterung x-stability : Klasse Was sie zusagt stable Pfad, Pflichtfelder und Fehlercodes ändern sich nicht ohne neue Version. Ein Feld kann dazukommen, keines verschwindet. Bis zur Beta mit einer Ausnahme, siehe unten . beta Öffentlich, aber jung: Die Form kann sich noch bewegen, bis sie einen Zyklus ohne Bruch überstanden hat. Bauen Sie darauf, aber lesen Sie die Notizen. internal Kein Teil der öffentlichen Fläche. Diese Operationen stehen weder in der Referenz noch in der Spezifikation dieser Seite. Die Klasse wird gesetzt, nicht geraten: Eine Operation ohne Klasse bricht die Erzeugung der öffentlichen Fassung ab. Eine unklassifizierte Route erscheint deshalb nie versehentlich draußen — und ebenso wenig verschwindet eine öffentliche stillschweigend. Was internal trägt, ist die Innenansicht des Hauses: die Betreiber-Konsole, die Brücken zu Gerät und Anzeige-Anwendung, der Eingang des Zahlungsdienstleisters und die Diagnose. Für eine Organisation gibt es dort nichts zu rufen; ein Aufruf endet mit 403 Permission.OperatorOnly oder gar nicht erst bei Ihnen."
  },
  {
    "id": "changes/stability-and-sunset#wie-ein-auslauf-abläuft",
    "path": "/de/changes/stability-and-sunset",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "stability-and-sunset",
    "pageTitle": "Stabilitätsklassen und Auslauf",
    "section": "Wie ein Auslauf abläuft",
    "anchor": "wie-ein-auslauf-abläuft",
    "text": "Zwischen Ankündigung und Abschaltung einer Operation liegen zwölf Monate , ab der Beta ohne Ausnahme ( bis dahin ). In der Spezifikation. Die Operation trägt deprecated: true und x-sunset mit dem Abschalt-Tag. Wer aus der Spezifikation einen Client erzeugt, sieht den Auslauf beim Bauen; viele Generatoren markieren die Methode dann als veraltet. In jeder Antwort. Die Operation antwortet weiter, trägt aber drei zusätzliche Kopfzeilen: Am Stichtag. Nach dem Datum aus Sunset antwortet die Operation nicht mehr. Wer die Spezifikation nie gelesen hat, sieht den Auslauf also trotzdem: im Protokoll seines eigenen Clients."
  },
  {
    "id": "changes/stability-and-sunset#bis-zur-beta-ausnahmen-von-der-zusage",
    "path": "/de/changes/stability-and-sunset",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "stability-and-sunset",
    "pageTitle": "Stabilitätsklassen und Auslauf",
    "section": "Bis zur Beta: Ausnahmen von der Zusage",
    "anchor": "bis-zur-beta-ausnahmen-von-der-zusage",
    "text": "Die Plattform ist bis Ende 2026 in der Alpha. Bis zur Beta dürfen auch Wege der Stabilität stable ohne Auslauf entfallen oder brechen. Jeder Bruch steht in den Änderungsnotizen des Zugs, unter „Entwickler“, mit dem Weg und dem, was an seine Stelle tritt. Ab der Beta gelten die Zusagen oben ohne Ausnahme. Der Zug mit der neuen Auswertung hat davon Gebrauch gemacht: Weg Was geschah GET /v1/analytics/playback , GET /v1/players/{playerId}/playback , GET /v1/media/assets/{assetId}/playback entfallen ohne Auslauf. Die neue Auswertung ersetzt sie: GET /v1/analytics/overview , GET /v1/analytics/players/{playerId}/day und /plays , GET /v1/analytics/content und POST /v1/analytics/explore GET /v1/analytics/insights/overview , POST /v1/analytics/insights/report entfallen ohne Auslauf, ersetzt durch den Überblick und die freie Auswertung /v1/analytics/reports und die Wege darunter Gestalt gewechselt: Die Abfrage eines Berichts ist die der freien Auswertung statt einer Kennzahl der alten Messreihen, Formate gibt es nur noch als csv und pdf . Seitdem beta Ein aus der Spezifikation erzeugter Client und @szenera/client vor diesem Zug bekommen für die entfallenen Wege 404 und für die alten Berichtsformen 400 . Was sich je Zug ändert, steht unter Release-Züge, Notizen und Abkündigungen ."
  },
  {
    "id": "changes/stability-and-sunset#was-ihr-client-damit-tun-sollte",
    "path": "/de/changes/stability-and-sunset",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "stability-and-sunset",
    "pageTitle": "Stabilitätsklassen und Auslauf",
    "section": "Was Ihr Client damit tun sollte",
    "anchor": "was-ihr-client-damit-tun-sollte",
    "text": "Regel Warum Auf Deprecation in jeder Antwort achten und den Fund einmal je Route protokollieren Ein Auslauf trifft selten alle Aufrufe gleichzeitig; eine Zeile je Route zeigt genau, was Sie noch benutzen. Sunset mitschreiben und daraus eine Frist machen Zwölf Monate sind lang genug, um es zu vergessen, und kurz genug, um dann zu spät zu sein. Nicht auf Link verzweigen Die Adresse führt zur Referenz, sie ist eine Lesehilfe für Menschen, kein Umzugsziel. Bei beta die Spezifikation regelmäßig vergleichen Siehe Die Spezifikation gegen Ihren Stand vergleichen . Die Politik wird von zwei Wächtern ehrlich gehalten: Eine Ankündigung ohne x-sunset ist ein Fehler im Bau, und ein verstrichenes x-sunset an einer noch antwortenden Route ebenfalls."
  },
  {
    "id": "changes/stability-and-sunset#wo-sie-den-stand-ablesen",
    "path": "/de/changes/stability-and-sunset",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "stability-and-sunset",
    "pageTitle": "Stabilitätsklassen und Auslauf",
    "section": "Wo Sie den Stand ablesen",
    "anchor": "wo-sie-den-stand-ablesen",
    "text": "Quelle Was sie sagt https://developer.szenera.com/api/openapi.json die öffentliche Spezifikation im Stand des aktuellen Zugs, mit x-stability und x-sunset je Operation GET /v1/openapi.json an Ihrer Umgebung dieselben Angaben für die Fassung, die dort tatsächlich läuft Änderungen im Handbuch was sich je Zug in der Oberfläche geändert hat, das Neueste zuerst Der Status der Plattform der aktuelle Zustand, angekündigte Wartungsfenster und die letzten Störungen Die Klasse steht auch in der Referenz an jeder Operation, siehe Die Referenz lesen ."
  },
  {
    "id": "changes/stability-and-sunset#weiterführendes",
    "path": "/de/changes/stability-and-sunset",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "stability-and-sunset",
    "pageTitle": "Stabilitätsklassen und Auslauf",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Basis-URL, /v1 und Stabilität Die Spezifikation gegen Ihren Stand vergleichen Release-Züge, Notizen und Abkündigungen"
  },
  {
    "id": "changes/release-notes",
    "path": "/de/changes/release-notes",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "release-notes",
    "pageTitle": "Release-Züge, Notizen und Abkündigungen",
    "section": "Release-Züge, Notizen und Abkündigungen",
    "anchor": "",
    "text": "Die API ändert sich in Zügen: Ein Zug bindet die Fassungen der Dienste zu einem Datum zusammen, und zu jedem Zug entsteht eine Notiz, die Brüche vor allem anderen nennt. Diese Seite sagt, wie beides aufgebaut ist, wo die Notizen heute stehen und was für Abkündigungen gilt."
  },
  {
    "id": "changes/release-notes#der-release-zug",
    "path": "/de/changes/release-notes",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "release-notes",
    "pageTitle": "Release-Züge, Notizen und Abkündigungen",
    "section": "Der Release-Zug",
    "anchor": "der-release-zug",
    "text": "Ein Zug ist ein Schnitt durch die Zeit, kein Versionsvertrag. Er trägt darum kein eigenes Major, Minor oder Patch, sondern sein Datum. Merkmal Was gilt Name release/JJJJ-MM-TT , bei mehreren Zügen am selben Tag release/JJJJ-MM-TT.2 (ein .1 gibt es nie) Was er transportiert die Dienste der Plattform: die API, das Portal, das Betreiber-Portal, die Startseite und die Anzeige-Anwendung der Bildschirme Fassungen jeder Dienst trägt seine eigene Zahl (etwa 0.4.14 ); der Zug führt nur die Dienste als bewegt, deren Zahl seit dem letzten Zug gestiegen ist; die übrigen stehen als unverändert in der Notiz Wann ein Zug fällig ist sobald die Fassung eines dieser Dienste über der des letzten Zugs liegt — in der Praxis, wenn die gesammelten Änderungsnotizen zu neuen Fassungen werden Was der Zug ausliefert dieselben Abbilder, die zuvor auf Staging liefen — sie werden nicht neu gebaut, nur mit ihrer Fassung etikettiert Was nicht im Zug reist das Betriebssystem der Player — es folgt einem eigenen Takt und eigenen Auslösern; ebenso die Display-Erweiterung für den Browser. Bibliotheken wie API-Client und Apps-SDK sowie die Apps reisen in den Diensten mit; ihre Fassungen nennt die Notiz unter „Pakete“ Die Fassung des Dienstes, der in Ihrer Umgebung antwortet, nennt GET /health/version — ohne Anmeldung, mit den Feldern service , version , builtAt und environment . In Produktion steht in version nur die Zahl ( 0.4.1 ), auf allen anderen Umgebungen hängt das Commit-Kürzel des Baus an ( 0.4.1+a1b2c3d "
  },
  {
    "id": "changes/release-notes#wie-eine-notiz-aufgebaut-ist",
    "path": "/de/changes/release-notes",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "release-notes",
    "pageTitle": "Release-Züge, Notizen und Abkündigungen",
    "section": "Wie eine Notiz aufgebaut ist",
    "anchor": "wie-eine-notiz-aufgebaut-ist",
    "text": "Der Text einer Notiz stammt aus den Änderungsnotizen , die jede Änderung an der Plattform mitbringt: Wer eine Änderung schreibt, legt dazu eine Notiz ab und schreibt darin je Paket Zeilen mit Art und Zielgruppe — für Kundinnen und Kunden, für den Betrieb, für Entwickler. Die Notiz eines Zugs richtet sich an den Betrieb: Sie nennt die Brüche und die Handgriffe des Ausrollens und verweist für Kunden und Entwickler auf ihre Seiten. Was niemand aufgeschrieben hat, behauptet sie nicht. Teil Inhalt Titel Szenera <Datum> Breaking changes je Bruch für Entwickler und Betrieb eine Zeile mit Paket, Umstieg und Verweis auf die Änderung, die ihn gebracht hat Ausrollen die Handgriffe des Betriebs, durchgehend nummeriert: erst vor dem Ausrollen, dann beim Ausrollen, dann danach Für Kunden der Verweis auf die Änderungsseite des Handbuchs und die Zahl der Apps, die ihre Neuerungen im Store erklären Für Entwickler der Verweis auf die Änderungsliste je Paket und auf die Paketseite der Entwicklerfläche Pakete Bibliotheken, Apps und Erweiterung, deren Fassung mit dem Zug steigt, mit der Fassung davor Auslieferung eine Tabelle Dienst · Fassung · vorher · Digest über alle Dienste des Zugs Vergleich ein Verweis auf alle Änderungen seit dem letzten Zug Ein Zug ohne eine einzige Notiz sagt das in einem Satz und nennt nur die Fassungen."
  },
  {
    "id": "changes/release-notes#was-ein-bruch-ist",
    "path": "/de/changes/release-notes",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "release-notes",
    "pageTitle": "Release-Züge, Notizen und Abkündigungen",
    "section": "Was ein Bruch ist",
    "anchor": "was-ein-bruch-ist",
    "text": "Ein Bruch ist eine Änderung, die Sie zum Handeln zwingt: ein Pfad, der verschwindet oder umzieht, ein neues Pflichtfeld, ein anderer Fehlercode. Gekennzeichnet wird er in der Notiz selbst: Die Zeile trägt breaking und nennt den Umstieg, und die Stufe des Pakets steigt. Solange die erste Ziffer einer Fassung 0 ist, ist die zweite Ziffer die Bruchgrenze — 0.4.21 auf 0.5.0 ist ein Bruch, 0.4.21 auf 0.4.22 nicht. Was zu tun ist, steht als Umstieg wortgetreu so, wie es der Autor geschrieben hat; eine gekürzte Anweisung wäre eine falsche. Ein Bruch steht im Abschnitt Breaking changes ganz oben. Bei den Paketen zeigt ihn zusätzlich die Änderungsliste: Dort steht er unter Breaking changes — siehe Pakete und ihre Fassungen ."
  },
  {
    "id": "changes/release-notes#was-am-release-hängt",
    "path": "/de/changes/release-notes",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "release-notes",
    "pageTitle": "Release-Züge, Notizen und Abkündigungen",
    "section": "Was am Release hängt",
    "anchor": "was-am-release-hängt",
    "text": "Anhang Inhalt manifest.json der Zug maschinenlesbar: Dienste, Fassungen, Digests, gestiegene Pakete, verbrauchte Notizen openapi.json die Spezifikation im Stand dieses Zugs zwei SBOMs (CycloneDX) die Stücklisten der Abhängigkeiten aus demselben Stand SHA256SUMS Prüfsummen über alle Anhänge Ein fehlender Anhang hält den Zug nicht auf. Die Stücklisten entstehen in einem eigenen Lauf, der langsamer sein kann als der Zug; fehlt etwas, sagt die Notiz das in einer Zeile: „Nicht angehängt: … — im Lauf nicht beschaffbar“."
  },
  {
    "id": "changes/release-notes#wo-die-notizen-heute-stehen",
    "path": "/de/changes/release-notes",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "release-notes",
    "pageTitle": "Release-Züge, Notizen und Abkündigungen",
    "section": "Wo die Notizen heute stehen",
    "anchor": "wo-die-notizen-heute-stehen",
    "text": "Die Notizen entstehen als Release-Eintrag im Quellcode-Archiv der Plattform. Dieses Archiv ist nicht öffentlich, und diese Seite führt keine Einträge daraus. Verlässlich und ohne Anmeldung erreichbar sind fünf Dinge: Quelle Was sie sagt https://developer.szenera.com/api/openapi.json die Spezifikation im Stand des aktuellen Zugs GET /v1/openapi.json Ihrer Umgebung die Spezifikation der Fassung, die dort tatsächlich läuft; die Antwort darf fünf Minuten zwischengespeichert werden ( Cache-Control: public, max-age=300 ) Pakete und ihre Fassungen Fassung und Änderungsliste von API-Client, Apps-SDK, CLI und Verträgen, Abschnitt für Entwickler Änderungen im Handbuch was sich je Zug in der Oberfläche geändert hat, das Neueste zuerst — ohne Anmeldung Der Status der Plattform der aktuelle Zustand, angekündigte Wartungsfenster und die letzten Störungen Beide Fassungen der Spezifikation können auseinanderliegen, wenn Ihre Umgebung einen älteren Zug fährt. Ein Vergleich der beiden Dateien ist darum der Weg, eine Änderung sicher zu erkennen."
  },
  {
    "id": "changes/release-notes#was-für-abkündigungen-gilt",
    "path": "/de/changes/release-notes",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "release-notes",
    "pageTitle": "Release-Züge, Notizen und Abkündigungen",
    "section": "Was für Abkündigungen gilt",
    "anchor": "was-für-abkündigungen-gilt",
    "text": "Frage Stand heute Wie ist eine Operation gekennzeichnet? Jede Operation trägt x-stability mit stable , beta oder internal . Heute sind 372 der 443 öffentlichen Operationen stable . Was bedeutet stable ? Pfad, Pflichtfelder und Fehlercodes ändern sich nicht ohne neue Version. Ein Feld kann dazukommen, keines verschwindet. Gibt es abgekündigte Operationen? Heute keine. Käme eine dazu, trüge sie deprecated: true und x-sunset in der Beschreibung sowie Deprecation , Sunset und Link in jeder Antwort. Gibt es ein Verfahren mit Fristen? Ja: Zwischen Ankündigung und Abschaltung liegen zwölf Monate . Die Einzelheiten stehen unter Stabilitätsklassen und Auslauf . Woran erkenne ich eine Änderung, die mich trifft? Sie steht in der Notiz unter Breaking changes , ganz oben, mit wortgetreuem Handlungsbedarf — und sie zeigt sich im Vergleich der Spezifikationen. Was ist mit Routen, die ich in der Referenz nicht finde? Als internal gekennzeichnete Routen sind nicht Teil der öffentlichen Fläche; sie erscheinen in keiner Datei dieser Seite und in keiner Zusage."
  },
  {
    "id": "changes/release-notes#weiterführendes",
    "path": "/de/changes/release-notes",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "release-notes",
    "pageTitle": "Release-Züge, Notizen und Abkündigungen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Stabilitätsklassen und Auslauf Die Spezifikation gegen Ihren Stand vergleichen Basis-URL, /v1 und Stabilität"
  },
  {
    "id": "changes/packages",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "Pakete und ihre Fassungen",
    "anchor": "",
    "text": "Fassung und Änderungen der Pakete für Entwickler — API-Client, Apps-SDK, CLI und Verträge, das Neueste zuerst. Die Liste entsteht mit jeder neuen Fassung aus den Änderungsnotizen der Plattform und zeigt deren Abschnitt für Entwickler. Einen öffentlichen Bezugsweg für die Pakete gibt es heute nicht; die Seite sagt, welche Fassung gilt und was sich darin geändert hat. Paket Fassung @szenera/cli 0.4.0 @szenera/client 0.6.0"
  },
  {
    "id": "changes/packages#szeneracli",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "@szenera/cli",
    "anchor": "szeneracli",
    "text": ""
  },
  {
    "id": "changes/packages#040--2026-10-25",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.4.0 · 2026-10-25",
    "anchor": "040--2026-10-25",
    "text": "Breaking changes Der gespeicherte Anmeldestand trägt die Felder token und expiresAt . Umstieg: Ein alter Anmeldestand gilt als nicht angemeldet, die nächste Anmeldung schreibt den neuen. ( #821 ) Added Neue Befehle szenera proof list , szenera proof show und szenera proof download . ( #822 ) Neue Befehle szenera analytics overview , szenera analytics explore und szenera analytics delivery <acp_…> . ( #822 ) Neue Befehle advertiser list , advertiser get , advertiser create , campaign list , campaign get , booking list , creative list und creative approve . ( #822 ) Changed Das Archiv folgt dem Klienten mit den Erlösaufstellungen, ohne neuen Befehl. ( #822 ) Das Archiv folgt dem Klienten ohne die Lesewege der Minutenzählung und der Insights. Die Befehle bleiben. ( #822 ) Das Archiv folgt dem Klienten mit den Panelquellen cec , ddc und none . Die Befehle bleiben. ( #822 ) Die Kommandozeile bringt den Client mit, der einen eigenen Anmelde-Wirt kennt. Ohne Einstellung ändert sich nichts. ( #825 ) Das Archiv folgt dem Klienten mit campaignId je Nachweis des Kampagnenberichts. Die Befehle bleiben. ( #822 ) Das Archiv bündelt den aktuellen Stand der Verträge, die Befehle von szenera bleiben gleich. ( #847 ) szenera playlist get zeigt einen Werbeblock als „Werbeblock · 3 Plätze à 10 s“ mit seiner Dauer. ( #822 )"
  },
  {
    "id": "changes/packages#030--2026-10-23",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.3.0 · 2026-10-23",
    "anchor": "030--2026-10-23",
    "text": "Breaking changes Die JSON-Ausgabe der Kommandozeile nennt dieselben Namen wie die API. Umstieg: Skripte, die die JSON-Ausgabe lesen, auf die englischen Feldnamen umstellen. ( #761 ) szenera api-key revoke <akm_…> --compromised nennt den Grund des Widerrufs. szenera api-key list mit einem Schlüssel-Profil antwortet 401. Umstieg: Für szenera api-key list ein Profil mit Sitzung verwenden. ( #762 ) Added Die Typen folgen der API: Feed-Katalog, Quellen für den Betrieb und der Fehlercode FeedSource.NotFound . ( #801 ) Neue Aufrufe: Zusammenfassung, Sammelaktion und Wiederherstellen von Benachrichtigungen, dazu state und groupKey an der Liste. ( #795 ) playerId an der Vorschau der Widget-Daten und der Lesebereich player-location . ( #801 ) Standorte: region und openingHours , dazu das Ereignis fleet.location.opening-hours-changed . ( #801 ) Widget-Definitionen des Katalogs kennen size und preview.states , Ansichten des Stores kennen size . ( #801 )"
  },
  {
    "id": "changes/packages#023",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.2.3",
    "anchor": "023",
    "text": "Änderungen GET /v1/audit/bulk/:correlationId entfällt und mit ihm client.audit.auditBulk und das Schema GetBulkActionDetailResponse . ( #742 ) Das Recht audit.bulk.read , die Ereignisart audit.bulk-action.completed und der Fehlercode Internal.AuditBulkActionDetailRepositoryUnavailable entfallen. ( #742 )"
  },
  {
    "id": "changes/packages#022",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.2.2",
    "anchor": "022",
    "text": "Änderungen @szenera/contracts : neu CatalogAppSchema , AppReleaseNotesSchema (Notizen je Zielgruppe und Sprache), AppReleaseEventSchema , RestoreAppRelease* , ListAppReleaseHistory* und GetAppVersionHistory* . Ein AppRelease trägt notes , die Wire-Form daneben weiter releaseNotes . PATCH /v1/platform/app-catalog/apps/{appId}/releases/{id} nimmt notes je Zielgruppe oder wie bisher releaseNotes . ( #677 ) Ein Grant trägt installedReleaseId , pendingReleaseId , pendingReasons und pendingDeadline , ein Zustimmungs-Nachweis releaseId , eine offene Zustimmung fromVersion , toVersion , reasons und deadline . Entfallen sind autoUpgrade am Grant und ConfigSchemaChangedPayloadSchema . ( #677 ) GET /v1/apps/{appId}/history liefert einer Organisation die Fassungen einer App, die stable erreicht haben, mit Datum und „Neu in dieser Fassung\" in der erbetenen Sprache ( ?locale=en-US , ohne Angabe Deutsch). ( #677 ) @szenera/api-client : neu client.platform.appCatalogAppsHistory.list , client.platform.appCatalogAppsReleases.restore und client.appCatalog.appsHistory.list . @szenera/client : neu client.appCatalog.appsHistory.list . @szenera/cli bündelt die neuen Verträge, ihre Befehle bleiben, wie sie sind. ( #677 ) Das scenes-Bündel der Momentaufnahme trägt appVersions (je App die eine Fassung des Displays) sowie appRendererHosts und appWidgetTypes je <appId>@<version> , alle mit der Vorgabe {} . Die Pins der Szenen und Widgets nennen dieselbe Fassung. ( #683 ) displayAppVersion(pins, installe"
  },
  {
    "id": "changes/packages#021",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.2.1",
    "anchor": "021",
    "text": "Ohne Eintrag für Entwickler."
  },
  {
    "id": "changes/packages#020",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.2.0",
    "anchor": "020",
    "text": "Brüche Die Kommandozeile heißt jetzt szenera . Das Paket @szenera/cli liefert das Programm unter diesem Namen aus, der Name signage entfällt ohne Übergang. ( #618 ) Die Umgebungsvariablen heißen SZENERA_PROFILE , SZENERA_BASE_URL , SZENERA_API_KEY , SZENERA_OUTPUT und SZENERA_PROFILE_FILE . Variablen mit dem Präfix SIGNAGE_ liest die Kommandozeile nicht mehr. ( #618 ) Die Profil-Datei liegt unter ~/.config/szenera/profile.json . Ein zwischengespeichertes Zugriffsmerkmal liegt daneben, nach dem Umzug meldet sich die Kommandozeile einmal neu an. ( #618 ) In der Sitzungsliste erscheint die Kommandozeile als szenera-cli mit ihrer Fassung. ( #618 ) Die Befehle des Plattformbetriebs sind nicht mehr enthalten. szenera führt 122 Befehle für Organisationen: Medien, Szenen, Programme, Geräte, Team, Schlüssel, Abonnement und den Notausgang api . ( #618 ) Ein Profil für den Plattformbetrieb ( \"audience\": \"operator\" ) weist die Kommandozeile beim Lesen ab. ( #618 ) permission catalog --key-eligible zeigt jetzt die Rechte, die ein Schlüssel der eigenen Organisation tragen darf. ( #618 ) invoice list kennt die Flagge --status nicht mehr. Für eine Organisation blieb sie bisher ohne Wirkung. ( #618 ) Die Pakete heißen jetzt @szenera/* und die Apps @szenera-app/* , Importe und Abhängigkeiten sind entsprechend anzupassen. ( #610 ) Änderungen POST /v1/devices/:id/release mit reason ersetzt POST /v1/devices/:id/reset . ( #601 ) Neu sind POST /v1/devices/:id/block , PATCH /v1/devices/:id sowie rel"
  },
  {
    "id": "changes/packages#010",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.1.0",
    "anchor": "010",
    "text": "Stand vor den Änderungsnotizen — die erste Fassung mit eigener Zahl. Was davor geschah, steht im Verlauf des Quellbaums; diese Liste beginnt mit der nächsten Fassung."
  },
  {
    "id": "changes/packages#szeneraclient",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "@szenera/client",
    "anchor": "szeneraclient",
    "text": ""
  },
  {
    "id": "changes/packages#060--2026-10-25",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.6.0 · 2026-10-25",
    "anchor": "060--2026-10-25",
    "text": "Breaking changes analytics.analyticsReports steht auf @beta und trägt als Abfrage eine SavedExploreQuery . Als Format gibt es nur csv und pdf . Umstieg: Berichte als Abfrage der freien Auswertung neu anlegen. ( #822 ) analytics.analyticsPlayback , analytics.playersPlayback , analytics.mediaAssetsPlayback , analytics.analyticsInsightsOverview und analytics.analyticsInsights entfallen. Umstieg: Die neue Auswertung über analytics.analyticsOverview , analytics.analyticsPlayersDay und analytics.analyticsExplore lesen. ( #822 ) Einträge von Playlisten und der Zeitleiste eines Displays kennen die Art adPod , den Werbeblock mit Plätzen, Spotlänge und Lückenfüller. Umstieg: Einträge mit kind: 'adPod' beim Lesen von Playlisten und Zeitleisten behandeln. ( #822 ) Added analytics.analyticsExplore.create und .export , analytics.analyticsCatalog.list und analytics.analyticsOverview.list (beta) für Abfrage, Katalog, Ausgabe und Überblick. ( #822 ) analytics.analyticsPlayersDay.list , analytics.analyticsPlayersPlays.list , analytics.analyticsContent.list und .byId , analytics.analyticsCampaignsDelivery.list (beta). ( #822 ) fleet.playersScreenshots.list und fleet.playersScreenshots.byId (beta) lesen die Zeitreihe der Bildschirmaufnahmen eines Displays. ( #822 ) analytics.analyticsProofs ( list , byId , interim ), analytics.analyticsProofsFiles.byId und analytics.analyticsShareLinks ( list , create , remove ), alle beta. ( #822 ) analytics.publicProofKeys.list , analytics.publicProofs.verify "
  },
  {
    "id": "changes/packages#050--2026-10-23",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.5.0 · 2026-10-23",
    "anchor": "050--2026-10-23",
    "text": "Breaking changes Das Paket nutzt das englische API-Format. Felder, Typen und Werte in Billing, Lizenzen, Abbildern und Bündeln heißen anders. Umstieg: Die Namen im eigenen Code ersetzen, etwa zielstand durch targetState und herkunft durch provenance . ( #761 ) me.apiKeys.remove(id, { reason }) verlangt den Grund owner-rotation oder compromised . Unter /v1/me antworten mit einem Schlüssel nur noch fünf Lesewege, alle übrigen mit 401. Umstieg: Den Grund mitsenden und Automation auf die fünf Lesewege beschränken. ( #762 ) Added Dokumente: Varianten page-<n> und page-<n>-sm , pageCount an Medien und der Befund DocumentPagesChanged . ( #801 ) Die Typen folgen der API: Feed-Katalog, Quellen für den Betrieb und der Fehlercode FeedSource.NotFound . ( #801 ) Neue Aufrufe: Zusammenfassung, Sammelaktion und Wiederherstellen von Benachrichtigungen, dazu state und groupKey an der Liste. ( #795 ) playerId an der Vorschau der Widget-Daten und der Lesebereich player-location . ( #801 ) Standorte: region und openingHours , dazu das Ereignis fleet.location.opening-hours-changed . ( #801 ) Widget-Definitionen des Katalogs kennen size und preview.states , Ansichten des Stores kennen size . ( #801 ) Changed Billing: zielstand heißt targetState , regel heißt rule , teilstorno heißt partialCancellation , staffel heißt tierScale , testphase heißt trialCredit . ( #761 ) Billing: belegteBildschirme heißt usedDisplays , zyklusVormerkung heißt pendingBillingCycle , restguthabenCents heißt remainingCredi"
  },
  {
    "id": "changes/packages#040",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.4.0",
    "anchor": "040",
    "text": "Brüche GET /v1/audit/bulk/:correlationId entfällt und mit ihm client.audit.auditBulk und das Schema GetBulkActionDetailResponse . ( #742 ) Das Recht audit.bulk.read , die Ereignisart audit.bulk-action.completed und der Fehlercode Internal.AuditBulkActionDetailRepositoryUnavailable entfallen. ( #742 )"
  },
  {
    "id": "changes/packages#030",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.3.0",
    "anchor": "030",
    "text": "Brüche Neue Routen der Organisation: GET /v1/me/apps/updates und GET /v1/me/apps/{appId}/update liefern die Entscheidung über ein Update (Fassung von und nach, Gründe, Neues, Änderungen in Klartext, Folgen, Frist, ob Ablehnen noch geht), POST /v1/me/apps/{appId}/update/accept und …/update/reject entscheiden mit dem Hash der Ziel-Fassung. ( #680 ) Kanäle mit Publikum: GET|PUT /v1/me/apps/beta-enrollment und POST|DELETE /v1/me/apps/{appId}/beta-enrollment tragen eine Organisation für Fassungen aus beta ein, global oder je App. Store, App-Seite, Installation und Upgrade nehmen die höchste Fassung, die die Organisation sieht, die Liste trägt dazu channel . ( #680 ) Schwimmende Pins: effectiveAppVersion(pin, installed) in @szenera/contracts/composition . Die Momentaufnahme nennt die gespielte Fassung. GET /v1/me/apps/{appId}/scene-pins und POST …/scene-pins/lift heben alle Szenen auf die installierte Fassung, je Szene als neue veröffentlichte Fassung. ( #680 ) POST /v1/marketplace/apps/{appId}/upgrade geht nur vorwärts und nur auf Fassungen, die die Organisation sieht. Neuer Fehlercode App.UpdateUnavailable (409, details.reason : not-forward , none , not-rejectable ). ( #680 ) Entfallen sind GET /v1/me/pending-reacceptances , POST /v1/marketplace/apps/{appId}/accept und …/reject samt ihren Verträgen. RetireAppReleaseRequest nimmt grace und securityAdvisory statt graceMonths . ( #680 ) Neue Ereignisse marketplace.app.update-declined , marketplace.app.update-withdrawn und marketplac"
  },
  {
    "id": "changes/packages#021-1",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.2.1",
    "anchor": "021-1",
    "text": "Ohne Eintrag für Entwickler."
  },
  {
    "id": "changes/packages#020-1",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.2.0",
    "anchor": "020-1",
    "text": "Brüche Die Fähigkeiten eines Geräts kennen den Block remote nicht mehr. Die Erweiterung meldet sich im Block host mit platform: extension . Der Block host trägt dazu bridge (die Brückenfassung des Wirts) und bootStart (ob der Wirt von selbst startet). ( #640 ) Geräte tragen statt runtime den Block software (je Baustein Fassung, Bau und angenommene Freigabe), dazu channel und softwareTarget , das Soll ihres Kanals. Player tragen kein capabilities mehr, der gemeldete Zustand kein reportedState.capabilities . Was ein Gerät kann, steht in seinen Fähigkeiten, deren Block software runtime ersetzt. ( #642 ) Der Zustand der Anzeige ( lastTelemetry.renderer ) nennt das Bau-Profil ( modern oder legacy ). ( #642 ) PlayerCapabilitySchema und die beiden OS_CHANNELS entfallen, ReleaseChannel lebt in @szenera/contracts/shared neben PROTOCOL_SERVED . Die Selbstsicht trägt channel und softwareTarget . channel fehlt, solange die Cloud keinen Kanal für das Gerät führt, currentOsChannel bleibt für ältere Fernwartungen. ( #642 ) Beispiele der Vorfassungen je Naht liegen unter packages/contracts/src/samples/protocols/ , Tests in TypeScript und Go legen sie den heutigen Lesern vor. ( #642 ) HOST_EDITIONS , HostEditionSchema und HostEdition stehen in @szenera/contracts/fleet und im Einstieg @szenera/contracts/fleet/host-bridge . Die Beschreibung eines Wirts in @szenera/host-bridge nimmt edition . ( #653 ) Änderungen Der Block host der Gerätefähigkeiten kennt zwei weitere Plattformen: vega für Fire T"
  },
  {
    "id": "changes/packages#010-1",
    "path": "/de/changes/packages",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "packages",
    "pageTitle": "Pakete und ihre Fassungen",
    "section": "0.1.0",
    "anchor": "010-1",
    "text": "Die erste Fassung mit eigener Zahl, noch nicht veröffentlicht. Diese Liste beginnt mit der nächsten Fassung."
  },
  {
    "id": "changes/compare-specification",
    "path": "/de/changes/compare-specification",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "compare-specification",
    "pageTitle": "Die Spezifikation gegen Ihren Stand vergleichen",
    "section": "Die Spezifikation gegen Ihren Stand vergleichen",
    "anchor": "",
    "text": "Am Ende liegt die Spezifikation des aktuellen Zugs datiert neben der Ihrer Umgebung, und Sie wissen, ob ein Pfad verschwunden ist, ein Pflichtfeld dazugekommen ist oder eine Antwort einen anderen Status trägt — bevor Ihre Integration es merkt. Die Entwicklerseite liefert unter /api/openapi.json den Stand des aktuellen Zugs. Ihre Umgebung antwortet unter /v1/openapi.json mit dem, was dort tatsächlich läuft. Staging und Produktion können auseinanderliegen; erst der Vergleich sagt, welche Änderung Sie schon vor sich haben und welche noch kommt."
  },
  {
    "id": "changes/compare-specification#schritte",
    "path": "/de/changes/compare-specification",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "compare-specification",
    "pageTitle": "Die Spezifikation gegen Ihren Stand vergleichen",
    "section": "Schritte",
    "anchor": "schritte",
    "text": ""
  },
  {
    "id": "changes/compare-specification#beide-spezifikationen-laden",
    "path": "/de/changes/compare-specification",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "compare-specification",
    "pageTitle": "Die Spezifikation gegen Ihren Stand vergleichen",
    "section": "Beide Spezifikationen laden",
    "anchor": "beide-spezifikationen-laden",
    "text": "Legen Sie die Spezifikation der Seite datiert ab: Legen Sie die Spezifikation Ihrer Umgebung daneben: Notieren Sie, welche Fassung dort läuft:"
  },
  {
    "id": "changes/compare-specification#fassung-und-pfade-vergleichen",
    "path": "/de/changes/compare-specification",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "compare-specification",
    "pageTitle": "Die Spezifikation gegen Ihren Stand vergleichen",
    "section": "Fassung und Pfade vergleichen",
    "anchor": "fassung-und-pfade-vergleichen",
    "text": "Vergleichen Sie info.version beider Dateien: Gleiche Zahlen heißen nicht gleicher Inhalt: info.version ist eine Betreibereinstellung (Vorgabe 1.0.0 ) und bewegt sich nicht mit jedem Zug. Vergleichen Sie darum immer auch die Pfade. Vergleichen Sie Pfade und Methoden. Der Befehl listet jede Operation als METHODE /pfad ; diff zeigt, was nur auf einer Seite steht:"
  },
  {
    "id": "changes/compare-specification#pflichtfelder-antworten-und-stabilität-prüfen",
    "path": "/de/changes/compare-specification",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "compare-specification",
    "pageTitle": "Die Spezifikation gegen Ihren Stand vergleichen",
    "section": "Pflichtfelder, Antworten und Stabilität prüfen",
    "anchor": "pflichtfelder-antworten-und-stabilität-prüfen",
    "text": "Prüfen Sie die Pflichtfelder der Schemata, die Sie senden. Die Liste steht in required : Prüfen Sie die Antworten der Operationen, die Sie nutzen. Die Statuscodes stehen als Schlüssel unter responses ; die Fehlercodes dazu nennt Das Fehlerformat : Prüfen Sie x-stability der Operationen, die Sie nutzen: Die Antwort lautet stable oder beta . Die Operation aus dem Beispiel ist stable ."
  },
  {
    "id": "changes/compare-specification#ergebnis-prüfen",
    "path": "/de/changes/compare-specification",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "compare-specification",
    "pageTitle": "Die Spezifikation gegen Ihren Stand vergleichen",
    "section": "Ergebnis prüfen",
    "anchor": "ergebnis-prüfen",
    "text": "diff in Schritt 5 meldet keine Zeile mit < (nur in Ihrer Umgebung): Kein Pfad, den Sie aufrufen, ist entfernt oder umgezogen. Zeilen mit > sind neue Operationen und brechen nichts. Die required -Listen der Schemata, die Sie senden, enthalten kein Feld, das Ihre Anfragen nicht mitschicken. Die Statuscodes unter responses Ihrer Operationen sind unverändert, und keine trägt deprecated: true . Steht dort x-stability: beta , kann sich die Form noch bewegen — siehe Stabilitätsklassen und Auslauf . Die datierte Datei bleibt liegen. Beim nächsten Zug laden Sie neu und vergleichen die beiden datierten Dateien miteinander."
  },
  {
    "id": "changes/compare-specification#wenn-etwas-nicht-klappt",
    "path": "/de/changes/compare-specification",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "compare-specification",
    "pageTitle": "Die Spezifikation gegen Ihren Stand vergleichen",
    "section": "Wenn etwas nicht klappt",
    "anchor": "wenn-etwas-nicht-klappt",
    "text": "Symptom Bedeutung Lösung Ihre Umgebung liefert nach einem Zug noch den alten Stand Die Antwort von /v1/openapi.json darf fünf Minuten zwischengespeichert werden ( Cache-Control: public, max-age=300 ). Nach fünf Minuten erneut laden. info.version ist gleich, diff zeigt trotzdem Unterschiede Die Zahl in info.version folgt nicht dem Zug. Den Inhalt vergleichen, nicht die Zahl; die laufende Fassung nennt /health/version . Eine Route fehlt in beiden Dateien, antwortet aber Sie ist als internal geführt und damit kein Teil der öffentlichen Fläche — in keiner der beiden Dateien. Nur öffentliche Operationen in die Integration nehmen; auf eine unbeschriebene Route gibt es keine Zusage. Eine Operation trägt plötzlich deprecation -Kopfzeilen Sie läuft aus; x-sunset in der Beschreibung und Sunset in der Antwort nennen den Abschalt-Tag. Zwölf Monate bleiben — den Aufruf auf den genannten Nachfolger umstellen, siehe Stabilitätsklassen und Auslauf . /health/version antwortet mit 429 Die Route ist auf 60 Aufrufe je Minute und Adresse begrenzt. Die in Retry-After genannten Sekunden warten. /health/version meldet unknown oder eine Zahl mit -dev Die Umgebung läuft nicht aus einem Auslieferungs-Abbild. Für den Vergleich die Spezifikation nehmen, nicht die Fassung."
  },
  {
    "id": "changes/compare-specification#weiterführendes",
    "path": "/de/changes/compare-specification",
    "area": "changes",
    "areaTitle": "Änderungen",
    "page": "compare-specification",
    "pageTitle": "Die Spezifikation gegen Ihren Stand vergleichen",
    "section": "Weiterführendes",
    "anchor": "weiterführendes",
    "text": "Release-Züge, Notizen und Abkündigungen Die Referenz lesen Einen eigenen Client aus der Spezifikation erzeugen"
  }
]
