REST-API

Jede Verein.so-Instanz bringt eine HTTP-Schnittstelle mit. Sie spricht dieselbe Fachlogik wie die Oberfläche und hält sich an dieselben Rechte — nicht an eine zweite, laxere Kopie davon.

In einer Minute

Basisadresse ist die Adresse des Vereins. Angemeldet wird mit einem persönlichen Zugriffs-Token, das der Nutzer selbst unter Einstellungen → Zugriff für KI & Apps anlegt.

Terminal
curl -H "Authorization: Bearer vso_…" \
     https://euer-verein.verein.so/api/v1/ich

Die Antwort sagt, wer man ist, welche Rechte das Token mitbringt und welche Mitglieder man überhaupt sehen darf. Wenn etwas nicht klappt, ist das der erste Aufruf.

Was man vorher wissen sollte

  • Die Schnittstelle ist im Auslieferungszustand aus. Ein Vereinsadmin muss sie freischalten. Vorher antwortet jeder Aufruf mit 403.
  • Ein Token kann nie mehr als sein Besitzer. Es gehört immer genau einem Nutzer und erbt dessen Rollen — bei jeder einzelnen Anfrage neu gelesen, nicht beim Anlegen eingefroren. Wer eine Rolle verliert, verliert sie sofort auch hier.
  • Token können nur lesen, sofern nicht anders angelegt. Ein Nur-Lese-Token bekommt auf jeden schreibenden Aufruf 403, unabhängig von den Rechten des Nutzers.
  • Bankverbindungen kommen nur maskiert ( DE21****5228 ). Das gilt auch für Nutzer, die sie in der Oberfläche vollständig sehen dürfen.
  • 600 Anfragen pro Stunde und Token. Danach 429 mit Retry-After.
  • Es gibt keinen löschenden Endpunkt. Über diese Schnittstelle kann nichts verschwinden.

Format

Alles ist JSON, UTF-8, ohne Umschlag. Zwei Konventionen weichen von dem ab, was man aus englischsprachigen APIs kennt, und zwar mit Absicht:

  • Beträge sind deutsch formatierte Zeichenketten ("1.234,50"), keine Fließkommazahlen. Beim Schreiben werden "-49,90", "-49.90" und -49.9 alle akzeptiert und in Cent umgerechnet.
  • Datumsangaben sind ISO (2026-08-13), damit man damit rechnen kann. Alles andere wird mit einer klaren Meldung abgewiesen.

Feldnamen sind deutsch. Das ist keine Nachlässigkeit: Die Fachbegriffe eines deutschen Vereins haben keine sauberen englischen Entsprechungen, und eine halbübersetzte Zuwendungsbestätigung hilft niemandem.

Fehler

Fehler kommen als { "fehler": "…" } mit einem Text, der sagt, was zu tun ist.

CodeBedeutet
400Eingabe passt nicht — Pflichtfeld fehlt, Betrag oder Datum unlesbar. Die Meldung nennt das Feld.
401Kein, falsches, abgelaufenes oder widerrufenes Token.
403Schnittstelle nicht freigeschaltet, fehlendes Recht, oder Nur-Lese-Token bei einem schreibenden Aufruf.
404Endpunkt gibt es nicht, Datensatz gibt es nicht — oder er liegt außerhalb der eigenen Datensicht. Diese drei Fälle sind bewusst nicht unterscheidbar.
409Vorgang ist bereits abgeschlossen (etwa ein erstatteter Beleg).
429Stundenlimit erreicht.

Der 404 in Zeile vier ist der wichtigste Punkt für Integrationen: Eine Abteilungsleitung, die eine fremde Mitglieds-ID abfragt, bekommt dieselbe Antwort wie bei einer erfundenen ID. Sie soll nicht durch Hochzählen herausfinden können, wer sonst noch im Verein ist.

Zwei vollständige Beispiele

Offene Beiträge holen
curl -H "Authorization: Bearer $VSO_TOKEN" \
     "https://euer-verein.verein.so/api/v1/beitraege/offen?limit=100"

{
  "anzahl": 9,
  "summe_offen_gesamt": "412,00",
  "posten": [
    {
      "id": 41,
      "mitglied_id": 17,
      "mitglied": "Martin Brenner",
      "email": "m.brenner@example.org",
      "betrag": "64,00",
      "zweck": "Jahresbeitrag 2026",
      "faellig": "2026-07-01",
      "lauf": "Beitragslauf 2. Halbjahr 2026"
    }
  ]
}
Buchung anlegen
curl -X POST \
     -H "Authorization: Bearer $VSO_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
           "konto_id": 1,
           "datum": "2026-08-13",
           "betrag": "-49,90",
           "verwendungszweck": "Bälle für die F-Jugend",
           "gegenpartei": "Sporthaus Menzel",
           "kategorie_id": 7
         }' \
     https://euer-verein.verein.so/api/v1/buchungen

{
  "id": 812,
  "ergebnis": "Buchung über -49,90 € auf \"Girokonto\" wurde angelegt (ID 812)."
}

Jede schreibende Anfrage landet im Auditlog des Vereins, mit dem Vermerk quelle: api und dem Namen des Tokens. Das ist nicht abschaltbar.

Die Schnittstelle beschreibt sich selbst

Ein GET auf /api/v1 liefert alle Endpunkte, die das eigene Token benutzen darf, samt Feldern, Typen und Pflichtangaben. Was die Rollen nicht abdecken oder was der Verein abgeschaltet hat, taucht dort gar nicht erst auf.

Die Liste unten stammt aus genau diesem Aufruf gegen eine echte Instanz (Stand 2026-08-13) — sie kann deshalb nicht behaupten, es gäbe etwas, das die Software nicht anbietet.

Alle 26 Endpunkte

Davon 8 schreibend. Jeder Eintrag nennt das benötigte Recht und, falls zutreffend, den Funktionsbereich, der eingeschaltet sein muss.

Selbstauskunft

GET/api/v1/ich

Wer bin ich in diesem Verein, welche Rollen habe ich, und welche Daten darf ich sehen? Immer zuerst aufrufen, wenn unklar ist, was möglich ist.

Werkzeugname:
ich
Benötigt:
portal.nutzen

Mitglieder

GET/api/v1/abteilungen

Alle Abteilungen und ihre Gruppen mit Mitgliederzahl.

Werkzeugname:
abteilungen_liste
Benötigt:
mitglieder.lesen
Nur wenn Bereich aktiv:
abteilungen
PATCH/api/v1/mitglieder/:idverändert Daten

Stammdaten eines Mitglieds ändern. Nur die angegebenen Felder werden überschrieben. Bankverbindung und Status "ausgetreten" laufen bewusst über die Oberfläche.

Werkzeugname:
mitglieder_aendern
Benötigt:
mitglieder.schreiben
9 Felder
  • idganzzahlPflichtaus dem Pfad

    ID des Mitglieds.

  • emailtext

    Neue E-Mail-Adresse.

  • telefontext

    Neue Telefonnummer.

  • strassetext

    Neue Straße.

  • plztext

    Neue Postleitzahl.

  • orttext

    Neuer Ort.

  • statustext

    Neuer Status.

    Erlaubt: interessentaktiv

  • mitgliedschaftsart_idganzzahl

    Neue Mitgliedschaftsart.

  • notizentext

    Notiz ersetzen.

POST/api/v1/mitgliederverändert Daten

Ein neues Mitglied anlegen. Legt bewusst KEINE Bankverbindung an — SEPA-Mandate werden in der Oberfläche erfasst.

Werkzeugname:
mitglieder_anlegen
Benötigt:
mitglieder.schreiben
12 Felder
  • vornametextPflicht

    Vorname.

  • nachnametextPflicht

    Nachname.

  • emailtext

    E-Mail-Adresse.

  • telefontext

    Telefonnummer.

  • geburtsdatumdatum

    Geburtsdatum (JJJJ-MM-TT).

  • strassetext

    Straße und Hausnummer.

  • plztext

    Postleitzahl.

  • orttext

    Ort.

  • eintrittdatum

    Eintrittsdatum (JJJJ-MM-TT).

  • statustext

    Status, Standard "interessent".

    Erlaubt: interessentaktiv

  • mitgliedschaftsart_idganzzahl

    ID der Mitgliedschaftsart (siehe mitgliedschaftsarten_liste).

  • notizentext

    Freitext-Notiz.

GET/api/v1/mitglieder/:id

Ein einzelnes Mitglied mit Abteilungen, Gruppen und offenen Beiträgen.

Werkzeugname:
mitglieder_detail
Benötigt:
mitglieder.lesen
1 Feld
  • idganzzahlPflichtaus dem Pfad

    ID des Mitglieds.

GET/api/v1/mitglieder

Mitglieder suchen und auflisten. Zeigt nur Mitglieder, die ich sehen darf (Trainer und Abteilungsleitung sehen ausschließlich ihre eigenen Gruppen).

Werkzeugname:
mitglieder_liste
Benötigt:
mitglieder.lesen
5 Felder
  • suchetext

    Freitext über Vor-, Nachname, Mitgliedsnummer und E-Mail.

  • statustext

    Mitgliedsstatus.

    Erlaubt: interessentaktivausgetretenabgelehnt

  • abteilung_idganzzahl

    Nur Mitglieder dieser Abteilung.

  • gruppe_idganzzahl

    Nur Mitglieder dieser Gruppe.

  • limitganzzahl

    Wie viele Datensätze höchstens (Standard 50, Maximum 500).

GET/api/v1/mitgliedschaftsarten

Die Mitgliedschaftsarten des Vereins mit Beitragshöhe und Zahlungsintervall.

Werkzeugname:
mitgliedschaftsarten_liste
Benötigt:
mitglieder.lesen

Finanzen

POST/api/v1/buchungenverändert Daten

Eine Buchung erfassen. Negativer Betrag = Ausgabe, positiver = Einnahme.

Werkzeugname:
buchungen_anlegen
Benötigt:
finanzen.buchen
Nur wenn Bereich aktiv:
kasse
8 Felder
  • konto_idganzzahlPflicht

    ID des Kontos (siehe konten_liste).

  • datumdatumPflicht

    Buchungsdatum (JJJJ-MM-TT).

  • betraggeldPflicht

    Betrag in Euro. Ausgaben negativ, z. B. -49,90.

  • verwendungszwecktextPflicht

    Wofür.

  • gegenparteitext

    Wer (Empfänger oder Einzahler).

  • kategorie_idganzzahl

    ID der Kategorie (siehe kategorien_liste).

  • projekt_idganzzahl

    ID des Projekts.

  • notiztext

    Interne Notiz.

GET/api/v1/buchungen

Buchungen durchsuchen und auswerten — nach Zeitraum, Konto, Kategorie oder Freitext. Liefert zusätzlich die Summe der Treffer.

Werkzeugname:
buchungen_liste
Benötigt:
finanzen.lesen
Nur wenn Bereich aktiv:
kasse
8 Felder
  • vondatum

    Ab diesem Datum (JJJJ-MM-TT).

  • bisdatum

    Bis zu diesem Datum (JJJJ-MM-TT).

  • konto_idganzzahl

    Nur dieses Konto.

  • kategorie_idganzzahl

    Nur diese Kategorie.

  • projekt_idganzzahl

    Nur dieses Projekt.

  • ohne_kategorieboolean

    Nur noch nicht kategorisierte Buchungen.

  • suchetext

    Freitext über Gegenpartei und Verwendungszweck.

  • limitganzzahl

    Wie viele Datensätze höchstens (Standard 50, Maximum 500).

PATCH/api/v1/buchungen/:idverändert Daten

Eine vorhandene Buchung kategorisieren oder ergänzen. Betrag, Datum und Konto lassen sich hier bewusst NICHT ändern — das geht nur in der Oberfläche.

Werkzeugname:
buchungen_zuordnen
Benötigt:
finanzen.buchen
Nur wenn Bereich aktiv:
kasse
5 Felder
  • idganzzahlPflichtaus dem Pfad

    ID der Buchung.

  • kategorie_idganzzahl

    Neue Kategorie.

  • projekt_idganzzahl

    Neues Projekt.

  • mitglied_idganzzahl

    Buchung diesem Mitglied zuordnen.

  • notiztext

    Notiz ersetzen.

GET/api/v1/kategorien

Buchungskategorien mit Richtung (Einnahme/Ausgabe) und steuerlicher Zuordnung. Nötig, um Buchungen richtig zu kategorisieren.

Werkzeugname:
kategorien_liste
Benötigt:
finanzen.lesen
Nur wenn Bereich aktiv:
kasse
GET/api/v1/konten

Alle Konten des Vereins mit aktuellem Saldo.

Werkzeugname:
konten_liste
Benötigt:
finanzen.lesen
Nur wenn Bereich aktiv:
kasse
1 Feld
  • jahrganzzahl

    Stichjahr, Standard laufendes Jahr.

GET/api/v1/projekte

Projekte und Kostenstellen mit Budget und bisherigem Verbrauch.

Werkzeugname:
projekte_liste
Benötigt:
projekte.verwalten
Nur wenn Bereich aktiv:
projekte
1 Feld
  • limitganzzahl

    Wie viele Datensätze höchstens (Standard 50, Maximum 500).

Belege, Beiträge, Spenden

GET/api/v1/beitraege/offen

Offene Mitgliedsbeiträge — wer hat noch nicht gezahlt, und wie viel steht insgesamt aus.

Werkzeugname:
beitraege_offen
Benötigt:
beitraege.verwalten
Nur wenn Bereich aktiv:
beitraege
1 Feld
  • limitganzzahl

    Wie viele Datensätze höchstens (Standard 50, Maximum 500).

POST/api/v1/belege/:id/entscheidungverändert Daten

Einen eingereichten Beleg freigeben oder ablehnen. Die eigentliche Auszahlung passiert dadurch nicht — sie läuft weiter über den Erstattungslauf in der Oberfläche.

Werkzeugname:
belege_entscheiden
Benötigt:
belege.freigeben
Nur wenn Bereich aktiv:
belege
3 Felder
  • idganzzahlPflichtaus dem Pfad

    ID des Belegs.

  • entscheidungtextPflicht

    Was passieren soll.

    Erlaubt: freigegebenabgelehnt

  • kommentartext

    Begründung — bei Ablehnung dringend empfohlen.

GET/api/v1/belege

Eingereichte Belege und Erstattungsanträge. Ohne Prüfrecht sind nur die eigenen sichtbar.

Werkzeugname:
belege_liste
Benötigt:
belege.einreichen
Nur wenn Bereich aktiv:
belege
2 Felder
  • statustext

    Nur Belege in diesem Status.

    Erlaubt: eingereichtin_pruefungfreigegebenabgelehnterstattet

  • limitganzzahl

    Wie viele Datensätze höchstens (Standard 50, Maximum 500).

GET/api/v1/spenden

Erfasste Spenden im Zeitraum, mit Angabe, ob bereits eine Zuwendungsbestätigung erstellt wurde.

Werkzeugname:
spenden_liste
Benötigt:
spenden.verwalten
Nur wenn Bereich aktiv:
spenden
2 Felder
  • jahrganzzahl

    Kalenderjahr, Standard laufendes Jahr.

  • limitganzzahl

    Wie viele Datensätze höchstens (Standard 50, Maximum 500).

Berichte

GET/api/v1/berichte/euer

Einnahmen-Überschuss-Rechnung für ein Jahr: alle Einnahmen und Ausgaben nach Kategorie mit Summen.

Werkzeugname:
bericht_euer
Benötigt:
berichte.lesen
Nur wenn Bereich aktiv:
kasse
1 Feld
  • jahrganzzahl

    Kalenderjahr, Standard laufendes Jahr.

GET/api/v1/berichte/jahresabrechnung

Jahresabrechnung nach den vier steuerlichen Bereichen (ideell, Vermögensverwaltung, Zweckbetrieb, wirtschaftlicher Geschäftsbetrieb).

Werkzeugname:
bericht_jahresabrechnung
Benötigt:
berichte.steuer
Nur wenn Bereich aktiv:
steuer
1 Feld
  • jahrganzzahl

    Kalenderjahr, Standard laufendes Jahr.

Vereinsleben

GET/api/v1/sitzungen

Sitzungen und Protokolle mit Anzahl der Tagesordnungspunkte.

Werkzeugname:
sitzungen_liste
Benötigt:
sitzungen.lesen
Nur wenn Bereich aktiv:
sitzungen
1 Feld
  • limitganzzahl

    Wie viele Datensätze höchstens (Standard 50, Maximum 500).

GET/api/v1/termine

Anstehende Fristen und Pflichttermine des Vereins (Steuererklärung, Mitgliederversammlung, Vereinsregister).

Werkzeugname:
termine_liste
Benötigt:
compliance.verwalten
Nur wenn Bereich aktiv:
vertraege
1 Feld
  • limitganzzahl

    Wie viele Datensätze höchstens (Standard 50, Maximum 500).

POST/api/v1/todosverändert Daten

Eine Aufgabe anlegen.

Werkzeugname:
todos_anlegen
Benötigt:
sitzungen.verwalten
4 Felder
  • titeltextPflicht

    Worum geht es.

  • beschreibungtext

    Details.

  • faelligdatum

    Fällig bis (JJJJ-MM-TT).

  • mitglied_idganzzahl

    Wer ist zuständig.

POST/api/v1/todos/:id/erledigtverändert Daten

Eine Aufgabe auf erledigt setzen.

Werkzeugname:
todos_erledigen
Benötigt:
sitzungen.verwalten
1 Feld
  • idganzzahlPflichtaus dem Pfad

    ID der Aufgabe.

GET/api/v1/todos

Offene Aufgaben des Vorstands, zum Beispiel Beschlüsse aus Sitzungen.

Werkzeugname:
todos_liste
Benötigt:
sitzungen.lesen
2 Felder
  • statustext

    Standard "offen".

    Erlaubt: offenerledigt

  • limitganzzahl

    Wie viele Datensätze höchstens (Standard 50, Maximum 500).

POST/api/v1/veranstaltungenverändert Daten

Eine Veranstaltung anlegen. Sie startet im Status "geplant" und wird erst nach Freigabe in der Oberfläche für Anmeldungen geöffnet.

Werkzeugname:
veranstaltungen_anlegen
Benötigt:
veranstaltungen.verwalten
Nur wenn Bereich aktiv:
veranstaltungen
7 Felder
  • titeltextPflicht

    Titel der Veranstaltung.

  • vondatumPflicht

    Datum (JJJJ-MM-TT).

  • uhrzeittext

    Startzeit als HH:MM, Standard 00:00.

  • orttext

    Veranstaltungsort.

  • beschreibungtext

    Beschreibungstext.

  • gebuehrgeld

    Teilnahmegebühr in Euro, Standard 0.

  • max_teilnehmerganzzahl

    Höchstzahl Teilnehmer.

GET/api/v1/veranstaltungen

Veranstaltungen des Vereins mit Anmeldezahlen.

Werkzeugname:
veranstaltungen_liste
Benötigt:
portal.nutzen
Nur wenn Bereich aktiv:
veranstaltungen
2 Felder
  • abdatum

    Nur Veranstaltungen ab diesem Datum. Ohne Angabe: alle kommenden.

  • limitganzzahl

    Wie viele Datensätze höchstens (Standard 50, Maximum 500).

Lieber im Gespräch statt im Code?

Dieselben Aktionen gibt es als MCP-Server für Claude und ChatGPT. Es ist derselbe Katalog und dieselbe Rechteprüfung — nur die Verpackung unterscheidet sich.