The silent errors of the weclapp API: 6 traps without an error message
A 400 is a gift. It tells you that something is wrong, and usually where. More dangerous are requests that respond with 200 or 201 and still don't do what you meant: the filter doesn't take effect, a field becomes empty, the action runs for real although you wanted a test run.
The knowledge in this article comes from the official weclapp API docs, the talk by Christian Lanig (weclapp) at Community Day 2026 and our own integrations such as Fulfillment 4 weclapp and 4Fulfillment. We don't see this as criticism of the platform, but as knowledge that saves you trouble: every one of these quirks is in the docs, just in places where it's easy to overlook.
There are six cases. Each has the same form: symptom, why it happens, how you spot and avoid it, plus a request. At the very end there's a checklist you can adopt in your reviews.
For all examples: base URL https://<your-tenant>.weclapp.com/webapp/api/v2/, authentication via the header AuthenticationToken. Always try write access against a test system, never against your production tenant.
First the good news: what weclapp does report
Not everything is silent. Before we get to the exceptions, a comparison helps. It shows where you can rely on an error message and where you can't:
| You send | What happens |
|---|---|
Unknown property in properties
|
Error |
Unknown property in sort
|
Error |
| Unknown property in the body of POST or PUT | 400 (platform.unknown_property) |
| Unknown or non-filterable filter | silently ignored |
PUT without ignoreMissingProperties=true
|
Anything not sent becomes null
|
List item without id in the PUT |
Item is removed |
This is the map for the rest of the article. A typo in the field name of a projection is thus a loud error, a typo in the filter a silent one.

1. An unknown filter suffix returns everything
Symptom. orderDate-gte=… returns HTTP 200 with the unfiltered total. In a loop over all pages, the entire dataset then runs through, although you only wanted a time period.
Why. weclapp knows a fixed list of operators: eq, ne, lt, gt, le, ge, null, notnull, like, notlike, ilike, notilike, in, notin. A gte or lte is not among them. The docs say clearly what happens then: filters on unknown or non-filterable properties are silently ignored (docs line 470). You get no error, you get everything. On top of that comes a side clause that's easy to overlook: not every operator works for every property type.
How to spot and avoid it. Check every new filter with a boundary count: shift the boundary and compare the numbers. If the count doesn't change, the filter isn't taking effect. Comparing with the unfiltered total is also worthwhile. In code review, you specifically grep for -gte and -lte.
Which properties are filterable is stated in machine-readable form in the spec: x-weclapp: filterable: false marks properties where a filter would remain ineffective. Live, you query the filterable properties of a resource via GET /meta/queryFilterProperties?resource=salesOrder.
GET https://<dein-mandant>.weclapp.com/webapp/api/v2/salesOrder/count?orderDate-gte=1767222000000
AuthenticationToken: <dein-token>
That's the error: gte is unknown, the filter is ignored and the count equals the total number of all sales orders. With the right suffix and a shifted boundary you see the difference:
GET https://<dein-mandant>.weclapp.com/webapp/api/v2/salesOrder/count?orderDate-ge=1767222000000
AuthenticationToken: <dein-token>
The timestamp is Berlin midnight of 01.01.2026 in epoch milliseconds, because that's how weclapp serializes date and time fields. Part 9 of the series (data type traps) explains more about this.
It's also about load: Christian Lanig (weclapp) named full queries at intervals as one of the most common anti-patterns at Community Day 2026. An ignored filter creates them unintentionally. More on the relationship between request duration and load is in part 1 of the series (load management).
2. A filter on the wrong field returns the wrong result
Symptom. Your request is formally correct, the response is 200, but the hits don't match. A typical example: customerId-eq=10004 on the sales order doesn't search for customer number 10004. customerId is a reference to the party's internal ID. So you get no hit, or the sales orders of a completely different party.
Why. The filter is valid, it just refers to a different field than you think. From the API's point of view there is nothing to object to, so no hint comes either.
A related stumbling block is the two filter syntaxes. The suffix parameter (ean-notnull=true) and the filter expression (filter=…) are two different notations. In the expression you write (not (ean null)), not (ean-notnull). If you mix the two up and translate error responses in your own code into empty lists, you turn a loud error into a silent one.
How to spot and avoid it. On “0 hits” you suspect the filter first, not the data. Verify the field path in the schema, never compare numbers with ID fields, and do the boundary count from point 1. For customers, you proceed in two steps: first resolve the party via the customer number, then filter with its ID.
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>
Part 2 of the series (reading selectively) covers selection and projection in more detail.
3. PUT without ignoreMissingProperties nulls what you don't send along
Symptom. You want to change a last name and send a body that contains version, lastName and the entity's required fields, but not the rest. The response is 200. The last name is correct, but everything else that wasn't in the body is null: phone number, addresses, payment terms.
Why. In weclapp, a PUT is a full replacement. The docs put it like this: all properties not specified are interpreted as null (docs line 1069). For a full update, writable boolean fields are mandatory (docs line 1065). If they're missing, weclapp rejects the request. But if you hit all the required fields and omit the rest, the partial payload slips through, and the rest gets nulled.
How to spot and avoid it. For partial updates, always append ignoreMissingProperties=true (docs line 1168) and send the version along. The parameter appears in the docs prose, but isn't declared as a parameter on any operation in the spec. Clients generated from the OpenAPI file therefore don't know it by themselves. The alternative is the classic way: fetch the entity via GET, change it, send it back in full. The docs also recommend this (line 1071), so that fields that have been newly added and that your client doesn't know aren't nulled by accident.
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. Lists without id in the PUT are deleted, even with the flag
Symptom. You change one item and send only that one in orderItems. The response is 200, and the remaining items are gone.
Why. ignoreMissingProperties=true affects top-level properties. Lists remain target state: if you omit the id of an existing entry, it is removed or emptied (docs line 1171). This affects every list of an entity, for example orderItems, shipmentItems, incomingGoodsItems, parcels or articlePrices.
How to spot and avoid it. Treat the item PUT as a merge, not as a subset. Fetch the entity, send every existing item back as a stub with id and version, change only what you want to change, and omit the id for new items. Before sending, check that no existing item ID is missing from the payload. The details, including code, are in part 4 of the series (writing safely).
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" }
]
}
Item 2 is unchanged, but included as a stub. If it's missing, it counts as deleted.
5. dryRun exists only for generic writes
Symptom. You want to test a credit note and append dryRun=true to createCreditNote. You expect a test run, but the test mode isn't provided for this endpoint at all.
POST https://<dein-mandant>.weclapp.com/webapp/api/v2/salesInvoice/id/<rechnungs-id>/createCreditNote?dryRun=true
AuthenticationToken: <dein-token>
Why. The docs describe dryRun explicitly for generic PUT, POST and DELETE (docs line 927). Named actions like createCreditNote or createSalesInvoice are not included: in the spec, none of the more than 200 action endpoints declares the parameter.
How to spot and avoid it. Before every use, check whether you have a generic write (POST /salesOrder, PUT /…/id/…) or an action (/id/{id}/create…) in front of you. With actions, assume they are executed for real, and test them only in the test system. How to use dryRun where it works is shown in part 4 of the series (writing safely).
6. “Accepted” doesn't mean “stored the way you think”
Symptom. The request responds 201 or 200, and yet a field contains something different from what you expected.
Why. Cases 3 and 4 are the best-known examples, but not the only ones. On creation, many required fields have sensible default values: if you omit them or send null, weclapp inserts the default value. That's practical as long as you know it, and a surprise if your code meant null as “don't set”. The HTTP status only tells you that the request was valid, not which values are now in the record.
How to spot and avoid it. After every new write path, read back the fields you set and compare them with what you sent. Project only the fields you wrote, and treat decimal values as strings in the comparison, otherwise your code reports differences that aren't any.
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(", ")}`);
}
The defensive checklist
If you take one thing away from this article, make it this: in weclapp, never treat an HTTP status as proof, but the target object. Specifically:
-
Boundary count for every new filter. Shift the boundary, compare
/count. Plus the comparison with the unfiltered total. - Count plausibility. Define an expected range per job and abort before fetching if the count exceeds it.
- Don't report empty results as success. “Nothing found” in a sync run is a warning signal, not a result.
-
Check projections against the current spec.
propertiesreports typos loudly. A conformance test in CI catches outdated field names before they go live. -
Always PUT with
ignoreMissingProperties=true. Send item arrays as a merge and check beforehand that no existing ID is missing. - Read-back after every new write path. Read back the fields you set and compare them, not the status.
-
dryRunonly for generic writes. Never for actions, only the test system helps there. - Whitelist status transitions in your own logic. Define which status changes your code may trigger, and check that before every write.
- Test against a sandbox, never against production. And compare the spec regularly: the docs reserve the right to make changes (change policy, line 179), and weclapp announces planned changes in the API newsletter (line 288).

Next steps
With the free Postman Collection 4 weclapp you start against your test system and rebuild the boundary counts and read-backs from this article. Our YouTube webinar “weclapp API Deep Dive” also gives you an introduction to the API. If you want to have an integration reviewed or harden it together, book an intro call at wals.pro/termin.