Clubnetz

Dokumentation

Clubnetz-API

Verbinde eigene Skripte und Werkzeuge mit deinem Verein. Alles, was die App für einen Vereins-Admin kann, steht mit einem API-Schlüssel auch über HTTP zur Verfügung: Buchungen, Mitglieder, Saisonen, Termine, Neuigkeiten, Platzsperren und mehr.

Was du damit bauen kannst

Die API ist für Vereins-Admins gedacht, die ein wenig programmieren können und sich wiederkehrende Arbeit sparen wollen. Ein paar Ideen:

  • Die heute freien Plätze oder die nächsten Vereinstermine auf der Vereinswebsite anzeigen.
  • Die Mitgliederliste jede Nacht in eine Tabelle oder die Buchhaltung exportieren.
  • Die Platzsperren für die gesamte Mannschaftsmeisterschaft aus einer Terminliste anlegen.
  • Die Buchungen des Tages auf einem Bildschirm im Clubhaus zeigen.
  • Automatisch eine Neuigkeit veröffentlichen, sobald die Ergebnisse der Liga da sind.

Es gibt keine eigene „öffentliche API“ mit weniger Funktionen: Ein Schlüssel verwendet dieselben Endpunkte wie die Clubnetz-App selbst.

API-Schlüssel erstellen

  1. Melde dich als Admin deines Vereins bei Clubnetz an.
  2. Öffne im Menü Admin → API-Schlüssel.
  3. Wähle Neuer Schlüssel, gib ihm einen Namen, der sagt, wofür er da ist, und lege fest, wie lange er gültig ist.
  4. Entscheide, was er darf: Nur lesen für Exporte und Anzeigen, Lesen und schreiben, wenn er etwas ändern soll.
  5. Kopiere den Schlüssel. Er beginnt mit cnz_ und wird nur einmal angezeigt.

Verwende einen Schlüssel pro Skript oder Werkzeug. So siehst du, welcher zuletzt verwendet wurde, und kannst einen einzelnen widerrufen, ohne die anderen lahmzulegen.

Die erste Anfrage

Sende den Schlüssel im Header Authorization mit. Diese Anfrage listet die Plätze deines Vereins auf:

curl -H "Authorization: Bearer cnz_DEIN_SCHLUESSEL" \
  https://my.clubnetz.app/api/Clubs/12/Courts

Die Antwort ist JSON:

{
  "courts": [
    { "courtId": 1, "name": "Platz 1", "alias": "1", "sortOrder": 1, "inactive": null },
    { "courtId": 2, "name": "Platz 2", "alias": "2", "sortOrder": 2, "inactive": null }
  ]
}

Die Adresse deines Vereins

Jede Adresse beginnt mit https://my.clubnetz.app/api/Clubs/clubId/. Die Nummer deines Vereins steht in der Adresszeile, wenn du die App verwendest (/clubs/12/my-club), und die Seite „API-Schlüssel“ zeigt ein fertiges Beispiel damit.

Betreibt euer Verein eine eigene Installation von Clubnetz, ersetze my.clubnetz.app durch eure Adresse.

So funktioniert ein Schlüssel

Er handelt als du
Ein Schlüssel kann, was der Admin, der ihn erstellt hat, im Verein kann. Änderungen damit werden als von dieser Person gemacht festgehalten.
Er gilt nur für einen Verein
Ein Schlüssel ist an den Verein gebunden, in dem er erstellt wurde. Für einen anderen Verein, dein persönliches Profil oder irgendetwas außerhalb von /api/Clubs/clubId/ funktioniert er nicht.
Nur-Lese-Schlüssel lesen nur
Ein Schlüssel mit „Nur lesen“ darf GET-Anfragen senden. Alles andere wird mit 403 beantwortet.
Er endet, wenn du kein Admin mehr bist
Verlässt die Person, die den Schlüssel erstellt hat, den Verein oder ist sie kein Admin mehr, wird der Schlüssel abgelehnt. Ein anderer Admin erstellt dann einen neuen.
Er kann keine Schlüssel und kein Stripe-Konto verwalten
Schlüssel werden nur in der App von einem angemeldeten Admin erstellt und widerrufen, nie mit einem Schlüssel. Dasselbe gilt für das Verbinden und Trennen des Stripe-Kontos des Vereins.
Jeder Admin kann ihn widerrufen
Das Widerrufen wirkt sofort und kann nicht rückgängig gemacht werden.

Datenformate

WasFormatBeispiel
Anfragen und AntwortenJSON mit Namen in camelCase. Sende bei einem Inhalt Content-Type: application/json mit.{ "courtId": 1 }
Ein TagJahr, Monat, Tag2027-06-12
Ein ZeitpunktISO 8601 mit Zeitverschiebung; Antworten sind in UTC2027-06-12T16:00:00+00:00
Eine UhrzeitStunden, Minuten, Sekunden in der Ortszeit des Vereins18:00:00
Feste AuswahlwerteText, keine Zahlen (auf Englisch)"Social", "Admin"
DateienExcel-Exporte und Anhänge kommen als Download zurück

Gib bei Buchungen den Namen deiner Zeitzone als timeZoneInfoId an, zum Beispiel Europe/Vienna.

Beispiele

Die Buchungen einer Woche

curl -H "Authorization: Bearer cnz_DEIN_SCHLUESSEL" \
  "https://my.clubnetz.app/api/Clubs/12/Bookings?dayFrom=2027-06-07&dayTo=2027-06-13"

Jede Buchung enthält ihren Platz, ihre Zeit (interval.from und interval.to), die Spielart und die Spieler.

Die Mitgliederliste, Seite für Seite

curl -H "Authorization: Bearer cnz_DEIN_SCHLUESSEL" \
  "https://my.clubnetz.app/api/Clubs/12/Members?Page=1&PageSize=50&EnablePaging=true"

Die Antwort enthält total und die Mitglieder der Seite in entities. Filtern kannst du mit SearchTerm, Roles, InSeasonIds und weiteren Parametern, siehe die Referenz unten.

Einen Vereinstermin anlegen

Dafür braucht es einen Schlüssel mit Lesen und schreiben. Die Antwort ist die Id des neuen Termins.

curl -X POST \
  -H "Authorization: Bearer cnz_DEIN_SCHLUESSEL" \
  -H "Content-Type: application/json" \
  -d '{
        "title": "Sommerfest",
        "category": "Social",
        "startDate": "2027-06-12",
        "startTime": "18:00:00",
        "location": "Clubhaus",
        "registrationEnabled": true,
        "notifyMembers": false
      }' \
  https://my.clubnetz.app/api/Clubs/12/ClubEvents

Mit "notifyMembers": true bekommen alle Mitglieder eine Push-Benachrichtigung und eine E-Mail zum neuen Termin. Lass es beim Testen ausgeschaltet.

Python: alle Mitglieder in eine CSV-Datei exportieren

import csv, os, requests

BASE = "https://my.clubnetz.app/api/Clubs/12"
HEADERS = {"Authorization": f"Bearer {os.environ['CLUBNETZ_API_KEY']}"}

members, page = [], 1
while True:
    response = requests.get(f"{BASE}/Members", headers=HEADERS,
                            params={"Page": page, "PageSize": 100, "EnablePaging": "true"})
    response.raise_for_status()
    data = response.json()
    members += data["entities"]
    if len(members) >= data["total"] or not data["entities"]:
        break
    page += 1

with open("mitglieder.csv", "w", newline="", encoding="utf-8") as file:
    writer = csv.writer(file)
    writer.writerow(["Vorname", "Nachname", "E-Mail", "Rollen"])
    for m in members:
        writer.writerow([m["firstName"], m["lastName"], m["email"], ", ".join(m["roles"])])

JavaScript (Node.js): die nächsten Vereinstermine

const base = "https://my.clubnetz.app/api/Clubs/12";
const headers = { Authorization: `Bearer ${process.env.CLUBNETZ_API_KEY}` };

const today = new Date().toISOString().slice(0, 10);
const inAMonth = new Date(Date.now() + 30 * 86400000).toISOString().slice(0, 10);

const response = await fetch(`${base}/ClubEvents?from=${today}&to=${inAMonth}`, { headers });
if (!response.ok) throw new Error(`Clubnetz antwortet mit ${response.status}`);

const { events } = await response.json();
for (const event of events) {
    console.log(event.startDate, event.startTime ?? "ganztägig", event.title);
}

Lass solche Skripte auf einem Server oder deinem eigenen Computer laufen. Wenn du etwas auf der Vereinswebsite zeigen willst, lass deinen Server die Daten holen und nur das weitergeben, was Besucher sehen sollen. Der Schlüssel gehört nie in die Seite selbst.

Fehler und Limits

StatusBedeutungWas tun
200Es hat funktioniert.
401Der Schlüssel fehlt, ist falsch, abgelaufen, widerrufen, gehört zu einem anderen Verein, oder sein Ersteller ist kein Admin mehr.Prüfe den Schlüssel und die Vereinsnummer in der Adresse.
403Der Schlüssel darf nur lesen (ApiKeyReadOnly), oder du darfst das nicht.Verwende einen Schlüssel mit „Lesen und schreiben“.
404Das Angefragte gibt es in deinem Verein nicht.Prüfe die Id.
412Die Anfrage verletzt eine Regel des Vereins oder enthält ungültige Daten.Lies errorCode und message in der Antwort.
429Zu viele Anfragen.Warte so viele Sekunden, wie im Header Retry-After stehen.

Ein Fehler, der deine Eingabe betrifft, sieht so aus:

{
  "message": "A title is required.",
  "errorCode": "ClubEventTitleRequired",
  "errorDetail": null
}

Verwende in deinem Code errorCode; der Text in message ist englisch und kann sich ändern.

Limit

Jeder Schlüssel darf 120 Anfragen pro Minute senden. Das reicht für Exporte und Anzeigen bei Weitem. Wenn du viele Daten lädst, frage größere Seiten ab, statt viele kleine Anfragen zu senden.

Schlüssel sicher aufbewahren

  • Behandle einen Schlüssel wie ein Passwort. Wer ihn hat, kann die Daten eurer Mitglieder lesen.
  • Speichere ihn in einer Umgebungsvariable oder einem Secret-Speicher, nicht im Code und nie in einem öffentlichen Repository.
  • Er gehört nie in eine Website oder eine Handy-App, wo ihn jeder auslesen kann. Die API beantwortet außerdem keine Anfragen, die ein Browser von einer anderen Website aus sendet.
  • Bevorzuge Schlüssel mit „Nur lesen“ und gib Schlüsseln ein Ablaufdatum.
  • Könnte ein Schlüssel bekannt geworden sein, widerrufe ihn sofort unter Admin → API-Schlüssel und erstelle einen neuen.

Änderungen an der API

Die App und die API werden gemeinsam veröffentlicht, ein Endpunkt kann sich also mit einer neuen Version von Clubnetz ändern. Jede Antwort enthält die laufende Version im Header X-App-Version. Wenn dir dein Skript wichtig ist, protokolliere diesen Header und prüfe dein Skript nach einem Update. Die Referenz unten beschreibt immer die Version, die gerade läuft.

Referenz der Endpunkte

Alle Endpunkte, die ein Schlüssel aufrufen kann, live aus dem OpenAPI-Dokument von Clubnetz geladen. Die Pfade sind ohne das führende /api/Clubs/clubId angegeben. Namen und Beschreibungen in der Referenz sind englisch.

OpenAPI-Dokument herunterladen

Diese Datei kannst du in Werkzeuge wie Postman, Insomnia oder Bruno importieren oder daraus einen Client für deine Programmiersprache erzeugen.

Fehlt etwas?

Schreib uns, was du bauen möchtest, oder eröffne ein Issue auf GitHub. Clubnetz ist Open Source, du kannst es also auch selbst ergänzen.

hello@clubnetz.app