Konventionen
Jeder Endpunkt der fepli-API folgt denselben Regeln für Kennungen, Datumsangaben, Geld, Filter und Schreibzugriffe. Sie werden hier einmal erklärt, damit die Referenz der Ressourcen kurz bleiben kann.
Anfragen und Antworten
Die API spricht nur JSON. Antworten sind application/json, Fehler sind application/problem+json (siehe Fehler).
Anfragen mit einem Body (POST, PATCH) müssen Content-Type: application/json senden. Bei PATCH funktioniert auch application/merge-patch+json. Ein Request-Body ohne JSON-Content-Type wird mit 415 Unsupported Media Type abgelehnt.
Eine Schreibanfrage
curl -X PATCH https://example.fepli.eu/api/offers/0192a4f1-6e3b-7c85-b1d2-8f4e6a9c3b03 \
-H "Authorization: Bearer $FEPLI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"maxParticipants": 15}'
Die Feldnamen sind englisch. Inhalte wie Namen und Beschreibungen von Angeboten kommen so zurück, wie sie eingegeben wurden, meist auf Deutsch.
Kennungen
Jeder Datensatz hat eine UUID (RFC 4122), die als uuid zurückkommt:
0192a4f1-6e3b-7c85-b1d2-8f4e6a9c3b03
Die URLs des Admin-Panels enthalten dieselbe UUID in einer kürzeren Base58-Form (22 Zeichen, zum Beispiel 1C8wV6pQk2Tz9RfXh4MbNd). Die API akzeptiert beide Formen überall, wo sie eine Kennung liest: in Pfaden, Filtern und Request-Bodys. Sie können also die einfügen, die Sie zur Hand haben. Antworten verwenden immer die Form nach RFC 4122.
Manche Datensätze können in Filtern und Request-Bodys auch über etwas Einprägsameres benannt werden:
| Datensatz | Ebenfalls akzeptiert |
|---|---|
| Saison | ihr alias, z. B. herbstferien-2026 |
| Veranstalter:in | ihr alias, z. B. stadtjugendring-elmsbrueck |
| Kategorie | ihr alias, z. B. sport-bewegung |
| Konto | seine E-Mail-Adresse |
Pfade nehmen eine UUID (in beiden Formen), nie einen Alias: /editions/herbstferien-2026 funktioniert nicht, ?edition=herbstferien-2026 funktioniert.
Referenzen
Verweist ein Datensatz auf einen anderen, bettet er eine kleine Referenz ein, die genug enthält, um den Datensatz anzuzeigen und abzurufen:
Eine Referenz
{
"uuid": "01913e5c-2b4a-7d10-9f3e-6a1c2e7b4d01",
"name": "Herbstferien 2026",
"alias": "herbstferien-2026"
}
- Name
uuid- Typ
- string
- Beschreibung
Die Kennung, mit der Sie den vollständigen Datensatz abrufen.
- Name
name- Typ
- string
- Beschreibung
Ein Anzeigename: der vollständige Name einer Person, der Titel eines Angebots, die Nummer eines Belegs. Fehlt, wenn die Daten der Person maskiert sind.
- Name
alias- Typ
- string
- Beschreibung
Der URL-taugliche Alias, bei Datensätzen, die einen haben (Saisons, Veranstalter:innen, Angebote, Kategorien).
Manche Listen verwandter Datensätze enthalten stattdessen reine UUID-Strings, zum Beispiel receipts bei einer Anmeldung.
Leere Felder fehlen
Felder, deren Wert null ist, werden in Antworten ausgelassen. Ein Angebot ohne Treffpunkt hat schlicht keinen Schlüssel meetingPoint. Behandeln Sie ein fehlendes Feld wie null.
Listen, die für Sie gelten, sind immer vorhanden, leere kommen als [] zurück. Felder, die Sie nicht sehen dürfen, etwa die internen Felder eines Angebots ohne Token, fehlen ganz. Booleans und Zahlen mit einem Standardwert sind immer vorhanden.
Eine Ausnahme sind einige verschachtelte Listen einfacher Werte, etwa die tasks einer Saison, die items eines Belegs und die bookEntries einer Debitor:in: Dort erscheinen leere Werte als null.
Datum und Uhrzeit
Zeitstempel folgen ISO 8601 mit Offset, in der Ortszeit der Organisation (Europe/Berlin):
2026-10-14T09:00:00+02:00
Wenn Sie einen Zeitstempel senden, geben Sie den Offset an. Werte nur mit Datum (etwa das dateOfBirth einer Teilnehmer:in) werden als 2015-04-12 akzeptiert.
Datumsfilter wie from, until oder modifiedSince akzeptieren ein Datum (2026-10-01) oder einen vollständigen Zeitstempel. Ein nicht lesbares Datum ist ein 400.
Geld
Beträge sind ganze Zahlen in Cent. fee: 750 sind 7,50 €. Negative Beträge erscheinen bei Stornierungen und in Freien Buchungen: siehe Belege und Debitor:innen.
Filter
Endpunkte für Sammlungen nehmen ihre Filter als Query-Parameter. Jede Ressource nennt ihre eigenen.
curl "https://example.fepli.eu/api/attendances?edition=herbstferien-2026&status=confirmed,waitlisted&paid=false" \
-H "Authorization: Bearer $FEPLI_TOKEN"
- Mehrere Filter werden kombiniert: Ein Datensatz muss alle erfüllen.
- Mehrere Werte eines Filters werden, wo der Filter es erlaubt, durch Kommas getrennt und mit ODER kombiniert:
status=confirmed,waitlisted. - Booleans sind
trueoderfalse(1/0undyes/nofunktionieren ebenfalls). - Die Freitextsuche (
q) findet jeden Teil der Felder, die die Ressource nennt, ohne Beachtung der Groß- und Kleinschreibung. - Ein fehlerhafter Wert, etwa ein Datum, das sich nicht lesen lässt, ein
true, das kein Boolean ist, oder ein unbekanntessort, ist ein400 Bad Request, dessendetailden Parameter nennt. - Eine Referenz, die nichts findet, etwa
?edition=mit einem Alias, den es nicht gibt, ist kein Fehler. Die Antwort ist eine leere Liste. (Eine Ausnahme ist der Filterofferder Anmeldungen: Ein unbekanntes Angebot ist ein404.) - Unbekannte Parameter werden ignoriert.
Schreibzugriffe
| Methode | Zweck | Erfolg |
|---|---|---|
POST /things | einen Datensatz anlegen | 201 Created mit dem neuen Datensatz |
PATCH /things/{uuid} | einen Datensatz ändern | 200 OK mit dem geänderten Datensatz |
DELETE /things/{uuid} | einen Datensatz löschen | 204 No Content |
POST /things/{uuid}/action | eine Aktion: ein Übergang, eine Stornierung, ein Widerruf | 200 OK mit dem Datensatz, der dabei entsteht |
POST /things/{uuid}/action | eine Aktion, die einen neuen Datensatz erzeugt: eine Kopie, eine Variante, die Übertragung auf ein anderes Angebot | 201 Created mit dem neuen Datensatz |
POST /things/{uuid}/action | eine Aktion, die im Hintergrund läuft: eine Warteliste auffüllen, Entscheidungen festschreiben | 202 Accepted mit dem Datensatz, wie er jetzt ist |
So funktioniert PATCH
PATCH wendet nur die Schlüssel an, die Sie senden:
- Ein Schlüssel, den Sie weglassen, bleibt unberührt.
- Ein Schlüssel, den Sie als
nullsenden, leert das Feld. - Eine Liste, die Sie senden (zum Beispiel
dates,hostsodercategorieseines Angebots), ersetzt die ganze Liste. - Ein leerer Body ist ein
422: Es gibt nichts zu ändern. (Saisons sind die Ausnahme und kommen unverändert zurück.)
Den Beitrag ändern, den Treffpunkt leeren
curl -X PATCH https://example.fepli.eu/api/offers/0192a4f1-6e3b-7c85-b1d2-8f4e6a9c3b03 \
-H "Authorization: Bearer $FEPLI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"fee": 500, "meetingPoint": null}'
Felder, die immer einen Wert haben müssen, etwa ein Name oder die Saison eines Angebots, ignorieren null. Jede Ressource nennt sie.
Validierung
Schreibzugriffe werden nach denselben Regeln validiert wie die Formulare des Admin-Panels. Ungültige Eingaben ergeben ein 422 Unprocessable Entity. Sein detail enthält immer eine Zeile pro Problem, jeweils beginnend mit dem Feld:
422 Unprocessable Entity
{
"type": "/validation_errors/04b91c99-a946-4221-afc5-e65ebac401eb",
"title": "An error occurred",
"status": 422,
"detail": "maxAge: This value should be between 0 and 99.",
"violations": [
{
"propertyPath": "maxAge",
"message": "This value should be between 0 and 99.",
"code": "04b91c99-a946-4221-afc5-e65ebac401eb"
}
]
}
Ist ein Wert fehlerhaft oder außerhalb des zulässigen Bereichs, wie oben, nennt die Antwort jedes Problem zusätzlich in violations. Ist eine Anfrage korrekt aufgebaut, ergibt aber für den Datensatz keinen Sinn, etwa eine Saison, die es nicht gibt, oder ein Betrag über dem Beitrag, erhalten Sie nur das detail. Wenn Sie detail lesen, sind Sie in beiden Fällen gut bedient.
Workflows
Angebote und Anmeldungen durchlaufen Status, genau wie im Admin-Panel. Sie setzen ihren Status nicht direkt. Stattdessen wenden Sie mit POST …/{uuid}/transition einen Übergang an:
curl -X POST https://example.fepli.eu/api/attendances/0192d0e8-3a4b-7c5d-9e6f-7a8b9c0d1e07/transition \
-H "Authorization: Bearer $FEPLI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"transition": "confirm", "notify": true}'
Jedes Angebot und jede Anmeldung nennt ihre möglichen Übergänge in transitions. Bei Angeboten berücksichtigt die Liste bereits Ihre Berechtigungen. Bei Anmeldungen spiegelt sie nur den Status. Versuchen Sie einen Übergang, der nicht möglich ist, lautet die Antwort 409 Conflict, und sie nennt die möglichen:
409 Conflict
{
"type": "/errors/409",
"title": "An error occurred",
"status": 409,
"detail": "The transition \"noshow\" is not possible from \"waitlisted\". Possible: confirm, withdraw, reset, reject, unfulfill, participated."
}
Die Übergänge haben dieselben Nebenwirkungen wie im Admin-Panel: E-Mails an Familien (wenn Sie sie anfordern), neu sortierte Teilnahmelisten, aktualisierte Zahlungsdaten.
Caching
Antworten auf authentifizierte Anfragen tragen Cache-Control: private, no-store. Sie sind die Sicht einer einzelnen Person und dürfen nie von einem gemeinsamen Cache gespeichert werden. Anonyme Lesezugriffe auf die öffentlichen Angebote bleiben cachebar.