
# Benutzerdefiniertes CSS setzen

`PUT /v1/branding/css` · Berechtigung `branding:write`

Schreibt das benutzerdefinierte CSS des Umgebungsentwurfs (Plan Startup oder höher, Funktion `branding_custom_css`). Das CSS durchläuft einen Allow-List-Bereiniger: nur `ui_catalog`-Klassen (`idp-*`) sowie erlaubte Eigenschaften und Werte; die geschützte Ebene (Rechtstexte, Einwilligung und Barrierefreiheitselemente) kann weder ausgeblendet noch überdeckt werden. Alles oder nichts: Bei einem einzigen Verstoß wird nichts geschrieben. Der Entwurf speichert die kanonische Ausgabe des Bereinigers, nicht den gesendeten Text; `css` in der Antwort ist diese Ausgabe. Eine leere Zeichenkette löscht es und unterliegt keiner Planprüfung.

## Anfrage

| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
| `If-Match` | Header | ja | Das zuletzt gelesene `ETag` des Entwurfs (`"0"`, wenn nie geschrieben). Fehlt: `428`; veraltet: `412`. |
| `css` | String | ja | Höchstens 51200 Bytes, gültiges UTF-8; `""` löscht. |

## Antwort

`200 OK` mit dem Umgebungsentwurf und seinem `ETag`. `theme` enthält nur gesetzte Tokens; nicht gesetzte erben vom Plattformstandard.

```json
{
  "theme": {"mode": "light", "light": {"color": {"primary": "#0a3d91"}}, "logo_height": 40},
  "css": ".idp-c .idp-title{color:#0a3d91}\n",
  "page_template": null,
  "updated_at": "2026-10-05T09:30:00.123456Z"
}
```

## Fehler

| HTTP | Code | Wann |
|---|---|---|
| 400 | `unknown_field` | Der Body enthält ein unbekanntes Feld oder ist kein JSON-Objekt. |
| 403 | `plan_feature_unavailable` | Der Plan enthält die Funktion nicht (`plan_feature_unavailable`, Erweiterungen `feature` und `required_plan`). Entwicklungsumgebungen sind nicht eingeschränkt. |
| 412 | `precondition_failed` | `If-Match` stimmt nicht mit der aktuellen Version des Entwurfs überein. |
| 422 | `validation_failed` | `css` fehlt oder ist `null` (`required`), zu lang (`too_long`) oder kein gültiges UTF-8 (`invalid_utf8`). |
| 422 | `branding_validation_failed` | Das CSS hat den Bereiniger nicht bestanden. Die Erweiterung `violations` listet jeden Verstoß: `line`, `column` (ab 1), `rule` (zum Beispiel `class_not_in_catalog`, `protected_property`, `property_not_allowed`, `syntax_error`) und, falls vorhanden, `name` (Name der Eigenschaft, Klasse oder Funktion). CSS-Werte werden nie zurückgegeben. Nach 100 Verstößen bricht der Bereiniger ab; das letzte Element hat die Regel `too_many_issues`. Die kanonische Ausgabe ist länger als die Eingabe (jede Regel erhält ein Bereichspräfix); überschreitet sie 51200 Bytes, ist der einzige Verstoß die Regel `too_large` in Zeile 1, Spalte 1. |
| 428 | `precondition_required` | Der `If-Match`-Header fehlt. |
| 401 | `unauthenticated` | Die Anmeldeinformation fehlt, ist ungültig oder widerrufen. |
| 403 | `insufficient_permissions` | Der Anmeldeinformation fehlt die erforderliche Berechtigung. |
| 429 | `rate_limited` | Das Ratenlimit wurde überschritten; nach der `Retry-After`-Zeit erneut versuchen. |
| 503 | `service_unavailable` | Eine Abhängigkeit ist vorübergehend nicht verfügbar; nach der `Retry-After`-Zeit erneut versuchen. |

Beispiel einer Ablehnung durch den Bereiniger:

```css
.idp-button{position:fixed}
.my-banner{color:red}
```

```json
{
  "type": "https://docs.keenauth.com/errors/branding_validation_failed",
  "title": "Branding validation failed",
  "status": 422,
  "code": "branding_validation_failed",
  "violations": [
    {"line": 1, "column": 13, "rule": "property_not_allowed", "name": "position"},
    {"line": 2, "column": 1, "rule": "class_not_in_catalog", "name": "my-banner"}
  ],
  "request_id": "req_01J9Z3N5W8Q2F6V4K7T1M0XB3C"
}
```

## Beispiel

```bash
curl -X PUT https://api.eu.keenauth.com/v1/branding/css \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "3"' \
  -d '{"css":".idp-title { color: #0a3d91 }"}'
```
