Skip to main content

Objects

Create, read, update, and delete individual objects. Type names in the path are always the plural REST name — see TypeFQN to URL mapping.

Create

POST /data/{tenant}/{typeName}

Creates a new object. Returns 201 Created with a Location header pointing at the new object's URI and no response body. To retrieve the created object, issue a follow-up Get one against the Location URI.

TODO

Interview needed. Document: request body shape (which fields are required vs. optional, how relations and files are supplied), what fields are computed server-side (formulas, id, name), and validation/error behavior.

POST /data/{tenant}/{referenceTypeName}/{referenceId}/{typeName}

Creates a new object of typeName linked to the existing referenceTypeName/referenceId object. An optional ?type= query parameter selects a concrete subtype to instantiate.

Returns 201 Created with a Location header pointing at the new object's URI and no response body. To retrieve the created object, issue a follow-up Get one against the Location URI.

TODO

Interview needed. Document: which relation the new object is attached through (how the reference is chosen when multiple relations connect the two types), the role of the type (subtype) parameter, and how it differs from creating then patching.

Get one

GET /data/{tenant}/{typeName}/{id}

Returns a single object as a DataObjectPresentation.

TODO

Interview needed. Document the DataObjectPresentation shape: object fields, relation links, formulas, presentation metadata, and how it differs from the list-row shape returned by querying.

Get own

GET /data/{tenant}/{typeName}/own

Returns the object of this type that belongs to the authenticated caller.

TODO

Interview needed. Explain the "own" concept: which type(s) support it (user profile?), how ownership is determined from the authenticated identity, and the response when the caller has no such object.

Patch

PATCH /data/{tenant}/{typeName}/{id}

Partially updates an object. Only the fields present in the body are changed.

Returns 204 No Content with a Location header pointing at the patched object's URI and no response body. To retrieve the updated object, issue a follow-up Get one against the Location URI.

TODO

Interview needed. Document: merge semantics (how to clear a field vs. leave it untouched), how relations are added/removed/replaced, whether formulas re-run, and concurrency/versioning if any.

Delete

DELETE /data/{tenant}/{typeName}/{id}

Deletes a single object.

TODO

Interview needed. Document cascade behavior (what happens to related objects and inverse relations), soft vs. hard delete, and the success/error responses.

Merge

POST /data/{tenant}/{typeName}/{id}/merge

Merges another object of the same BASIC class — the loser — into this object — the winner, identified by typeName/id — then deletes the loser.

{
"loser": "550e8400-e29b-41d4-a716-446655440000",
"patch": {
"email": "winner@example.com"
}
}
  • loser — the id of the object to merge away, and delete, into the winner. Accepts a bare UUID or a full resource URI (e.g. an href copied straight out of a _links entry) — only the last 36 characters are read, so a truncated or malformed value is not rejected the way an invalid patch field would be.
  • patch — optional, same shape as the body of Patch. Applied to the winner exactly like an ordinary patch: fields and relations not mentioned keep the winner's current value. The server never compares the winner's and loser's field values or detects conflicts — resolving which value wins per field is entirely up to the caller, expressed by what is (and isn't) included in patch.

Returns 204 No Content with a Location header pointing at the winner's URI and no response body. To see the winner's state after the merge — including any repointed relations — issue a follow-up Get one against the Location URI.

What happens, in order:

  1. patch is applied to the winner as an ordinary patch.
  2. Any classes the loser has that the winner doesn't are added to the winner (a union), regardless of patch. This keeps working scripts and gateways bound to the loser's types active on the merged object.
  3. Every relation that points at the loser from other objects is repointed to the winner: single-valued relations are overwritten, multi-valued relations are deduplicated (if the winner is already linked, the loser's link is dropped rather than duplicated). The winner's own relations only change through step 1.
  4. The winner is saved.
  5. The loser is deleted through the normal delete pipeline: a hard delete, with no tombstone and no pointer left behind from the loser to the winner. By this point nothing should still reference the loser, so its delete constraints normally pass — but see the 409 case below.

History-enabled fields are treated like any other field for conflict purposes: the entire history list follows whichever side won that field (winner's or loser's), never interleaved.

Errors:

StatusWhen
400 Bad RequestThe loser's BASIC class differs from the winner's.
404 Not FoundtypeName, the winner id, or the loser could not be resolved.
409 ConflictDeleting the loser violates a delete constraint that relation repointing didn't clear — the same conflict a direct DELETE on the loser would raise.

Notes:

  • The merge is best-effort, not transactional across the platform's stores, consistent with save/delete elsewhere in this API.
  • Passing the winner's own id as loser is not guarded against: the object is patched, then immediately deleted by step 5. Never pass the same id for both sides.
  • There is no server-side conflict UI or SDK helper for this endpoint yet — callers build the patch payload themselves.