# API: Fehler

Fehlerform, Statuscodes und stabile Codes der öffentlichen API.

## Hinweise für KI-Agenten

- API-Basisadresse: `https://api.eventwerk.eu/api/v1`
- OpenAPI 3.1: `https://api.eventwerk.eu/openapi.json` und `/api/v1/openapi.json`. Daraus lassen sich Clients und Typprüfungen generieren.
- Anmeldung: `Authorization: Bearer ew_...`
- Schlüssel: Einstellungen unter `/dashboard/einstellungen`, nur mit Eventwerk Plus und nur für Eigentümer. Der volle Schlüssel wird nur einmal angezeigt. Name und Zeitpunkt der letzten Nutzung werden geführt und ausgegeben.
- Zeiten: Alle Zeitpunkte kommen in UTC mit Suffix `Z`. Die Zeitzone des Events steht getrennt in `timezone` als IANA-Name, Standard `Europe/Vienna`. Clients rechnen selbst um.
- Paginierung: Die Event- und Anmeldelisten liefern `{items, has_more, next_cursor}` mit `limit` (Standard 50, höchstens 200), `cursor` und `updated_since`.
- Grenzen: 200 Anfragen pro Minute je Schlüssel. Der Zähler liegt im Speicher je Instanz, ist hinter mehreren Instanzen ein Vielfaches und beginnt nach einem Deploy neu. Bei Überschreitung antwortet die API mit 429, `rate_limited` und `Retry-After`.
- Gastgrenzen: Anmelde-Link 20 je Stunde je IP-Adresse, Anmeldung 60 je Stunde je IP-Adresse und 3 je Stunde je E-Mail-Adresse und Event.
- Fehler: JSON mit `code` und `message`, optional mit `details`.

Jeder Fehler kommt als JSON. Das Feld `code` ist stabil und für Programme gedacht. Das Feld `message` ist deutsch und für Menschen formuliert. `details` ist optional.

```json
{
  "code": "invalid_title",
  "message": "Ein Titel ist erforderlich."
}
```

## Statuscodes

| Status | Bedeutung |
| --- | --- |
| 400 | Die Anfrage ist ungültig. Meist ein Feld oder ein JSON-Problem. |
| 401 | Kein gültiger API-Schlüssel. |
| 403 | Der Plan erlaubt die Aktion nicht oder die Rolle fehlt. |
| 404 | Die Ressource wurde nicht gefunden. |
| 405 | Diese Methode ist für die Ressource nicht erlaubt. |
| 409 | Die Aktion passt nicht zum aktuellen Zustand. |
| 413 | Der Rumpf ist größer als 1 MiB. |
| 429 | Zu viele Anfragen. `Retry-After` beachten. |
| 500 | Interner Fehler. |
| 503 | Der Dienst ist vorübergehend nicht verfügbar. |

## Codes

| Code | Status | Bedeutung |
| --- | --- | --- |
| invalid_json | 400 | Kein gültiges JSON. |
| unknown_field | 400 | Ein Feld im Rumpf ist unbekannt. `details.field` nennt den Namen. |
| request_too_large | 413 | Der Rumpf ist größer als 1 MiB. |
| invalid_title | 400 | Titel fehlt oder ist zu lang. |
| invalid_start_at | 400 | Beginn fehlt oder ist kein RFC-3339-Zeitpunkt. |
| invalid_end_at | 400 | Ende ist ungültig oder liegt vor dem Beginn. |
| invalid_timezone | 400 | Zeitzone fehlt oder ist keine IANA-Zeitzone. |
| invalid_location_type | 400 | Ortstyp ist nicht offline, online oder hybrid. |
| invalid_visibility | 400 | Sichtbarkeit ist nicht public oder private. |
| invalid_status | 400 | Status ist nicht draft oder published, oder der Statusfilter ist ungültig. |
| invalid_capacity | 400 | Kapazität ist negativ. |
| invalid_category | 400 | Kategorie ist unbekannt. |
| invalid_tags | 400 | Zu viele Schlagworte oder ein Schlagwort ist zu lang. |
| no_changes | 400 | Die Änderung enthält kein Feld. |
| invalid_days | 400 | Der Zeitraum liegt nicht zwischen 1 und 365 Tagen. |
| invalid_limit | 400 | Ein `limit` ist ungültig, etwa keine ganze Zahl oder außerhalb des erlaubten Bereichs. |
| idempotency_conflict | 409 | Derselbe Idempotency-Key wurde mit einem anderen Rumpf verwendet. |
| event_has_registrations | 409 | Ein veröffentlichtes Event mit Anmeldungen lässt sich nicht löschen. `details.count` nennt die Zahl der Anmeldungen. |
| invalid_api_key | 401 | Der Schlüssel fehlt, ist unbekannt oder wurde zurückgezogen. |
| auth_unavailable | 503 | Der Schlüssel konnte nicht geprüft werden. |
| plan_required | 403 | Die Aktion erfordert Eventwerk Plus. |
| forbidden | 403 | Die Rolle erlaubt die Aktion nicht. |
| event_not_found | 404 | Das Event wurde nicht gefunden. |
| ticket_not_found | 404 | Die Ticketart wurde nicht gefunden. |
| registration_not_found | 404 | Die Anmeldung wurde nicht gefunden. |
| not_found | 404 | Die Route existiert nicht. |
| method_not_allowed | 405 | Die Methode ist für diese Route nicht erlaubt. |
| rate_limited | 429 | Mehr als 200 Anfragen pro Minute je Schlüssel und Instanz. |
| internal_error | 500 | Interner Fehler. |
| service_unavailable | 503 | Der Dienst ist vorübergehend nicht verfügbar. |

## Weitere Codes

| Code | Status | Bedeutung |
| --- | --- | --- |
| invalid_webhook_url | 400 | Die Webhook-Adresse ist keine gültige http- oder https-Adresse. |
| webhook_not_found | 404 | Der Webhook wurde nicht gefunden. |
| invalid_name | 400 | Der Name des API-Schlüssels ist leer oder zu lang. |
| key_limit_reached | 400 | Es sind bereits 25 aktive API-Schlüssel vorhanden. |
| key_daily_limit | 400 | Es wurden heute bereits 10 API-Schlüssel angelegt. |
| weekly_send_limit | 403 | Das wöchentliche Versandkontingent ist erreicht. Details enthält limit, used und plan. |
| invalid_unread_only | 400 | unread_only ist nicht true oder false. |
| invalid_notification_id | 400 | Die Kennung einer Benachrichtigung ist ungültig. |

## Fehler behandeln

1. Bei 400 die Anfrage anhand von code, message und details korrigieren. Nicht automatisch wiederholen.
2. Bei 401 den Schlüssel prüfen. Ein zurückgezogener Schlüssel wird nie wieder gültig.
3. Bei 403 den Plan und die Rolle prüfen. Ein Planwechsel hilft sofort.
4. Bei 404 die Kennung prüfen. Die Ressource kann gelöscht worden sein.
5. Bei 409 den Zustand prüfen. `idempotency_conflict` bedeutet einen anderen Rumpf beim selben Schlüssel, `event_has_registrations` ein veröffentlichtes Event mit Anmeldungen.
6. Bei 429 den Wert von Retry-After abwarten.
7. Bei 500 und 503 mit wachsender Pause erneut versuchen.

> **Hinweis:** `POST /events` akzeptiert einen `Idempotency-Key`. Gleicher Schlüssel und gleicher Rumpf liefern dieselbe Antwort, ein anderer Rumpf ergibt 409 `idempotency_conflict`. Ohne Schlüssel legt ein wiederholter POST ein neues Event an.
