SG Ground Truth

entity

An entity link is a {type,id} hash under relationships, cleared only by null. Enforce valid_types yourself: it binds on a few fields, is ignored on most, and nothing in the schema marks which.

API

Data type entity, probed on stock Version fields. On the probed site 10 of Version's 71 fields are entity: seven editable, plus created_by, updated_by and image_source_entity, which are not.

Read Under relationships, never attributes. ?fields=entity puts nothing in attributes, so a reader that only walks attributes sees every link as absent (probe 003, probe 004):

"relationships": {"entity": {
  "data":  {"id": 1230, "name": "charA", "type": "Asset"},
  "links": {"self":    "/api/v1/entity/versions/17055/relationships/entity",
            "related": "/api/v1/entity/assets/1230"}}}

data is null when unlinked. name is the target's cached_display_name, filled on every type measured, so a picker needs no second call. It matches the target's identity field only because cached_display_name copies it: Delivery reads #<id>: <title>, and a client wanting the raw code or content reads the target (probe 060). A dotted path reads back on a single link (unlike multi_entity, probe 016), but it is typed:

row attributes
links an Asset {"code": "charA_art_v001", "entity.Shot.code": null}
links a Shot {"code": "sh010_0010_comp_v001", "entity.Shot.code": "sh010_0010"}

A middle segment outside this field's valid_types drops the key at 200 rather than reading null (probe 059).

Write PUT /entity/versions/<id> with a {"type": ..., "id": ...} hash; identical shape on create (probe 012).

sent result
{"type": "Shot", "id": A} 200, reads back {"id":7478,"name":"zzprobe_entity_a","type":"Shot"}
7479 (bare int) 400 API update() Version.entity expected [Hash, ActiveSupport::HashWithIndifferentAccess, ActionDispatch::Http::Parameters, ActionDispatch::Http::ParamsHashWithIndifferentAccess, NilClass] data type(s) but got Integer: 7479
{"id": 7479} (no type) 400 API update() invalid/missing entity hash string 'type': {"id" => 7479} Valid entity types: ["ActionMenuItem", "ApiUser", ... all 113 site entity types listed in full ...]
{"type": "Asset", "id": <a real Shot id>} 400 Update failed for [Version.entity]: Value is not legal.
{"type": "Shot", "id": 99999999} 400 Update failed for [Version.entity]: Value is not legal.
a Shot in another project 200, linked; no project-consistency check

Value is not legal is the only signal for both a wrong type and a missing id, and it has no source and no detail. Every failed write left the previous value intact: no partial update.

Whether valid_types binds is per field, and the schema does not mark which. Every entity field on Version, each sent a type its own list omits, and every other case the type cards measured:

field valid_types sent result measured in
Version.client_approved_by ['HumanUser','ClientUser'] Shot 200, reads back as a Shot here
Version.entity 15 types Task 200, reads back as a Task here
Version.sg_task ['Task'] Shot 200, reads back as a Shot here
Version.source_clip ['SourceClip'] Shot 200, reads back as a Shot here
Version.user ['HumanUser','ApiUser','Group'] Shot 200, reads back as a Shot here
Shot.sg_sequence ['Sequence'] Shot 200, reads back as a Shot entity_types/Shot
Task.entity 8 types Task 200, reads back as a Task entity_types/Task
TimeLog.entity ['Task'] Shot, Project 201 each, stored as sent entity_types/TimeLog
Note.note_links, multi_entity 36 types Project, HumanUser 201 each, the link read back entity_types/Note
Cut.entity ['Sequence','Scene','Episode','Reel'] Shot, Version 200 each, read back as sent entity_types/Cut
Cut.sg_scene ['Scene'] Shot 200, reads back as a Shot entity_types/Cut
Version.project ['Project'] Shot 400 Update failed for [Version.project]: Project expected, got Shot here
Version.task_template ['TaskTemplate'] Shot 400 Update failed for [Version.task_template]: TaskTemplate expected, got Shot here
Sequence.episode ['Episode'] Sequence 400 Update failed for [Sequence.episode]: Episode expected, got Sequence entity_types/Sequence
TimeLog.user ['HumanUser'] ApiUser, Project 400 Invalid field value, update failed [5 - Update failed for [TimeLog.user]: HumanUser expected, got ApiUser] entity_types/TimeLog
Cut.version ['Version'] Shot 400 Update failed for [Cut.version]: Version expected, got Shot entity_types/Cut
CutItem.cut ['Cut'] Shot 400 Cut expected, got Shot entity_types/CutItem
CutItem.shot ['Shot'] Version 400 Shot expected, got Version entity_types/CutItem
CutItem.version ['Version'] Shot 400 Version expected, got Shot entity_types/CutItem
Version.created_by, updated_by ['HumanUser','ApiUser'] Shot 400 API update() Version.created_by is editable on create only. here
Version.image_source_entity 114 types Task 400 API update() Version.image_source_entity is read only. here

No property in the schema separates the two groups, and the obvious guesses do not survive the table. The count of declared types does not predict it: Version.source_clip and TimeLog.entity each declare one type and are advisory, while Version.project and TimeLog.user each declare one and bind. The sg_ prefix does not predict it either: Version.source_clip has no prefix and is advisory. Two fields on one type can differ, as TimeLog.entity and TimeLog.user do.

A field that binds names the expected type, <Expected> expected, got <Sent>, at code: 104. The last two rows refuse for editability, not for type.

Clear

sent result
null cleared, reads back null
{} 400 invalid/missing entity hash string 'type': {} Valid entity types: [... 113 ...]
"" 400 ... expected [Hash, ... NilClass] data type(s) but got String: ""
[] 400 ... expected [Hash, ... NilClass] data type(s) but got Array: []
{"type": "Shot", "id": null} 400 invalid/missing entity hash integer 'id': {"type" => "Shot", "id" => nil}

There is no "empty entity" value, and a cleared link is findable: ["entity", "is", None] matches the row.

Filter A bogus relation makes the API name the whole vocabulary (probe 017), here verbatim from errors[0].source["Version.entity"]:

Valid relations: ["is", "is_not", "name_contains", "name_not_contains", "name_is", "type_is", "type_is_not", "in", "not_in"]

Three value shapes across the nine. Row counts against a 100-Version baseline, where 862 stands in for the id of the Shot sh010_0010 names:

operatorvaluematches
is {"type": "Shot", "id": 862} 3; null 0
is_not {"type": "Shot", "id": 862} 97; null 100
in [{"type": "Shot", "id": 862}], or a bare hash 3 for one hash, 6 for two
not_in [{"type": "Shot", "id": 862}], or a bare hash 97 for one hash, 94 for two
type_is "Shot" 99; a name string 0; null 0
type_is_not "Shot" 1; a name string 100; null 100
name_is "sh010_0010" 3; a type string 0
name_contains "sh010_0010" 3; a type string 0
name_not_contains "sh010_0010" 97; a type string 100

Every shape outside that column 400s: [{"id": N}] and bare ints throughout, a list for is/is_not, a hash for type_* and name_*, null for the name_* three.

Dotted paths work, and every negative control returns 0 rather than the baseline:

filter rows
entity.Shot.code is 'sh010_0010' 3
entity.Shot.code in [2 real codes] 6
entity.Shot.code contains '010_00' 21
entity.Shot.code is 'ZZZNOPE' 0
entity is {Shot, 99999999} 0
entity is None 0 (all 100 linked here)

Traps

  • Validate the type against valid_types client-side, on every field. A client cannot predict which field will protect it, and an unenforced write stores nonsense at 200: sg_task (['Task']) took a Shot and read it back as a Shot.
  • No project-consistency check. Pointing a sandbox Version at a Shot in another project returned 200 and read back linked, with nothing in the response flagging it. Compare projects before writing.
  • A wrong type for a real id 400s here only because ids come from one site-wide sequence (250 shots 862..1111 vs 250 assets 1226..3588, zero overlap), so the wrong table holds no such row. That is a lookup miss, not type validation.
  • Reading attributes alone makes every entity link look absent, and filter[] needs the full {type, id}: [{"id": N}] and bare ints both 400 (probe 017), while a bad ?fields name is silently dropped at 200 (probe 004).

Every entry on this site is the output of a probe in probes/. The corpus is generated by running those probes against a live Flow Production Tracking site, not written from memory.

Not affiliated with or endorsed by Autodesk. Flow Production Tracking is their product; this is an independent record of how its REST API answers.