Daten anlegen, ändern und löschen
Über die Schnittstelle kann ein freigegebenes Programm Einträge anlegen, ändern und löschen. Hier siehst du die nötigen Aufrufe und ihre Folgen.
Wer hat Zugriff?
- Administratoren verwalten Zugänge unter System → API-Schlüssel und System → MCP.
- Programme brauchen einen aktiven, gültigen Schlüssel. Seine Stufe und Bereichsfreigaben begrenzen den Zugriff. Ein zugeordneter Kontakt muss aktiv sein; seine Rechte können zusätzlich einschränken.
- Schreibzugriffe brauchen zusätzlich Schreiben erlauben. Jeder Bereich hat eigene Lese- und Schreibrechte. Die Schnittstelle bietet nicht jede Funktion der Oberfläche an.
- Die Schnittstelle selbst legt keine API-Schlüssel an. Details: Rollen und Rechte.
Welche E-Mails werden verschickt?
- Lesen sowie Schlüssel anlegen, ändern oder sperren: keine E-Mails.
- Spezielle Assistent-Aktionen für Termine, Kunden und Tiere: keine Kundenmail und kein Rechnungsversand. Termine entstehen ohne Benachrichtigung.
- Andere Schreibaktionen: Beachte die Beschreibung des jeweiligen Bereichs. Eine Newsletter-Abmeldung kann beispielsweise eine Meldung an den Betreiber auslösen.
Kurz gesagt: Aktiviere beim Schlüssel Schreiben erlauben. Prüfe zuerst Pflichtfelder und Rechte. Verwende für Versuche eigene Testeinträge.
Begriffe: API bedeutet Schnittstelle für andere Programme. Ein Endpunkt ist eine bestimmte Abrufadresse. JSON ist das Textformat der Antworten; eine Kennung (id) bezeichnet einen Eintrag.
1. So legst du einen Kontakt an
- Frage zuerst
/api/v1/contacts/_schemaab. - Prüfe darin die erlaubten Felder und Pflichtangaben.
- Sende die Kontaktdaten mit
POST, dem Befehl zum Anlegen. - Übernimm die neue Kennung aus
data.idin der Antwort.
Schicke JSON-Daten mit der Kopfzeile Content-Type: application/json. Eine Kopfzeile ist eine zusätzliche Angabe zur Web-Anfrage.
Was macht das? Dieser Aufruf legt einen Kontakt an. Die Kennung des neuen Kontakts steht in der Antwort.
POST /api/v1/contacts
Content-Type: application/json
{
"lastname": "Müller",
"firstname": "Erika",
"email": "erika.mueller@example.de",
"city": "Musterstadt",
"zip": "12345"
}
Die Antwort hat den Status 201 und enthält den fertigen Eintrag samt seiner neuen Kennung:
Was macht das? Dieser gekürzte Antwortausschnitt zeigt die neu vergebene Kontaktkennung.
{ "data": { "id": 4711, "lastname": "Müller" } }
Welche Felder es gibt und welche Pflicht sind, sagt dir vorher /api/v1/contacts/_schema.
2. So änderst du einen Kontakt
Mit PATCH änderst du nur die Felder, die du mitschickst. Alles andere bleibt, wie es war:
Was macht das? Dieser Aufruf ändert beim angegebenen Kontakt nur den Ort.
PATCH /api/v1/contacts/4711
{ "city": "Neustadt" }
PUT verhält sich wie PATCH, verlangt aber zusätzlich alle Pflichtfelder. Nicht mitgeschickte Felder bleiben auch hier stehen — die Schnittstelle leert nie stillschweigend etwas. PATCH ist fast immer richtig.
Manche Felder sind nur beim Anlegen schreibbar, danach nicht mehr — etwa der Kontakt einer Rechnung. In _schema tragen sie "immutable": true.
3. So löschst du einen Kontakt
Was macht das? Dieser Aufruf verschiebt den Kontakt in den Papierkorb, sofern keine Verweise das verhindern.
DELETE /api/v1/contacts/4711
Das verschiebt den Eintrag in den Papierkorb. Er ist damit aus dem Weg, aber nicht weg. Zurückholen:
Was macht das? Dieser Aufruf stellt einen Kontakt aus dem Papierkorb wieder her.
POST /api/v1/contacts/4711/restore
Endgültig löschen — nur für Administratoren und unwiderruflich:
Was macht das? Dieser Aufruf löscht den Kontakt endgültig, sofern Rechte und Löschregeln das erlauben.
DELETE /api/v1/contacts/4711?force=1
4. Doppelte Einträge verhindern
Stell dir vor: Dein Programm schickt „Kontakt anlegen“, und die Verbindung bricht ab. Ist der Kontakt jetzt da oder nicht? Dein Programm weiß es nicht. Schickt es die Anfrage sicherheitshalber noch einmal, hast du den Kontakt womöglich zweimal.
Dagegen gibt es eine einfache Vorkehrung. Schick beim Anlegen einen frei gewählten Wert mit:
Was macht das? Dieser Aufruf legt einen Kontakt mit einer Wiederholungskennung an. Derselbe Aufruf wird dadurch nicht doppelt ausgeführt.
POST /api/v1/contacts
Idempotency-Key: bestellung-4711
{ "lastname": "Müller" }
Kommt dieselbe Anfrage ein zweites Mal, liefert die Schnittstelle die ursprüngliche Antwort zurück, statt einen zweiten Eintrag anzulegen. Der Wert wird 24 Stunden lang gemerkt.
Als Wert eignet sich alles, was den Vorgang eindeutig kennzeichnet: eine Bestellnummer, eine fortlaufende Nummer aus deinem System, oder einfach eine zufällige Kennung, die dein Programm pro Vorgang einmal erzeugt.
Nimmst du denselben Wert für einen anderen Inhalt, bekommst du einen Fehler 409 — das ist Absicht und schützt vor Verwechslungen.
5. Fremde Änderungen nicht überschreiben
Zweites Szenario: Dein Programm liest einen Kontakt, rechnet ein bisschen, und schreibt eine Minute später zurück. In der Zwischenzeit hat eine Mitarbeiterin denselben Kontakt in der Oberfläche bearbeitet. Ohne Vorkehrung überschreibt dein Programm ihre Arbeit stillschweigend.
Beim Lesen bekommst du eine Stand-Kennung in der Kopfzeile ETag:
Was macht das? Dieser Aufruf liest den Kontakt. Die darunter gezeigte Antwort-Kopfzeile kennzeichnet seinen Stand.
GET /api/v1/contacts/4711
ETag: "3f8a91c2b4e07d15"
Schick sie beim Ändern als If-Match mit:
Was macht das? Dieser Aufruf ändert den Ort nur, wenn der zuvor gelesene Stand noch stimmt.
PATCH /api/v1/contacts/4711
If-Match: "3f8a91c2b4e07d15"
{ "city": "Neustadt" }
Hat inzwischen jemand anderes den Eintrag angefasst, kommt Status 412 zurück, und deine Änderung wird nicht ausgeführt. Dein Programm liest dann neu, entscheidet, und schreibt noch einmal.
Dieselbe Kennung spart beim Lesen Übertragung: Schickst du sie als If-None-Match und hat sich nichts geändert, antwortet die Schnittstelle mit 304 und ohne Daten.
6. Prüffehler verstehen
Passt eine Angabe nicht, bekommst du Status 422 und eine Liste der betroffenen Felder:
Was macht das? Diese Fehlerantwort zeigt mehrere ungültige Eingaben.
{
"error": {
"status": 422,
"code": "validation_failed",
"message": "Die übergebenen Daten sind ungültig.",
"details": [
{ "field": "email",
"message": "E-Mail-Adresse ist keine gültige E-Mail-Adresse." },
{ "field": "zip",
"message": "Postleitzahl ist zu lang (300 Zeichen, erlaubt sind 255)." },
{ "field": "lastname",
"message": "Nachname ist ein Pflichtfeld." }
]
}
}
Die Meldungen sind so geschrieben, dass du sie einem Benutzer unverändert anzeigen kannst.
Auch ein Tippfehler im Feldnamen wird gemeldet, statt stillschweigend ignoriert zu werden — sonst wunderst du dich, warum eine Angabe nicht ankommt:
Was macht das? Dieses Detail erklärt einen unbekannten Feldnamen.
{ "field": "nachname",
"message": "Unbekanntes Feld \"nachname\". Die schreibbaren Felder liefert GET /api/v1/contacts/_schema." }
7. Verweise werden geprüft
Trägst du beim Tier einen Halter ein, der nicht existiert, wird das abgelehnt:
Was macht das? Dieses Detail erklärt einen Verweis auf einen nicht vorhandenen Kontakt.
{ "field": "id_contact",
"message": "Kontakt verweist auf \"contacts\" mit der Kennung 99999 — dieser Datensatz existiert nicht (oder liegt im Papierkorb)." }
So entstehen über die Schnittstelle keine Einträge, die ins Leere zeigen.
8. Datumsangaben und Zahlen
Die Schnittstelle nimmt beide Schreibweisen an und gibt einheitlich zurück:
| Du darfst schicken | Du bekommst zurück |
2026-08-17 oder 17.08.2026 | 2026-08-17 |
2026-08-17 14:30 oder 17.08.2026 14:30 | 2026-08-17 14:30:00 |
2026-08-17T12:30:00Z (mit Zeitzone) | 2026-08-17 14:30:00 — umgerechnet in die Zeit deiner Instanz |
12.50 oder "12,50" | 12.5 |
true, 1, "ja" | true |
Uhrzeiten schreibst du als 14:30 oder 14:30:00.
9. Was die Schnittstelle nicht zulässt — und warum
Die Schnittstelle kennt die Regeln der Software. Eine festgeschriebene Rechnung bleibt festgeschrieben, eine stornierte Buchung storniert, eine gebuchte Zahlung gebucht — egal, welchen Schlüssel du benutzt. Solche Einträge sind gesperrt: Ändern und Löschen beantwortet die Schnittstelle mit Status 409 und dem Code record_locked. Die Meldung sagt dir jeweils, warum und was stattdessen geht.
| Das geht nicht | Stattdessen |
| Festgeschriebene Rechnung ändern oder löschen | POST /api/v1/invoices/{id}/cancel erzeugt eine Korrekturrechnung |
| Rechnungsnummer, Festschreibung oder Summen selbst setzen | POST /api/v1/invoices/{id}/close schreibt den Entwurf fest und vergibt alles |
| Position an eine festgeschriebene Rechnung hängen | Positionen kommen nur an Entwürfe |
| Zahlung ändern oder löschen | POST /api/v1/payments/{id}/cancel bucht sie gegen |
| Zahlung auf einen Rechnungsentwurf | erst festschreiben, dann buchen |
| Stornierte, ausgecheckte oder abgerechnete Buchung oder Termin ändern | Notizfelder bleiben änderbar; alles Weitere in der Oberfläche |
| Kennwort eines Kontakts setzen | gar nicht — Kennwörter vergibt nur die Software |
| E-Mails anlegen, Kassenbuch oder Kundenkonto bebuchen | nur lesen; diese Vorgänge laufen über die Oberfläche |
Shop-Verkäufe (shopsales), Download-Zugänge (shopdownloads) oder Gutschein-Einlösungen anlegen oder ändern | nur lesen — sie entstehen an der Kasse und bei der Auslieferung; Zustellung, Freigabe, Widerrufsverzicht und Zugangscodes setzt die Software |
Inhalte eines Digitalprodukts (digitalproductitems) oder Gutscheincodes (shopcoupons) mit einem Backoffice-Schlüssel anlegen | dafür braucht es die Stufe Administrator; den Einlösezähler used führt die Kasse |
| Bestand einer Ware pflegen | geht: services.stock ist schreibbar — für den Lagerabgleich aus einem anderen System |
Welche Bedingung einen Bereich sperrt, steht in GET /api/v1/<bereich>/_schema unter meta.locked_when; die Felder, die trotz Sperre änderbar bleiben, unter meta.writable_when_locked. Viele Zustandsfelder wie finalized werden von der Software gesetzt. Prüfe im jeweiligen Bereichsschema, welche Felder nur lesbar sind.
10. So stellst du eine Rechnung über die Schnittstelle
- Lies den Kunden, den passenden Steuersatz und die Zahlungsart aus deiner Instanz.
- Lege den Entwurf an und übernimm seine zurückgegebene Kennung für die Position.
- Prüfe die Positionen und schreibe die Rechnung fest.
- Buche erst nach dem tatsächlichen Geldeingang die Zahlung.
Die Kennungen unten sind Beispiele: Steuersatz 3 muss tatsächlich 19 Prozent haben. Zahlungsart 1 muss zur Zahlung passen. Der Abschluss prüft auch die Steuerkategorie und nötige Kundendaten.
Der Ablauf ist derselbe wie in der Oberfläche: Entwurf anlegen, Positionen dazu, festschreiben, Zahlung buchen.
Was macht das? Diese Aufruffolge legt einen Rechnungsentwurf mit einer Position an, schreibt ihn fest und bucht eine Zahlung. Ersetze alle Kennungen durch passende Werte deiner Instanz.
POST /api/v1/invoices
{ "id_contact": 4711, "invoicedate": "2026-09-09", "target": "2026-09-23",
"subject": "Rechnung September" }
→ 201, Entwurf mit invoicenumber "ENTWURF"
POST /api/v1/lineitems
{ "id_contact": 4711, "id_invoice": 9338, "servicename": "Einzelstunde",
"servicedate": "2026-09-09", "quantity": 1,
"net_price_item": 50, "end_price_item": 59.50,
"net_price_overall": 50, "end_price_overall": 59.50, "id_taxrate": 3, "taxrate": 19 }
POST /api/v1/invoices/9338/close
→ 200, Rechnungsnummer vergeben, PDF erzeugt, Rechnung gesperrt
POST /api/v1/payments
{ "id_invoice": 9338, "id_paymenttype": 1, "amount": 59.50,
"paymentdate": "2026-09-10" }
→ 201, Rechnung steht auf „bezahlt“
Die Schnittstelle rechnet dabei den Steueranteil der Zahlung aus, weist Überzahlungen ab und setzt den Rechnungszustand (offen, teilweise bezahlt, bezahlt) — genau wie beim Buchen in der Oberfläche. Eine E-Mail an den Kunden verschickt sie nicht; der Versand bleibt der Oberfläche vorbehalten.
Einen Entwurf, den du nicht mehr brauchst, löschst du mit DELETE /api/v1/invoices/{id}. Seine Positionen bleiben als offene Positionen erhalten. Eine festgeschriebene Rechnung machst du mit POST /api/v1/invoices/{id}/cancel rückgängig: Es entsteht eine Korrekturrechnung mit der Nummer …-S, alle Positionen werden gutgeschrieben und die zugehörigen Zahlungen storniert. Bei einer nicht vollständig verbrauchten Stempelkarte kann zuvor 409 confirmation_required kommen. Prüfe die Meldung; bestätige die Folge gegebenenfalls mit "confirm_multipass": true.
Wenn etwas nicht klappt
- 403 forbidden: Prüfe Schreiben erlauben sowie Stufe und Bereichsfreigaben des Schlüssels.
- 422 validation_failed: Lies details in der Antwort. Korrigiere die dort genannten Felder.
- 409 conflict oder record_locked: Verweise oder Fachregeln verhindern den Schritt. Lies die Meldung; entsperre keine festgeschriebene Rechnung.
- 412 precondition_failed: Der Eintrag wurde inzwischen geändert. Lies ihn erneut und prüfe deine Änderung.