Finanzen
Sechs Bereiche, bewusst getrennt: Eine Vereinswebsite, die Zuwendungsbestätigungen
ausstellt, hat nichts in den Kontoauszügen zu suchen – deshalb ist spenden kein Teil von
bank.
Alle Pfade sind relativ zu https://<domain>/api/v1.
Bank
Bereich bank. Konten, Buchungen, Belege und Kostenstellen.
| Methode | Pfad | Bereich | Beschreibung |
|---|---|---|---|
| GET | /bank | bank:read | Konten (Liste) |
| GET | /bank/{accountId} | bank:read | Ein Konto |
| GET | /bank/{accountId}/buchungen | bank:read | Buchungen (Liste) |
| POST | /bank/{accountId}/buchungen | bank:write | Manuelle Buchung erfassen |
| GET | /bank/{accountId}/buchungen/{txId} | bank:read | Eine Buchung |
| PUT | /bank/{accountId}/buchungen/{txId} | bank:write | Zuordnung nachziehen |
| GET | /bank/belege | bank:read | Belege (Liste) |
| POST | /bank/belege | bank:write | Beleg anlegen |
| GET | /bank/belege/{id} | bank:read | Ein Beleg samt Aufteilung |
| PUT | /bank/belege/{id} | bank:write | Beleg ändern |
| DELETE | /bank/belege/{id} | bank:write | Beleg löschen |
| GET | /bank/belege/{id}/inhalt | bank:read | Die Belegdatei |
| PUT | /bank/belege/{id}/inhalt | bank:write | Belegdatei nachreichen |
| GET | /bank/kostenstellen | bank:read | Kostenstellen (Liste) |
| GET | /bank/kategorien | bank:read | Buchungskategorien (Liste) |
Kontoauszüge werden nicht über die Schnittstelle importiert. Der CAMT-Import hat Dubletten-Erkennung, Saldo-Abgleich und Fehlerprotokoll; ein Dateiupload würde die halbe Mechanik umgehen.
Konten anlegen und das Regelwerk ändern bleibt der Anwendung vorbehalten. Beides ändert, wie alle künftigen Buchungen zugeordnet werden – das gehört nicht in ein Skript.
Buchungen
Filter: q (Zweck und Beteiligte), von, bis (Buchungsdatum), kategorie,
kostenstelle.
Sortierbar: booking_date (Standard), value_date, amount.
Manuelle Buchung anlegen (Barkasse, Nachtrag):
| Feld | Typ | Anmerkung |
|---|---|---|
booking_date | JJJJ-MM-TT | Pflicht |
value_date | JJJJ-MM-TT | Wertstellung |
amount | Zahl | Pflicht; negativ = Ausgabe |
currency | 3 Zeichen | |
purpose | Text | Verwendungszweck |
counterparty_iban, debtor_name, creditor_name | Text | Gegenseite |
category, cost_center | Text | |
note | Text |
Beim Ändern einer Buchung sind nur die App-Felder erlaubt: category, cost_center,
note, receipt_ref, reviewed. Betrag und Datum sind das, was die Bank gemeldet hat,
und werden nicht nachträglich über die Schnittstelle verändert.
Belege
Filter: q (Bezeichnung, Belegnummer), von, bis (Belegdatum).
Sortierbar: receipt_date (Standard), amount, created_at.
| Feld | Typ | Anmerkung |
|---|---|---|
description | Text | Pflicht – die Bezeichnung des Belegs |
receipt_date | JJJJ-MM-TT | Pflicht |
amount | Zahl | Pflicht |
direction | income | expense | |
category, cost_center | Text | |
submitter_note | Text | Notiz der einreichenden Person |
title, vendor oder project gibt es an einem Beleg nicht – unter solchen Namen
geschickte Werte würden stillschweigend verschwinden.
Ein GET /bank/belege/{id} liefert den Beleg ohne die Datei: Belege enthalten oft
Adressen und Kontodaten Dritter. Die Datei kommt einzeln über /inhalt – und lässt sich
dort auch nachreichen, etwa aus einem Scan-Ordner:
curl -X PUT -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/pdf" --data-binary @quittung.pdf \
"$BASIS/bank/belege/<id>/inhalt?name=quittung.pdf"
Obergrenze 20 MB (Belege liegen in der Datenbank), darüber 413 too_large.
Beiträge
Bereich beitraege. Eine Sammlung ist ein Anlass („Mitgliedsbeitrag 2026“), eine
Forderung eine einzelne Zahlungspflicht daraus.
| Methode | Pfad | Bereich | Beschreibung |
|---|---|---|---|
| GET | /beitraege | beitraege:read | Forderungen (Liste) |
| POST | /beitraege | beitraege:write | Forderungen anlegen (Stapel) |
| GET | /beitraege/{id} | beitraege:read | Eine Forderung |
| PUT | /beitraege/{id} | beitraege:write | Forderung ändern |
| DELETE | /beitraege/{id} | beitraege:write | Forderung löschen |
| POST | /beitraege/{id}/status | beitraege:write | Status setzen |
| GET | /beitraege/mitglied/{contactId} | beitraege:read | Forderungen einer Person |
| GET | /beitraege/sammlungen | beitraege:read | Sammlungen (Liste) |
| GET | /beitraege/sammlungen/{id} | beitraege:read | Eine Sammlung |
| PUT | /beitraege/sammlungen/{id} | beitraege:write | Sammlung umbenennen |
Filter: q, sammlung_id, status, kostenstelle, zahlweg.
Sortierbar: created_at (Standard), due_date, amount, status.
Forderungen entstehen als Stapel, weil sie in der Praxis so entstehen („allen Wölflingen den Jahresbeitrag stellen“):
{
"sammlung": { "mode": "new", "name": "Mitgliedsbeitrag 2026", "kind": "membership" },
"defaults": { "description": "Jahresbeitrag", "due_date": "2026-03-01", "cost_center": "Verein" },
"forderungen": [
{ "contact_id": "<uuid>", "amount": 42 },
{ "payer_name": "Familie Beispiel", "amount": 84, "description": "zwei Kinder" }
]
}
sammlung.mode ist existing (dann id) oder new (dann name, optional kind:
manual, membership, event). defaults gilt für alle Zeilen, die nichts Eigenes
mitbringen.
Status setzen: {"status":"paid","paid_at":"2026-03-04","payment_method":"lastschrift"}.
Erlaubt sind open, paid, cancelled. Eigener Endpunkt, weil daran Zahldatum und
Zahlweg hängen – ein direktes Schreiben der Spalte hinterließe eine bezahlte Forderung ohne
Zahldatum.
Damit wird Geld eingezogen; das bleibt ein bewusster Vorgang mit der dortigen Vorschau samt Liste der übersprungenen Mandate.
Spenden
Bereich spenden. Zuwendungsbestätigungen samt PDF.
| Methode | Pfad | Bereich | Beschreibung |
|---|---|---|---|
| GET | /spenden | spenden:read | Bestätigungen (Liste) |
| POST | /spenden | spenden:write | Bestätigung ausstellen |
| GET | /spenden/{id} | spenden:read | Eine Bestätigung |
| PUT | /spenden/{id} | spenden:write | Nur die interne Notiz ändern |
| POST | /spenden/{id}/storno | spenden:write | Stornieren (Grund erforderlich) |
| GET | /spenden/{id}/pdf | spenden:read | Die Bestätigung als PDF |
| POST | /spenden/{id}/als-beleg | spenden:write + bank:write | Als Beleg in die Buchhaltung |
| GET | /spenden/einstellungen | spenden:read | Aussteller-Angaben (nur lesend) |
| GET | /spenden/kandidaten | spenden:read | Buchungen, die sich noch bescheinigen lassen |
Filter: q (Name, Nummer), jahr, art (money, goods, waiver),
inkl_storniert.
Sortierbar: issued_on (Standard), receipt_number, amount, donation_date.
| Feld | Typ | Anmerkung |
|---|---|---|
kind | money | goods | waiver | Geld, Sache, Aufwandsverzicht; Standard money |
donor_name | Text | Pflicht |
donor_contact_id | UUID | wenn die Person im Bestand steht |
donor_address | Text | |
amount | Zahl oder Text | Pflicht |
donation_date | JJJJ-MM-TT | bei Einzelzuwendungen |
is_collective, period_from, period_to | Sammelbestätigung über einen Zeitraum | |
goods_description, goods_origin, goods_value_basis | bei Sachzuwendungen | goods_origin: business, private, unknown |
transaction_id | UUID | zugrunde liegende Buchung |
issued_on | JJJJ-MM-TT | |
notes | Text | interne Notiz |
Eine Bestätigung bekommt eine fortlaufende Nummer und geht an das Finanzamt. Es gibt
kein DELETE, sondern nur den Storno mit Begründung – wie in der Anwendung. Und PUT
ändert ausschließlich notes: alles andere steht auf dem Dokument.
Fehlen die Aussteller-Angaben, bricht das Ausstellen mit 400 ab und nennt sie in
details.missing. Was fehlt, sagt vorab auch GET /spenden/einstellungen in
meta.missing – ein Werkzeug soll nicht reihenweise Dokumente erzeugen, die das Finanzamt
zurückweist.
Kandidaten (von, bis, kategorien kommagetrennt, mitglied_id) sind Buchungen
ohne gültige Bestätigung – die Arbeitsliste für ein Spendentool.
Fahrtkosten
Bereich fahrtkosten. Der Weg ist derselbe wie in der Anwendung:
offen → eingereicht → freigegeben → ausgezahlt.
| Methode | Pfad | Bereich | Beschreibung |
|---|---|---|---|
| GET | /fahrtkosten | fahrtkosten:read | Abrechnungen (Liste) |
| POST | /fahrtkosten | fahrtkosten:write | Abrechnung anlegen |
| GET | /fahrtkosten/{id} | fahrtkosten:read | Eine Abrechnung |
| PUT | /fahrtkosten/{id} | fahrtkosten:write | Ändern |
| DELETE | /fahrtkosten/{id} | fahrtkosten:write | Löschen |
| POST | /fahrtkosten/{id}/status | fahrtkosten:write | Statuswechsel |
| GET | /fahrtkosten/{id}/pdf | fahrtkosten:read | Das ausgefüllte Formular als PDF |
| POST | /fahrtkosten/{id}/als-beleg | fahrtkosten:write + bank:write | Als Auslage in die Buchhaltung |
| GET | /fahrtkosten/einstellungen | fahrtkosten:read | Kilometersatz und Formularangaben |
Filter: q (Name, Nummer, Anlass), status, veranstaltung_id, jahr.
Sortierbar: created_at (Standard), claim_number, total_amount, total_km,
status.
| Feld | Typ | Anmerkung |
|---|---|---|
person_name | Text | Pflicht |
person_contact_id, person_address | ||
iban | Text | wird geprüft, wenn angegeben (Barerstattung braucht keine) |
license_plate | Text | |
event_id | UUID | Bezug zur Veranstaltung |
purpose | Text | Anlass |
cost_center, category | Text | |
rate_cents | Zahl | abweichender Kilometersatz |
received_on, received_from | Eingang der Abrechnung | |
notes | Text | |
trips | Liste | die Fahrten |
Eine Fahrt: trip_date, start_address, end_address, reason, km, round_trip,
km_source (manual oder auto).
Kilometer × hinterlegter Satz. Ein mitgeschickter Betrag wird ignoriert. Den geltenden Satz
liefert GET /fahrtkosten/einstellungen – nur lesend, denn daran hängt, was der Verein
tatsächlich auszahlt.
Statuswechsel: {"status":"paid","ausgezahlt_am":"2026-08-01"} – erlaubt sind open,
submitted, approved, paid; ausgezahlt_am gilt nur beim Wechsel auf paid.
Als Beleg ablegen erzeugt eine Auslage samt PDF in der Buchhaltung. Zweimal geht das
nicht (409 conflict), und ohne Fahrten gibt es nichts zu erstatten.
Sie ruft einen fremden Kartendienst auf Kosten des Vereins auf; ein Skript in einer Schleife hätte das Tageskontingent verbraucht, bevor es jemand merkt. Kilometer lassen sich angeben.
Abrechnung
Bereich abrechnung. Eine Abrechnung bündelt Belege (aus dem Belege-Bereich der
Bankbuchhaltung) und Einnahmen zu einer Veranstaltung oder einem Zeitraum.
| Methode | Pfad | Bereich | Beschreibung |
|---|---|---|---|
| GET | /abrechnung | abrechnung:read | Liste |
| POST | /abrechnung | abrechnung:write | Anlegen |
| GET | /abrechnung/{id} | abrechnung:read | Vollständig, mit Belegen, Einnahmen und Kategorien |
| PUT | /abrechnung/{id} | abrechnung:write | Ändern |
| DELETE | /abrechnung/{id} | abrechnung:write | Löschen |
| GET | /abrechnung/{id}/belege | abrechnung:read | Beleg-Auswahl (Liste) |
| PUT | /abrechnung/{id}/belege | abrechnung:write | Beleg-Auswahl vollständig setzen |
| GET | /abrechnung/{id}/einnahmen | abrechnung:read | Einnahmen (Liste) |
| POST | /abrechnung/{id}/einnahmen | abrechnung:write | Einnahme ohne Bankbezug erfassen |
| DELETE | /abrechnung/{id}/einnahmen/{einnahmeId} | abrechnung:write | Einnahme entfernen |
| Feld | Typ | Anmerkung |
|---|---|---|
title | Text | Pflicht |
description | Text | |
event_id | UUID | Bezug zur Veranstaltung |
period_from, period_to | JJJJ-MM-TT | |
responsible_name | Text | |
status | open | closed |
Beleg-Auswahl setzen: {"beleg_ids":["<uuid>","<uuid>"]}. Der Aufruf ersetzt die
Zuordnung vollständig und nummeriert anschließend neu durch – die Nummern auf dem
Beleg-Stempel müssen zur Reihenfolge in der Mappe passen.
Einnahme ohne Bankbezug (Barzahlung, Zuschuss per Scheck): description (Pflicht),
amount (Pflicht), booking_date, note.
Sie kommen aus dem Belege-Bereich, Einnahmen aus den Buchungen. Über die Schnittstelle lässt sich die Auswahl setzen, nicht ein Beleg erzeugen – das wäre eine zweite Belegquelle mit eigener Nummernfolge.
Kalkulation
Bereich kalkulation. Budgetplanung für Lager: Szenarien („knapp“, „realistisch“),
Parameter (Teilnehmerzahl, Tage) und Positionen (Verpflegung, Fahrt).
| Methode | Pfad | Bereich | Beschreibung |
|---|---|---|---|
| GET | /kalkulation | kalkulation:read | Liste |
| POST | /kalkulation | kalkulation:write | Anlegen |
| GET | /kalkulation/{id} | kalkulation:read | Einzeln lesen |
| PUT | /kalkulation/{id} | kalkulation:write | Ändern |
| DELETE | /kalkulation/{id} | kalkulation:write | Löschen |
| POST | /kalkulation/{id}/positionen | kalkulation:write | Position anlegen |
| PUT | /kalkulation/{id}/positionen/{positionId} | kalkulation:write | Position ändern |
| DELETE | /kalkulation/{id}/positionen/{positionId} | kalkulation:write | Position entfernen |
| POST | /kalkulation/{id}/parameter | kalkulation:write | Parameter anlegen |
| PUT | /kalkulation/{id}/parameter/{parameterId} | kalkulation:write | Parameter ändern |
| DELETE | /kalkulation/{id}/parameter/{parameterId} | kalkulation:write | Parameter entfernen |
| PUT | /kalkulation/{id}/werte | kalkulation:write | Werte je Szenario setzen |
| Feld | Typ | Anmerkung |
|---|---|---|
title | Text | Pflicht |
description | Text | |
event_id | UUID | |
fee_round_step | Zahl ≥ 0 | Rundungsschritt des Teilnehmerbeitrags |
fee_round_mode | ceil | floor | round | |
status | draft | final | |
template | Wahrheitswert | nur beim Anlegen: false erzeugt eine leere Kalkulation |
Ohne template: false kommt die übliche Vorlage mit – damit ein Aufruf ohne weitere
Angaben dasselbe Ergebnis liefert wie der Knopf in der Anwendung.
Position: bezeichnung (Pflicht), szenario_id, gruppe, art, formel, note,
sort_order.
Parameter: schluessel (Pflicht), bezeichnung, einheit, note, sort_order.
Werte setzen geht in einem einzigen Aufruf für parameter und positionen zusammen:
{ "parameter": [ { "…": "…" } ], "positionen": [ { "…": "…" } ] }
Einzeln geschrieben stünde die Kalkulation zwischendurch auf halbem Weg – Parameter- und Positionswerte ergeben zusammen eine Rechnung.
Hat dies deine Frage beantwortet?