# Anmeldung: Schlüssel und Sitzungen

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.

> **Typ:** nachschlagen
> **Für wen:** Wer einen Ausweis in eine Integration einbaut oder einen 401 einordnen muss.
> **Voraussetzung:** ein Konto in einer Organisation, deren Paket den API-Zugang enthält

## Die Präfixe

| 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](/de/api/oauth-delegated-access) |
| `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](/de/api/scim-provisioning) |
| `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 Operationen sind so.

## Der API-Schlüssel

| 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.proof.read`, `analytics.proof.manage`) sowie `billing.usage.read` und `analytics.query.run`. Nicht schlüsselfähig sind unter anderem die Geräteliste `identity.device.list`, die Wiedergabe-Rückschau `fleet.playback-history.read`, das Anlegen und Entfernen von Displays (`fleet.player.pair`, `fleet.player.delete`), die Kontoverwaltung `identity.*`, die Webhook-Verwaltung `webhook.*` und alles unter `platform.*`. Was dort steht, bleibt Sitzungen vorbehalten.

Die Kommandozeile zeigt die Freigabeliste: `szenera api-key issue --list-allowed`; den ganzen Katalog: `szenera permission catalog`.

Ein Schlüssel handelt mit den Rechten des Kontos, das ihn erstellt hat. Ist dieses Konto gesperrt, ruht der Schlüssel: Die Anmeldung antwortet `401 Auth.UserSuspended`, und nach dem Aufheben der Sperre arbeitet er wieder. Wird das Konto gelöscht, ist der Schlüssel widerrufen (Grund `user-deleted-cascade`). Dasselbe gilt für eine verbundene Anwendung und das Konto, das ihr zugestimmt hat.

Unter `/v1/me` erreicht ein Schlüssel nur die Lesewege, die ein Programm braucht: `GET /v1/me` (wer der Ausweis ist), `GET /v1/me/permissions`, `GET /v1/me/capabilities`, `GET /v1/me/apps/store` und `GET /v1/me/licensing`. Alles Übrige dort gehört der Person und verlangt eine Sitzung.

`lastUsedAt` zeigt die letzte Nutzung. Der Wert wird höchstens einmal je Minute fortgeschrieben.

## Lebenszyklus eines Schlüssels

| 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](/de/api/rate-limits)). 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 |

```bash
curl -sS -X POST "$SZENERA_API/v1/me/api-keys/akm_01KQRWJRC0APKEY00000000001/rotate" \
  -H "Authorization: Bearer $SZENERA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "graceHours": 24 }'
```

Die Antwort — beide Schlüsselobjekte hier auf die Felder gekürzt, die eine Integration braucht:

```json
{
  "newApiKey": {
    "id": "akm_01KQRWJRC0APKEY00000000002",
    "keyPrefix": "ak_APKEY0002",
    "name": "Warenwirtschaft-Anbindung",
    "originClass": "read-only",
    "scope": ["asset.read", "playlist.read"],
    "expiresAt": "2027-05-04T07:00:00.000Z",
    "revokesAt": null
  },
  "newPlainKey": "ak_01KQRWJRC0APKEY00000000002",
  "oldApiKey": {
    "id": "akm_01KQRWJRC0APKEY00000000001",
    "keyPrefix": "ak_APKEY0001",
    "name": "Warenwirtschaft-Anbindung",
    "originClass": "read-only",
    "scope": ["asset.read", "playlist.read"],
    "expiresAt": "2027-05-04T07:00:00.000Z",
    "revokesAt": "2026-07-21T09:15:00.000Z"
  },
  "oldRevokesAt": "2026-07-21T09:15:00.000Z"
}
```

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 Stunden** und **3 Tage**. Wie Sie den ersten Schlüssel anlegen, steht unter [Einen API-Schlüssel erzeugen](/de/getting-started/create-api-key).

Die Antworten auf einen ungültigen Schlüssel: `401 Auth.ApiKeyUnknown`, `401 Auth.ApiKeyExpired`, `401 Auth.ApiKeyRevoked` — alle unter [Fehlercodes: auf jeder Route möglich](/de/api/error-codes-general).

## Die Sitzung

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-Anmeldung über den Anbieter Ihrer Organisation und die plattformweiten Anbieter sind Rundläufe durch den Browser, keine Integrationswege; wie Sie sie einrichten, steht im Handbuch unter [Organisation](https://szenera.com/de/docs/organization).

## Schlüssel oder Sitzung?

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

## Weiterführendes

- [Einen API-Schlüssel erzeugen](/de/getting-started/create-api-key)
- [Delegierter Zugang mit OAuth 2.1](/de/api/oauth-delegated-access)
- [Provisionierung mit SCIM 2.0](/de/api/scim-provisioning)
- [Fehlercodes: Konten und Sitzungen](/de/api/error-codes-accounts)
- [Handbuch: Organisation](https://szenera.com/de/docs/organization)
