Schnittstelle (API und Webhooks)
Für Ihren Shop, Ihre Warenwirtschaft oder Ihren Programmierer: Rechnungen und Kunden aus RCDisi lesen und anlegen — und sofort erfahren, wenn eine Rechnung ausgestellt oder bezahlt ist. Fassung 1.
Einrichten (Inhaber oder Administrator): Verwaltung → Automatik → Schnittstelle (API und Webhooks).
- API-Schlüssel erzeugen, Rechte ankreuzen. Der Schlüssel (
rcd_…) erscheint einmal — sofort kopieren und sicher ablegen. - Webhook anlegen: Ihre Adresse (https) und die Ereignisse. Das Geheimnis (
whsec_…) erscheint einmal — damit prüfen Sie die Signatur. - Basisadresse:
https://rcdisi.de/api/v1
Ein API-Schlüssel ist wie ein Passwort. Nicht in eine Webseite, eine App oder ein öffentliches Verzeichnis schreiben — nur auf Ihrem Server. Nur die Rechte geben, die das Programm braucht. Wird ein Schlüssel bekannt: sofort widerrufen (gleicher Ort) und einen neuen erzeugen. Wer die Daten über die Schnittstelle bekommt, verarbeitet sie in Ihrem Auftrag — dafür brauchen Sie mit ihm ggf. einen eigenen Vertrag zur Auftragsverarbeitung.
1. Anmelden
Jede Anfrage trägt den Schlüssel im Kopf:
Authorization: Bearer rcd_IhrSchluessel…
| Recht | erlaubt |
|---|---|
rechnungen:lesen | Rechnungen lesen (mit Stand und PDF) |
rechnungen:schreiben | Rechnungen ausstellen |
kunden:lesen | Kunden lesen |
kunden:schreiben | Kunden anlegen |
2. Die Wege
| Weg | Recht | was |
|---|---|---|
GET /ich | — | Ihr Betrieb und die Rechte dieses Schlüssels (zum Ausprobieren) |
GET /rechnungen | rechnungen:lesen | Kundenrechnungen und Direktabrechnungen, älteste zuerst. Filter: seit=2026-11-01 (angelegt ab),
bezahlt=true|false, limit=1…200 (Vorgabe 50), nach=<id> (nächste Seite) |
GET /rechnungen/{id} | rechnungen:lesen | eine Rechnung |
GET /rechnungen/{id}/pdf | rechnungen:lesen | das PDF (nur für Rechnungen, die in RCDisi geschrieben wurden) |
POST /rechnungen | rechnungen:schreiben | eine Rechnung ausstellen — wie „Rechnung schreiben": Nummer, PDF mit E-Rechnung. Sie wird nicht verschickt. |
GET /kunden | kunden:lesen | Kunden, älteste zuerst. suche= (Name, Kundennummer, E-Mail), limit, nach |
GET /kunden/{id} | kunden:lesen | ein Kunde |
POST /kunden | kunden:schreiben | einen Kunden anlegen |
Seiten: Steht in der Antwort "weiter": "<id>", gibt es mehr — dieselbe Anfrage mit nach=<id>. Bei "weiter": null ist alles da.
Beispiele
# Ausprobieren
curl -H "Authorization: Bearer $RCDISI_SCHLUESSEL" https://rcdisi.de/api/v1/ich
# Offene Rechnungen
curl -H "Authorization: Bearer $RCDISI_SCHLUESSEL" "https://rcdisi.de/api/v1/rechnungen?bezahlt=false&limit=100"
# Einen Kunden anlegen
curl -X POST https://rcdisi.de/api/v1/kunden \
-H "Authorization: Bearer $RCDISI_SCHLUESSEL" -H "Content-Type: application/json" \
-H "Idempotency-Key: shop-kunde-4711" \
-d '{"name":"Muster GmbH","nummer":"K-4711","email":"buchhaltung@muster.example","strasse":"Hauptstraße 1","plzOrt":"12345 Musterstadt"}'
# Eine Rechnung ausstellen
curl -X POST https://rcdisi.de/api/v1/rechnungen \
-H "Authorization: Bearer $RCDISI_SCHLUESSEL" -H "Content-Type: application/json" \
-H "Idempotency-Key: shop-bestellung-10023" \
-d '{"kunde":{"name":"Muster GmbH","nummer":"K-4711","strasse":"Hauptstraße 1","plzOrt":"12345 Musterstadt","email":"buchhaltung@muster.example"},
"datum":"2026-11-03","zielTage":14,"basis":"netto","betreff":"Bestellung 10023",
"positionen":[{"bezeichnung":"Beratung","menge":2,"einheit":"Std.","einzel":90,"steuersatz":19}]}'
Eine Rechnung ausstellen — die Angaben
| Feld | |
|---|---|
kunde | name, strasse, plzOrt (Pflicht), dazu nummer, zusatz, land, email, ustid.
Gibt es die Kundennummer schon, kommt die Rechnung in diese Akte; ohne Nummer vergibt RCDisi die nächste freie. |
positionen | je Zeile bezeichnung, menge, einzel (Einzelpreis in Euro), steuersatz (19, 7 oder 0),
dazu einheit, beschreibung |
basis | netto oder brutto — gilt der Einzelpreis mit oder ohne Umsatzsteuer (Vorgabe: Ihre Einstellung) |
datum, leistungVon, leistungBis | wie 2026-11-03 (Vorgabe: heute) |
zielTage | Zahlungsziel in Tagen (Vorgabe: Ihre Einstellung) |
betreff, einleitung, schluss | Texte auf der Rechnung |
Fehlt eine Pflichtangabe, antwortet RCDisi mit 400 und sagt, was fehlt — es gibt dann keine Nummer und keine Rechnung. Angebote, Abos, Abschlags- und Schlussrechnungen, Korrekturen und Gutschriften gehen über das Programm, nicht über die Schnittstelle.
Doppelt geschickt? Idempotency-Key
Schicken Sie bei POST einen eigenen, eindeutigen Wert mit (z. B. Ihre Bestellnummer). Kommt dieselbe Anfrage innerhalb von 24 Stunden
noch einmal — etwa weil die Verbindung abriss —, bekommen Sie die erste Antwort zurück (mit "wiederholt": true) und es entsteht keine zweite Rechnung.
Was zurückkommt
{ "id": "…", "nummer": "RE-2026-0042", "art": "kundenrechnung", "kunde": "Muster GmbH", "kundennummer": "K-4711",
"datum": "2026-11-03", "faellig": "2026-11-17", "betrag": 214.2, "waehrung": "EUR", "offen": 214.2, "bezahlt": false,
"bezahltAm": null, "storniert": false, "mahnstufe": 0, "inRcdisiGeschrieben": true, "pdf": true }
{ "id": "…", "kundennummer": "K-4711", "name": "Muster GmbH", "email": "buchhaltung@muster.example", "telefon": "", "anschrift": "…" }
Fehler und Grenzen
| Status | bedeutet |
|---|---|
| 400 | Angabe fehlt oder stimmt nicht — {"error": "…"} sagt, was |
| 401 | kein oder ein widerrufener Schlüssel |
| 403 | dem Schlüssel fehlt das Recht, oder der Zugang des Betriebs ist beendet/gesperrt |
| 404 | gibt es nicht (oder gehört nicht zu Ihrem Betrieb) |
| 409 | gibt es schon (z. B. die Kundennummer) oder derselbe Idempotency-Key für einen anderen Weg |
| 423 | der Vertrag ist zu Ende — nur noch lesen |
| 429 | mehr als 120 Anfragen in einer Minute mit diesem Schlüssel — kurz warten |
3. Webhooks — RCDisi meldet sich bei Ihnen
Bei jedem bestellten Ereignis schickt RCDisi ein POST mit JSON an Ihre Adresse. Antworten Sie innerhalb von 10 Sekunden mit einem Status 2xx —
dann gilt die Meldung als angekommen. Umleitungen (3xx) folgt RCDisi nicht.
| Ereignis | wann | daten |
|---|---|---|
rechnung.ausgestellt | eine Rechnung wurde in RCDisi geschrieben (auch über die Schnittstelle) | id, nummer, art, datum, bruttoCent, betrag, waehrung, kunde, kundennummer |
rechnung.storniert | sie wurde storniert | dasselbe; nummer ist die des Stornos, stornoZu die der Rechnung, id die Rechnung |
rechnung.bezahlt | sie gilt als bezahlt (Kontoauszug, Online-Zahlung oder von Hand) | id, nummer, betrag, waehrung, kunde, kundennummer, bezahltAm, vonHand |
kunde.angelegt | ein Kunde wurde angelegt | id, kundennummer, name, email |
angebot.angenommen | ein Angebot wurde angenommen | id, nummer, art, kunde |
probe.ping | „Probe senden" in der Verwaltung | nachricht |
POST https://ihr-programm.example/rcdisi
Content-Type: application/json; charset=utf-8
X-RCDisi-Ereignis: rechnung.bezahlt
X-RCDisi-Zustellung: 0b7e… (dieselbe wie "id" unten)
X-RCDisi-Signatur: t=1793700000,v1=5f2c…
{"id":"0b7e…","ereignis":"rechnung.bezahlt","zeit":"2026-11-03T10:15:00Z",
"daten":{"id":"…","nummer":"RE-2026-0042","betrag":214.2,"waehrung":"EUR","kunde":"Muster GmbH","kundennummer":"K-4711",
"bezahltAm":"2026-11-03T10:14:58.123+00:00","vonHand":false}}
Kam nichts an (kein 2xx, keine Antwort), versucht RCDisi es nach 1 Minute, 5 Minuten, 30 Minuten, 2 Stunden, 6 Stunden und 24 Stunden noch einmal.
Darum kann eine Meldung auch zweimal kommen — merken Sie sich die id und verarbeiten Sie jede nur einmal. Nach 20 Fehlschlägen in Folge
schaltet RCDisi den Webhook aus und sagt es Ihnen in der Glocke; einschalten in der Verwaltung.
Die Signatur prüfen
X-RCDisi-Signatur: t=<Sekunden seit 1970>,v1=<HMAC-SHA256> — der HMAC wird mit Ihrem Geheimnis über
t + "." + der unveränderte Inhalt gebildet (hexadezimal). Prüfen Sie den rohen Inhalt, bevor Sie ihn als JSON lesen, und weisen Sie
Zeitstempel ab, die älter als 5 Minuten sind.
Node.js
const crypto = require('crypto');
function echt(geheimnis, kopf, roh) { // roh: der Inhalt als Text, genau wie empfangen
const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(kopf || '');
if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
const soll = crypto.createHmac('sha256', geheimnis).update(m[1] + '.' + roh).digest('hex');
return crypto.timingSafeEqual(Buffer.from(soll, 'hex'), Buffer.from(m[2], 'hex'));
}PHP
function echt($geheimnis, $kopf, $roh) {
if (!preg_match('/^t=(\d+),v1=([0-9a-f]{64})$/', $kopf, $m)) return false;
if (abs(time() - (int)$m[1]) > 300) return false;
return hash_equals(hash_hmac('sha256', $m[1] . '.' . $roh, $geheimnis), $m[2]);
}
$roh = file_get_contents('php://input');
$ok = echt(getenv('RCDISI_GEHEIMNIS'), $_SERVER['HTTP_X_RCDISI_SIGNATUR'] ?? '', $roh);Python
import hashlib, hmac, re, time
def echt(geheimnis: str, kopf: str, roh: bytes) -> bool:
m = re.fullmatch(r't=(\d+),v1=([0-9a-f]{64})', kopf or '')
if not m or abs(time.time() - int(m.group(1))) > 300:
return False
soll = hmac.new(geheimnis.encode(), m.group(1).encode() + b'.' + roh, hashlib.sha256).hexdigest()
return hmac.compare_digest(soll, m.group(2))Welche Adressen gehen
Nur https:// auf Port 443, ohne Benutzername und Passwort in der Adresse, und nur Adressen im öffentlichen Netz — nichts, was ins eigene Netz
oder auf den Rechner selbst zeigt (das prüft RCDisi beim Anlegen und vor jeder Meldung).