Die stillen Fehler der weclapp API: 6 Fallen ohne Fehlermeldung
Ein 400 ist ein Geschenk. Er sagt dir, dass etwas nicht stimmt, und meistens auch, wo. Gefährlicher sind Requests, die mit 200 oder 201 antworten und trotzdem nicht das tun, was du gemeint hast: Der Filter greift nicht, ein Feld wird leer, die Aktion läuft echt, obwohl du einen Testlauf wolltest.
Das Wissen in diesem Artikel kommt aus der offiziellen weclapp-API-Doku, dem Vortrag von Christian Lanig (weclapp) am Community Day 2026 und unseren eigenen Integrationen wie Fulfillment 4 weclapp und 4Fulfillment. Wir betrachten das nicht als Kritik an der Plattform, sondern als Wissen, das dir Ärger erspart: Jede dieser Eigenheiten steht in der Doku, nur an Stellen, an denen man sie leicht überliest.
Es sind sechs Fälle. Jeder hat dieselbe Form: Symptom, warum es passiert, wie du es erkennst und vermeidest, dazu ein Request. Ganz am Ende steht eine Checkliste, die du in deine Reviews übernehmen kannst.
In allen Beispielen gilt: Basis-URL https://<dein-mandant>.weclapp.com/webapp/api/v2/, Authentifizierung über den Header AuthenticationToken. Probiere Schreibzugriffe immer gegen ein Testsystem aus, nie gegen deinen Produktivmandanten.
Erst die gute Nachricht: Was weclapp laut meldet
Nicht alles ist still. Bevor es um die Ausnahmen geht, hilft eine Gegenüberstellung. Sie zeigt, wo du dich auf eine Fehlermeldung verlassen kannst und wo nicht:
| Du schickst | Was passiert |
|---|---|
Unbekannte Property in properties
|
Fehler |
Unbekannte Property in sort
|
Fehler |
| Unbekannte Property im Body von POST oder PUT | 400 (platform.unknown_property) |
| Unbekannter oder nicht filterbarer Filter | still ignoriert |
PUT ohne ignoreMissingProperties=true
|
Nicht Gesendetes wird null
|
Listen-Item ohne id im PUT |
Item wird entfernt |
Das ist die Landkarte für den Rest des Artikels. Ein Tippfehler im Feldnamen einer Projektion ist demnach ein lauter Fehler, ein Tippfehler im Filter ein stiller.

1. Ein unbekannter Filter-Suffix liefert alles
Symptom. orderDate-gte=… liefert HTTP 200 mit der ungefilterten Gesamtmenge. In einer Schleife über alle Seiten läuft dann der komplette Bestand durch, obwohl du nur einen Zeitraum wolltest.
Warum. weclapp kennt eine feste Liste von Operatoren: eq, ne, lt, gt, le, ge, null, notnull, like, notlike, ilike, notilike, in, notin. Ein gte oder lte gehört nicht dazu. Die Doku sagt klar, was dann geschieht: Filter auf unbekannte oder nicht filterbare Properties werden still ignoriert (Doku Z. 470). Du bekommst keinen Fehler, sondern alles. Dazu kommt ein Nebensatz, den man leicht überliest: Nicht jeder Operator funktioniert für jeden Property-Typ.
So erkennst und vermeidest du es. Prüfe jeden neuen Filter mit einem Boundary-Count: Verschiebe die Grenze und vergleiche die Zahlen. Ändert sich der Count nicht, greift der Filter nicht. Zusätzlich lohnt der Vergleich mit der ungefilterten Menge. Im Code-Review greifst du gezielt nach -gte und -lte.
Welche Properties filterbar sind, steht maschinenlesbar in der Spec: x-weclapp: filterable: false markiert Properties, bei denen ein Filter wirkungslos bliebe. Live fragst du die filterbaren Properties einer Ressource über GET /meta/queryFilterProperties?resource=salesOrder ab.
GET https://<dein-mandant>.weclapp.com/webapp/api/v2/salesOrder/count?orderDate-gte=1767222000000
AuthenticationToken: <dein-token>
Das ist der Fehler: gte ist unbekannt, der Filter wird ignoriert und der Count entspricht der Gesamtzahl aller Aufträge. Mit dem richtigen Suffix und einer verschobenen Grenze siehst du den Unterschied:
GET https://<dein-mandant>.weclapp.com/webapp/api/v2/salesOrder/count?orderDate-ge=1767222000000
AuthenticationToken: <dein-token>
Der Zeitstempel ist Berlin-Mitternacht des 01.01.2026 in Epoch-Millisekunden, denn so serialisiert weclapp Datums- und Zeitfelder. Mehr dazu erklärt Teil 9 der Serie (Datentyp-Fallen).
Es geht dabei auch um Last: Christian Lanig (weclapp) nannte am Community Day 2026 Vollabfragen im Intervall als eines der häufigsten Anti-Patterns. Ein ignorierter Filter erzeugt sie ungewollt. Mehr zum Zusammenhang von Request-Dauer und Last steht in Teil 1 der Serie (Lastmanagement).
2. Ein Filter auf das falsche Feld liefert das falsche Ergebnis
Symptom. Dein Request ist formal richtig, die Antwort ist 200, aber die Treffer passen nicht. Ein typisches Beispiel: customerId-eq=10004 am Auftrag sucht nicht nach der Kundennummer 10004. customerId ist ein Verweis auf die interne ID der Partei. Du bekommst also keinen Treffer oder die Aufträge einer ganz anderen Partei.
Warum. Der Filter ist gültig, er bezieht sich nur auf ein anderes Feld, als du denkst. Für die API gibt es daran nichts zu beanstanden, deshalb kommt auch kein Hinweis.
Ein verwandter Stolperstein sind die zwei Filter-Syntaxen. Der Suffix-Parameter (ean-notnull=true) und der Filter-Ausdruck (filter=…) sind zwei verschiedene Schreibweisen. Im Ausdruck schreibst du (not (ean null)), nicht (ean-notnull). Wer die beiden verwechselt und Fehlerantworten im eigenen Code in leere Listen übersetzt, macht aus einem lauten Fehler einen stillen.
So erkennst und vermeidest du es. Bei „0 Treffer“ verdächtigst du zuerst den Filter, nicht die Daten. Belege den Feldpfad im Schema, vergleiche nie Nummern mit ID-Feldern und mache den Boundary-Count aus Punkt 1. Für Kunden gehst du zweistufig vor: erst die Partei über die Kundennummer auflösen, dann mit deren ID filtern.
GET https://<dein-mandant>.weclapp.com/webapp/api/v2/party?customerNumber-eq=10004&properties=id,customerNumber
AuthenticationToken: <dein-token>
GET https://<dein-mandant>.weclapp.com/webapp/api/v2/salesOrder?customerId-eq=<party-id>&properties=id,orderNumber
AuthenticationToken: <dein-token>
Mit Auswahl und Projektion geht Teil 2 der Serie (Gezielt lesen) ausführlicher um.
3. PUT ohne ignoreMissingProperties nullt, was du nicht mitschickst
Symptom. Du willst einen Nachnamen ändern und schickst einen Body, der version, lastName und die Pflichtfelder der Entität enthält, aber nicht den Rest. Die Antwort ist 200. Der Nachname stimmt, aber alles andere, was nicht im Body stand, ist null: Telefonnummer, Adressen, Zahlungsbedingungen.
Warum. Ein PUT ist in weclapp ein Vollersatz. Die Doku formuliert es so: Alle nicht angegebenen Properties werden als null interpretiert (Doku Z. 1069). Bei einem vollständigen Update sind schreibbare Boolean-Felder Pflicht (Doku Z. 1065). Fehlen sie, lehnt weclapp den Request ab. Triffst du aber alle Pflichtfelder und lässt den Rest weg, rutscht der Teil-Payload durch, und der Rest wird genullt.
So erkennst und vermeidest du es. Hänge für Teil-Updates immer ignoreMissingProperties=true an (Doku Z. 1168) und sende die version mit. Der Parameter steht in der Doku-Prosa, ist aber an keiner Operation der Spec als Parameter deklariert. Clients, die aus der OpenAPI-Datei generiert werden, kennen ihn deshalb nicht von selbst. Die Alternative ist der klassische Weg: Entität per GET holen, ändern, ganz zurückschicken. Das empfiehlt auch die Doku (Z. 1071), damit Felder, die neu dazugekommen sind und die dein Client nicht kennt, nicht versehentlich genullt werden.
PUT https://<dein-mandant>.weclapp.com/webapp/api/v2/party/id/<party-id>?ignoreMissingProperties=true
AuthenticationToken: <dein-token>
Content-Type: application/json
{ "version": "0", "lastName": "Mustermann-Neu" }

4. Listen ohne id im PUT werden gelöscht, auch mit dem Flag
Symptom. Du änderst eine Position und schickst nur diese eine in orderItems. Die Antwort ist 200, und die übrigen Positionen sind weg.
Warum. ignoreMissingProperties=true betrifft Properties auf oberster Ebene. Listen bleiben Zielzustand: Lässt du die id eines bestehenden Eintrags weg, wird er entfernt oder geleert (Doku Z. 1171). Das betrifft jede Liste einer Entität, zum Beispiel orderItems, shipmentItems, incomingGoodsItems, parcels oder articlePrices.
So erkennst und vermeidest du es. Behandle den Item-PUT als Merge, nicht als Teilmenge. Hole die Entität, sende jede bestehende Position als Stub mit id und version zurück, ändere nur, was du ändern willst, und lasse bei neuen Positionen die id weg. Vor dem Senden prüfst du, dass keine bestehende Item-ID im Payload fehlt. Die Details, inklusive Code, stehen in Teil 4 der Serie (Sicher schreiben).
PUT https://<dein-mandant>.weclapp.com/webapp/api/v2/salesOrder/id/<auftrags-id>?ignoreMissingProperties=true
AuthenticationToken: <dein-token>
Content-Type: application/json
{
"version": "5",
"orderItems": [
{ "id": "<position-1>", "version": "3", "quantity": "2" },
{ "id": "<position-2>", "version": "1" }
]
}
Position 2 ist unverändert, aber als Stub dabei. Fehlt sie, gilt sie als gelöscht.
5. dryRun gibt es nur für generische Writes
Symptom. Du willst eine Gutschrift testen und hängst an createCreditNote ein dryRun=true an. Du erwartest einen Testlauf, aber für diesen Endpunkt ist der Testmodus gar nicht vorgesehen.
POST https://<dein-mandant>.weclapp.com/webapp/api/v2/salesInvoice/id/<rechnungs-id>/createCreditNote?dryRun=true
AuthenticationToken: <dein-token>
Warum. Die Doku beschreibt dryRun ausdrücklich für generische PUT, POST und DELETE (Doku Z. 927). Benannte Aktionen wie createCreditNote oder createSalesInvoice gehören nicht dazu: In der Spec deklariert keiner der über 200 Aktions-Endpunkte den Parameter.
So erkennst und vermeidest du es. Prüfe vor jeder Nutzung, ob du einen generischen Write (POST /salesOrder, PUT /…/id/…) oder eine Aktion (/id/{id}/create…) vor dir hast. Bei Aktionen rechnest du damit, dass sie echt ausgeführt werden, und testest sie nur im Testsystem. Wie du dryRun dort einsetzt, wo es wirkt, zeigt Teil 4 der Serie (Sicher schreiben).
6. „Akzeptiert“ heißt nicht „so gespeichert, wie du denkst“
Symptom. Der Request antwortet 201 oder 200, und trotzdem steht in einem Feld etwas anderes, als du erwartet hast.
Warum. Die Fälle 3 und 4 sind die bekanntesten Beispiele, aber nicht die einzigen. Beim Anlegen haben viele Pflichtfelder sinnvolle Standardwerte: Lässt du sie weg oder schickst null, setzt weclapp den Standardwert ein. Das ist praktisch, solange du es weißt, und eine Überraschung, wenn dein Code null als „nicht setzen“ gemeint hat. Der HTTP-Status sagt dir nur, dass der Request gültig war, nicht, welche Werte jetzt im Datensatz stehen.
So erkennst und vermeidest du es. Lies nach jedem neuen Schreibpfad die gesetzten Felder zurück und vergleiche sie mit dem, was du gesendet hast. Projiziere dabei nur die Felder, die du geschrieben hast, und behandle Dezimalwerte beim Vergleich als String, sonst meldet dein Code Differenzen, die keine sind.
GET https://<dein-mandant>.weclapp.com/webapp/api/v2/party/id/<party-id>?properties=id,lastName,customerBlocked
AuthenticationToken: <dein-token>
const gesendet = { lastName: "Mustermann-Neu", customerBlocked: false };
const gespeichert = await getParty(id, ["id", ...Object.keys(gesendet)]);
const abweichend = Object.keys(gesendet)
.filter((k) => String(gespeichert[k]) !== String(gesendet[k]));
if (abweichend.length) {
throw new Error(`Nicht wie gesendet gespeichert: ${abweichend.join(", ")}`);
}
Die Defensive-Checkliste
Wenn du aus dem Artikel eine Sache mitnimmst, dann diese: Behandle in weclapp nie einen HTTP-Status als Beweis, sondern das Zielobjekt. Konkret:
-
Boundary-Count für jeden neuen Filter. Grenze verschieben,
/countvergleichen. Dazu der Vergleich mit der ungefilterten Menge. - Count-Plausibilität. Lege pro Job einen Erwartungsbereich fest und brich vor dem Abruf ab, wenn der Count ihn sprengt.
- Leere Ergebnisse nicht als Erfolg melden. „Nichts gefunden“ bei einem Sync-Lauf ist ein Warnsignal, kein Ergebnis.
-
Projektionen gegen die aktuelle Spec prüfen.
propertiesmeldet Tippfehler laut. Ein Konformitätstest in der CI fängt veraltete Feldnamen, bevor sie produktiv laufen. -
PUT immer mit
ignoreMissingProperties=true. Item-Arrays als Merge senden und vorher prüfen, dass keine bestehende ID fehlt. - Read-back nach jedem neuen Schreibpfad. Gesetzte Felder zurücklesen und vergleichen, nicht den Status.
-
dryRunnur bei generischen Writes. Bei Aktionen nie, dort hilft nur das Testsystem. - Statusübergänge in der eigenen Logik whitelisten. Lege fest, welche Statuswechsel dein Code auslösen darf, und prüfe das vor jedem Schreiben.
- Tests gegen eine Sandbox, nie gegen Produktion. Und die Spec regelmäßig abgleichen: Die Doku behält sich Änderungen vor (Change Policy, Z. 179), geplante Änderungen kündigt weclapp im API-Newsletter an (Z. 288).

Weiter
Mit der kostenlosen Postman Collection 4 weclapp startest du gegen dein Testsystem und baust die Boundary-Counts und Read-backs aus diesem Artikel nach. Einen Einstieg in die API gibt außerdem unser YouTube-Webinar „weclapp API Deep Dive“. Wenn du eine Integration prüfen lassen oder sie gemeinsam härten willst, buche ein Vorgespräch unter wals.pro/termin.