What this is about
With PUT {basis}/update/{id} you replace an object. What you send in data is the complete target state: this is how the object should look afterwards, field by field.
PUT /api/rest/crm/customer/update/5a2b…
{
"data": {
"id": "5a2b…",
"name": "Muster GmbH",
"email": "neu@muster.de",
"phone": "+49 611 123456"
},
"response": ["+"]
}{
"data": {
"id": "5a2b…",
"_createdOn": "2026-03-02 09:14:00",
"_updatedOn": "2026-09-21 10:20:31",
"name": "Muster GmbH",
"email": "neu@muster.de",
"phone": "+49 611 123456",
"note": null
},
"meta": { "error": false }
}note was set in the object before, but it is missing in the PUT. That is why it is empty now. The response is 200. As with reading, response decides which fields come back, and it is required.
Before, payload, after
A company with a name, a note, a main address (reference) and two employees (list). The employees are dependent children: they belong to the company and are deleted with it.
| Field | before | in the PUT | after |
|---|---|---|---|
companyname | “CodamIC” | "Codamic AG" | “Codamic AG” |
note | “Stammkunde” | missing | null |
logo | “logo.png” | null | null |
mainAddress | Address A | missing | detached, address A still exists |
employees | Anna, Ben | [{ "id": Anna, …all fields… }] | only Anna; Ben is deleted |
The table shows the three rules for the three field kinds:
| Simple field | Single reference | List | |
|---|---|---|---|
| Field is missing or null | is cleared | is detached; a dependent child is deleted | all members are removed |
| Field has a value | is set | points to the named object afterwards | becomes exactly this list (target state) |
Whether a reference is only detached or the child is deleted is defined by the relation in the model. See Relation types and recursive flags.
The stations of a PUT
-
CIASFilter chainIs the token valid?↳ no 401
-
CDMSField selectionIs there a
responsein the body?↳ no 400response -
CDMSVisibilityDoes the object with
data.idexist, and may the person see it? Same filters as for reading.↳ no 404not-found -
CDMSModel roleDoes the person have the right to change
customer? The same applies to every child that is changed along with it.↳ no 403missing-permission|<rolle> -
CDMSTransfer fieldsEvery field of the model is set to the value from
data, missing ones to empty. Rule violations are collected. -
HookBefore hooksHooks see the changed object and may still adjust it.
-
CDMSValidationAre all rules met after the hooks?↳ no 422
validation-failedwith all violations -
DatabaseSave and read backWrite the change, run after hooks, then read the object again with the
response - 200 with the object as it looks now
Two things stand out:
- Visibility comes before the role. An object you may not see returns 404, even if you also lack the role to change it. See Why invisible objects return 404.
- Reading back is part of the request. It runs with your read permissions, in the same transaction. If it fails, the change is not saved either.
_createdOn stays as it was, _updatedOn is set to now. CDMS skips all fields starting with _ and the id when writing. See System fields that the server sets.
Where the ID comes from
With PUT, the id appears twice in the request: in the path and in data. data.id is what counts. CDMS does not evaluate the path.
| data.id | id in the path | What happens |
|---|---|---|
| present | same | the object with this id is replaced |
| present | different | the object from data.id is replaced, without an error |
| missing | any | 400 missing-id |
Child objects in a PUT
You can send references and list entries as objects. What CDMS does with them depends on two questions: Does the child have an id? Does the relation allow creating or changing children?
When: The relation does not allow changing the children. Typical for pointers like mainAddress.
-
1Client→CDMSsends
"mainAddress": { "id": "a7…" } -
2CDMS→Databaselooks up the object
a7… -
3CDMS→Clientdoes not exist → 404
missing-object|a7…|mainAddress -
4CDMSexists → the reference now points to it; other fields of the child stay untouched
Result: The company points to address a7…. The address itself does not change.
When: The relation allows changing the children. Typical for dependent children like employees.
-
1Client→CDMSsends
"employees": [{ "id": "k1…", "firstname": "Anna", "lastname": "Schmidt" }] -
2CDMSreplaces the child
k1…with PUT rules as well: missing fields of the child are cleared -
3CDMSremoves all other employees, dependent ones are deleted
Result: Only Anna stays, with exactly these fields. If you send only { "id": "k1…" }, her fields are cleared. For required fields you get 422 employees[0].firstname cannot-be-null.
When: You want to create a new child.
-
1Client→CDMSsends
"employees": [{ "firstname": "Cem", "lastname": "Yıldız" }] -
2CDMSrelation allows creating? → creates the employee, with default values and CREATE rules
-
3CDMS→Clientnot allowed → 400
recursive-create-not-allowed|employees
Result: Cem is created and assigned to the company. All previous employees are removed because they are missing from the list.
The complete rules are in The four cases in nested writing and Lists as target state.
Decision table
| Field kind | in the PUT | Relation: dependent child | After |
|---|---|---|---|
| simple field | missing / null | – | empty |
| simple field | value | – | new value |
| single reference | missing / null | no | detached, the object still exists |
| single reference | missing / null | yes | the child is deleted |
| list | missing / null / [] | no | all links detached |
| list | missing / null / [] | yes | all children deleted |
| list | partial list | – | exactly these members; members not named are detached or deleted |
Pitfalls
Where to go next
- Change only some fields: Changing with PATCH
- Which verb when: PUT or PATCH? The null trap
- What gets checked: Validation
- Two clients change at the same time: Concurrent changes