SG Ground Truth

Endpoints — Records, GET

Every call in this family: what the card records, the edge cases that live on the call, and the verdict of every entry that measured it. Each of those lines names the door holding that entry's rules. The map is corpus/INDEX.md.

API

GET /entity/<type>

Pages rows. An entity field is returned under relationships and never attributes, an unknown fields name is dropped at 200, and links.next is emitted on empty pages forever.

you send what happens
fields=code,sg_not_a_field 200, and the key is absent from attributes
page[number]=99999 200, data: [], and links.next points at page 100000
page[size]=5000 200, 5000 rows. No cap was reached; the 500 limit is folklore
page[size]=0 400
  • Stop paging when data is empty. links.next is never absent, so a loop waiting for it to disappear never ends.

  • An entity link is under relationships, with the row's name alongside its id. Reading attributes alone makes every link look null.

Measured by

  • 001_auth (findings) — Send the token request as application/x-www-form-urlencoded: application/json is 400 Invalid JSON body. client_credentials returns a 600s bearer, so ignore the refresh_token and re-auth.
    rules: doors/findings-auth
  • 027_auth_permissions (findings) — The token endpoint accepts password and session_token. Impersonation is the OAuth2 scope sudo_as_login:, never a body field, and a lower level reads far fewer rows and as many fields.
    rules: doors/findings-auth
  • 004_array_vs_hash (findings) — api3_array/api3_hash are a POST _search request Content-Type, not a GET Accept header: as Accept they 406, and entity fields are returned under relationships either way.
    rules: doors/findings-protocol
  • 028_loud_and_silent (findings) — A 400 is trustworthy and usually names the legal set, but a 200 proves nothing: an unknown field, sort key or query param is a no-op, and a batch can return an id for a row it never made.
    rules: doors/findings-protocol
  • 051_api_version (findings) — /api/v1 and /api/v1.1 are the same API. Across 20 read-only calls the only difference is api_version in the root document and the prefix each echoes in its own links. Any other segment is 404.
    rules: doors/findings-protocol
  • 061_shipped_statuses (findings) — Nothing in the schema marks a shipped Status. system is true on a minority of them; the stock set is created_by is null, plus options[return_only]=retired for the rows a site retired.
    rules: doors/findings-schema
  • 003_query (findings) — A dotted ?fields path comes back flat under literal key "sg_task.Task.content" in attributes; an entity field is returned under relationships as {data, links}. Never read a row from attributes alone.
    rules: doors/findings-read
  • 005_link_usage (findings) — On the sample project every Version links through entity (99% Shot, 1% Asset) and only 1% through sg_task, so measure link usage per site rather than hardcoding Task-linking.
    rules: doors/findings-read
  • 006_pagination (findings) — links.next is emitted on every page forever, including zero-row ones, so stop paging when data is empty and never on a missing next.
    rules: doors/findings-read
  • 007_fill_rates (findings) — On the sample project 30 of 71 Version fields are populated. Rank by fill rate, but drop checkbox, summary and computed fields first: False and 0 are not null and read as 100% filled.
    rules: doors/findings-read
  • 018_project_listing (findings) — sg_status is not a liveness filter and is null on 15 of 22 projects; is_template, is_demo and archived are the discriminators, so pick the ones your list wants - is_demo hides the demo show.
    rules: doors/findings-read
  • 023_pages (findings) — A page's layout is the PageSetting row whose user is null; settings_json reads back as decoded JSON and body/list_content settings.columns is the column list. Every filter on it is ignored.
    rules: doors/findings-read
  • 026_result_order (findings) — Rows come back id ascending unless you sort; ["id", "in", [...]] discards the order of the list, and an unsortable or unknown sort field is a silent 200 no-op where the same name in a filter 400s.
    rules: doors/findings-read
  • 059_dotted_path_type_check (findings) — The middle segment of a dotted path is checked against the field's valid_types in a projection and against the schema alone in a filter: the projection drops the key at 200, the filter 400s.
    rules: doors/findings-read
  • 016_dotted_multi_entity (findings) — A dotted path through a multi_entity field reads back nothing: HTTP 200 with the key silently absent from attributes. Filters on that same path work, including two hops.
    rules: doors/findings-filter
  • 030_complex_filters (findings) — api3_hash nests and/or groups 265 deep and mixes leaves with sub-groups; api3_array cannot express or, query-string filter[] is ignored on _search, and {path,relation,values} runs nowhere.
    rules: doors/findings-filter
  • 011_create_project (findings) — A script user can create a Project with nothing but {"name": ...}, at 201, but the response echoes only 6 attributes, so read the project back if you need anything else.
    rules: doors/findings-write
  • 010_status_icons (findings) — Status.icon is an entity link under relationships; display_type picks one of three renderings; url is empty unless image_data is asked for beside it; the stock sprite is in the site's own stylesheet.
    rules: doors/findings-render
  • 004_register_published_file (recipes) — Register the next PublishedFile without overwriting the last one, and write a path the server resolves for every platform
    rules: doors/recipes
  • 003_sort_fails_silently (reports) — A sort on an unknown or unsortable field answers 200 with the rows in default order, while the same field name in a filter answers 400 and names the reason.
    rules: doors/reports
  • 008_jsonb_filters_return_everything (reports) — A filter on PageSetting.settings_json or EventLogEntry.audit_trail is accepted and ignored, so the unfiltered set comes back at 200 and is_null and is_not_null each return every row.
    rules: doors/reports

Silent on this call

  • get_entity_type — Pages rows. An entity field is returned under relationships and never attributes, an unknown fields name is dropped at 200, and links.next is emitted on empty pages forever.
  • 028_loud_and_silent — A 400 is trustworthy and usually names the legal set, but a 200 proves nothing: an unknown field, sort key or query param is a no-op, and a batch can return an id for a row it never made.
  • 023_pages — A page's layout is the PageSetting row whose user is null; settings_json reads back as decoded JSON and body/list_content settings.columns is the column list. Every filter on it is ignored.
  • 026_result_order — Rows come back id ascending unless you sort; ["id", "in", [...]] discards the order of the list, and an unsortable or unknown sort field is a silent 200 no-op where the same name in a filter 400s.
  • 059_dotted_path_type_check — The middle segment of a dotted path is checked against the field's valid_types in a projection and against the schema alone in a filter: the projection drops the key at 200, the filter 400s.
  • 016_dotted_multi_entity — A dotted path through a multi_entity field reads back nothing: HTTP 200 with the key silently absent from attributes. Filters on that same path work, including two hops.
  • 030_complex_filters — api3_hash nests and/or groups 265 deep and mixes leaves with sub-groups; api3_array cannot express or, query-string filter[] is ignored on _search, and {path,relation,values} runs nowhere.

corpus/endpoints/get_entity_type.md

GET /entity/<type>/<id>

One row, and the only read where fields is honoured on a single record. A retired row is 404 here and 200 under options[return_only]=retired.

  • The 404 for "never existed" and the 404 for "retired" are the same message with the same code. Only a second call with options[return_only]=retired tells them apart, and that distinction is the whole of what DELETE does.

  • Code 104 here, against code 103 for a bad type name. 104 is "this row is not there", 103 is "your request is wrong".

  • This is the only read where fields is honoured on a single record. Every write ignores it.

Measured by

  • 060_entity_dict_name (findings) — The name in an entity dict is the target's cached_display_name, filled on every type measured, single and multi alike. Read it, not the per-type identity field, and expect decoration.
    rules: doors/findings-read
  • 058_local_storage_roots (findings) — One create fills every local_path_* the storage row defines, whichever platform's root the path was under. The server picks the deepest matching root, and no conditional-write header is honoured.
    rules: doors/findings-write
  • 003_query_fields_and_pages (recipes) — Resolve a query field's value, and run the rows a saved Page shows
    rules: doors/recipes
  • 005_propagate_status (recipes) — Roll a status up from a parent's Tasks and Versions onto the parent, without racing a concurrent write
    rules: doors/recipes
  • 006_media_round_trip (recipes) — Take media off one Version and put the same bytes on another, which is what every sync, transfer and hand-off does
    rules: doors/recipes
  • 007_build_and_reconcile_a_cut (recipes) — Write a Cut and its CutItems from an edit, read the timeline back, and reconcile a second edit against the Cut already there
    rules: doors/recipes
  • 009_multi_entity_safely (recipes) — Add to and remove from a multi_entity field without destroying the links you did not mean to touch
    rules: doors/recipes
  • 013_publish_file_bytes (recipes) — Publish a file's bytes onto a PublishedFile when the caller has no LocalStorage root to write under
    rules: doors/recipes

Silent on this call

  • 058_local_storage_roots — One create fills every local_path_* the storage row defines, whichever platform's root the path was under. The server picks the deepest matching root, and no conditional-write header is honoured.
  • 005_propagate_status — Roll a status up from a parent's Tasks and Versions onto the parent, without racing a concurrent write
  • 009_multi_entity_safely — Add to and remove from a multi_entity field without destroying the links you did not mean to touch

corpus/endpoints/get_entity_type_id.md

GET /entity/<type>/<id>/<field>

Reads one image or attachment field, and with ?alt redirects to the bytes. Every other data type is a 400, so this is not a cheap single-field read.

<field> result
image, an image field 200, data is a URL string
sg_uploaded_movie, a url field 200, data is the attachment hash
code, sg_status_list, id 400 is not an image or attachment
entity, playlists 400, the same message. Use relationships/<field>
entity.Shot.code 406, one byte
a name the type does not have 404, code 103
  • A dotted path 406s because the segment after the last dot is read as a format extension, not as a field. There is no single-field read for a dotted path.

  • Range without ?alt is ignored and the field hash comes back at 200. The header only reaches storage once the redirect is in play.

  • Reading image here costs about the same as GET /entity/<type>/<id>?fields=image, because the presigned URL is most of both bodies.

  • An empty field answers 200 with "data": null. Add ?alt to the same call and it is a 404, worded differently per data type: Field sg_uploaded_movie is empty. for url, File not found for image. Read the field first if the difference between empty and missing matters.

Measured by

  • 048_one_record_beyond_crud (findings) — POST on one record is revive, not update: ?revive=1 is required and the body is ignored. /<field> reads image and attachment fields only, and relationships/<field> is the same data, unpaged.
    rules: doors/findings-read

Silent on this call

  • 048_one_record_beyond_crud — POST on one record is revive, not update: ?revive=1 is required and the body is ignored. /<field> reads image and attachment fields only, and relationships/<field> is the same data, unpaged.

corpus/endpoints/get_entity_type_id_field.md

GET /entity/<type>/<id>/relationships/<related_field>

The entity or multi_entity link list on its own, unpaged and unsorted. page, fields and sort are accepted and ignored, and every link is returned in one body.

you send result
page[size]=2 on a 60-link field 200, all 60 rows
page[number]=2 200, the same 60 rows
fields=code ignored, links stay {id, name, type}
sort=code ignored, source order kept
an image field 400 is not a relationship field
  • data is byte-identical to what GET /entity/<type>/<id>?fields=<field> returns under relationships, in the same order, minus the links.related pointer to the linked row.

  • There is no links.next and no measured page cap. The whole link list is in the one response.

  • The saving is small: on the probed site 120 bytes against 353 for a single link, and 3048 against 3231 for 60. Call it when the link list is the entire request, not to trim a read you are making anyway.

  • options[return_only]=retired is evaluated against the owning record, so it 404s on a live row rather than filtering the links.

  • GET is the only verb. POST and DELETE on this path are 404 (field_types/multi_entity); edit links with PUT /entity/<type>/<id>.

Measured by

  • 048_one_record_beyond_crud (findings) — POST on one record is revive, not update: ?revive=1 is required and the body is ignored. /<field> reads image and attachment fields only, and relationships/<field> is the same data, unpaged.
    rules: doors/findings-read

Silent on this call

  • 048_one_record_beyond_crud — POST on one record is revive, not update: ?revive=1 is required and the body is ignored. /<field> reads image and attachment fields only, and relationships/<field> is the same data, unpaged.

corpus/endpoints/get_entity_type_id_relationships_field.md

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.