What this is about
You know the id of an object and want to read it. Every model has two endpoints for this:
| Endpoint | Body | what comes back |
|---|---|---|
POST {base}/read/{id} | JSON with response | exactly the fields you list in response |
GET {base}/read/{id} | none | always everything that ["*"] returns |
{base} is the path of the model, for example /api/rest/hr/employee.
The two ways compared
POST /api/rest/hr/employee/read/7f3…
{ "response": ["firstname", "lastname"] }{
"data": {
"id": "7f3…",
"_createdOn": "2026-03-02 09:14:00",
"_updatedOn": "2026-09-01 16:40:12",
"firstname": "Daniel",
"lastname": "Mertins",
"company": null,
"department": null
},
"meta": { "error": false }
}id, _createdOn and _updatedOn always come along, even if they are not in response. Everything else you did not request is null in the response. More on this under System fields.
GET /api/rest/hr/employee/read/7f3…{
"data": {
"id": "7f3…",
"firstname": "Daniel",
"lastname": "Mertins",
"company": { "id": "a1…", "companyname": null },
"department": { "id": "d4…", "name": null }
},
"meta": { "error": false }
}- you choose the fields in
response - you can expand references on purpose
- you only need the roles of the models you actually request
- no body, CDMS always uses
["*"] - references come with their
idonly - needs the read role of every referenced model
The stations of a read request
A read request passes several checks. Data comes back only when all of them pass:
-
CIASFilter chainIs the token valid?↳ no 401
-
CDMSField selectionIs there a
responsein the body?↳ no 400 withmessageKeyresponse -
CDMSModel roleDoes the user have the read role of
employee?↳ no 403missing-permission|employee-read -
CDMSRow filtersDoes the object exist, and may the user see it (tenant, own data, attribute filters)?↳ no 404
not-found -
CDMSReferencesDoes the user have the read role of every model the
responsereaches into?↳ no 403missing-permission|<role>, the whole request fails - 200 with the requested fields
Two things matter here:
- Invisible is the same as not present. An object that does not exist and an object you may not see both return 404. This way CDMS does not reveal that other people’s data exists. See Why invisible means 404.
- The
responsedecides which roles you need. If you read only simple fields, the read role of the model is enough. As soon as theresponsereaches into a reference, you also need the read role of the referenced model.
All variants
When: The normal case. You know which fields you need.
-
1Client→CDMSsends
POST /hr/employee/read/7f3…with{ "response": ["firstname", "lastname"] } -
2CDMSresolves the
responseinto a list of fields -
3CDMSchecks the read role of
employee -
4CDMS→Databaselooks for the row with this
id, restricted by the row filters, and reads only the requested columns -
5Hookthe project's read hooks see the object before it becomes the response
-
6CDMS→Clientreturns the object in
data
Result: 200 with firstname, lastname and the system fields.
When: You want to look at an object quickly, for example with curl or in the browser.
The flow is the same as with POST. CDMS just sets response to ["*"] itself. You get all simple fields and every reference with its id. For this you need the read role of every referenced model.
Result: 200 with everything * returns, or 403 if a reference role is missing.
When: The model has exactly one object, e.g. settings. Path without id: POST /read or GET /read.
-
1Client→CDMSsends
POST /crm/einstellungen/readwith{ "response": ["+"] } -
2CDMS→Databasefinds the one object itself, with the row filters
-
3CDMS→Clientnone there → 404
no-object-found -
4CDMS→Clientpresent → reads it like a normal object
Result: 200 with the one object. See Singletons.
When: You read through the hub API of an abstract model, e.g. /crm/kunde/read/{id}.
-
1Client→CDMSsends only the
id, no@type -
2CDMS→Databaselooks up the stored type for the
id, e.g.crm.privatkunde -
3CDMSpasses the request on to the API of the subtype, with its roles and filters
-
4CDMS→Clientreturns the object with
@type
Result: 200 with the fields of the subtype and "@type": "crm.privatkunde". See Abstract models.
When: The user lacks the role employee-read.
-
1Client→CDMSsends a valid read request
-
2CDMSchecks the read role of
employee -
3CDMS→Clientrole missing → 403
missing-permission|employee-read
Result: 403. The database is not even asked.
When: The id does not exist, or the object belongs to another user or is excluded by an attribute filter.
-
1CDMS→Databaselooks for the row with the
idand all row filters -
2CDMS→Clientno row found → 404
not-found
Result: 404. The client cannot tell whether the object does not exist or whether it may not see it.
Decision table
| response in body | role employee-read | object visible | roles of the requested references | Response |
|---|---|---|---|---|
| no | – | – | – | 400 response |
| yes | no | – | – | 403 missing-permission|employee-read |
| yes | yes | no | – | 404 not-found |
| yes | yes | yes | one is missing | 403 missing-permission|<role> |
| yes | yes | yes | all present or none requested | 200 |
The table describes the default, strict mode. When it is switched off, status and key change: without employee-read you get 404, and a single reference without the role is silently null. See Strict mode.
Pitfalls
Where to go next
- What may go into
response: Field selection withresponse - Many fields at once: Wildcards + and *
- Loading references fully: Expanding references and lists
- The three error codes compared: 401, 403 or 404?