# Schlüssel-API (eigener Shop)

> Lizenzschlüssel aus einem eigenen Shop oder über Wiederverkäufer ausgeben und verwalten.

Verkaufst du eine **Anwendung mit Lizenzschlüsseln** auch außerhalb von Cyrellian – im eigenen Shop, über einen
Wiederverkäufer, auf Rechnung –, gibt dein System die Schlüssel über diese API aus. Die Software aktiviert sie danach
ganz normal über die [Lizenzschlüssel-API](doc:lizenzschluessel-api).

Über diese API ausgegebene Schlüssel sind mit **Quelle „API“** markiert. Sie wurden nicht über Cyrellian verkauft und
erscheinen deshalb **nicht** in Partner-Abrechnungen.

## Token

In der Konsole: Anwendung → **Schlüssel & Tokens** → Service-Token mit Zweck **Schlüssel-API**. Das Token wird nur
einmal angezeigt; es gilt nur für diese eine Anwendung. Jeder Aufruf schickt es als
`Authorization: Bearer <token>`. Die API ist für Server gedacht – das Token gehört nie in einen Browser oder in die
Software.

Basis-URL: `https://updates.cyrellian.com/<anwendung>/keys/v1`

## Schlüssel ausgeben

`POST /keys`

```json
{
  "plan": "standard",
  "count": 1,
  "email": "kunde@example.com",
  "name": "Erika Muster",
  "devices": 3,
  "expiresAt": "2027-12-31",
  "note": "Bestellung R-1001"
}
```

| Feld | Pflicht | Bedeutung |
|---|---|---|
| `plan` | ja | Code des Tarifs (öffentlich oder verborgen; keine Entwürfe oder archivierten Tarife) |
| `count` | nein | 1–100 Schlüssel auf einmal, Standard 1 |
| `email`, `name` | nein | Kunde; mit einer E-Mail sieht der Kunde den Schlüssel auch in seinem Cyrellian-Konto |
| `devices` | nein | Geräte gleichzeitig; fehlt es, gilt der Wert des Tarifs, `null` = unbegrenzt |
| `expiresAt` | nein | Ablaufdatum (ISO; nur Datum = Ende dieses Tages, UTC); fehlt es, läuft der Schlüssel nie ab |
| `note` | nein | eigene Notiz, z. B. Bestellnummer |

Antwort `201`:

```json
{ "keys": [{ "id": "…", "key": "ABCDE-FGHJK-LMNPQ-RSTUV-WXYZ2", "manageCode": "VC-…" }] }
```

Der Schlüssel im Klartext steht **nur in dieser Antwort** – gib ihn direkt an deinen Kunden weiter. `manageCode` gibt
es nur, wenn die Anwendung Verwaltungscodes nutzt.

## Schlüssel finden

`GET /keys?email=kunde@example.com` oder `GET /keys?key=ABCDE-FGHJK-…`

```json
{ "keys": [{ "id": "…", "lastFour": "WXYZ2", "plan": "standard", "status": "active", "email": "kunde@example.com",
  "name": "Erika Muster", "maxActivations": 3, "activeDevices": 1, "expiresAt": null, "source": "api",
  "createdAt": "2026-10-06T08:00:00.000Z" }] }
```

`status`: `active`, `expired`, `suspended` oder `revoked`. `source`: `api`, `console` oder `checkout`.

## Sperren, widerrufen, entsperren

`POST /keys/<id>/suspend` · `POST /keys/<id>/revoke` · `POST /keys/<id>/reactivate` – optional mit `{"reason": "…"}`.

- **suspend**: vorübergehend, z. B. bei einer Rückbuchung.
- **revoke**: endgültig; ein widerrufener Schlüssel lässt sich nicht wieder aktivieren.
- Die Software merkt es spätestens, wenn ihr Token abläuft (7 Tage).

## Fehler

| Status | `error` | Bedeutung |
|---|---|---|
| 400 | `invalid_request` | Eingabe ungültig – `message` sagt, was |
| 400 | `invalid_json` | Body ist kein JSON-Objekt |
| 401 | `unauthorized` | Token fehlt, ist widerrufen oder gehört zu einer anderen Anwendung |
| 404 | `not_found` | Anwendung ohne Lizenzschlüssel, unbekannter Pfad oder Schlüssel |
| 429 | `rate_limited` | mehr als 120 Aufrufe pro Minute |

Jeder Aufruf, der etwas ändert, steht im Protokoll der Konsole (mit dem Namen des Tokens).
