POST /entity/<type>/_search
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
The only way to send a filter the query string cannot express, and it refuses application/json at 415 naming both vendor types. api3_array cannot express or; api3_hash nests.
| you send | what happens |
|---|---|
a query-string filter[] |
ignored. Only the body filters here |
{"path", "relation", "values"} as a condition |
400 Missing logical operator. That shape runs nowhere |
filters: [] |
200, unscoped, every row on the site |
an or under api3_array |
not expressible; the array form is and only |
- The unknown-operator 400 enumerates the legal set for that field's data type. It is the cheapest way to discover the vocabulary, and it is where the site's filter matrix comes from.
Measured by
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-protocol028_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-protocol062_cors(findings) — Every path under/api/v1answers the preflight and echoes anyOrigin, credentials true./internal_apiand the web paths send no CORS header, so a page on another origin proxies those.
rules:doors/findings-protocol061_shipped_statuses(findings) — Nothing in the schema marks a shipped Status.systemis true on a minority of them; the stock set iscreated_by is null, plusoptions[return_only]=retiredfor the rows a site retired.
rules:doors/findings-schema091_status_summary_exclusions(findings) — Excluded statuses are invisible to REST: not in /schema at any scope, not writable by PUT. status_list honours them: fin plus an excluded omt rolls up to fin, omt alone to na.
rules:doors/findings-schema003_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-read006_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-read018_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-read021_media_resolution(findings) — PublishedFile.path is returned with the LocalStorage join already done, so a client never reads LocalStorage or reassembles a root, but a platform whose storage root is unset reads null.
rules:doors/findings-read023_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-read026_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-read059_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-read060_entity_dict_name(findings) — Thenamein an entity dict is the target'scached_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-read072_page_layouts(findings) — A page's views are root settings.layouts [{name, display_name}], each name a child of the root. Query widgets sit at /body, inside a view, or in tabs: walk the tree, never read /body alone.
rules:doors/findings-read073_page_grid_settings(findings) — Stored pages group one level deep (one of 438 goes two), summarise a column once in 41 grids, and colour columns by DisplayColumn id, a type REST cannot read. mode is list on 94%.
rules:doors/findings-read075_page_overrides(findings) — A per-user override patches columns, widths, sorts, mode and the filter panel at any spec_path, "" meaning the root. One row per user and page; 3 of 32 patches name a path the shared tree lacks.
rules:doors/findings-read076_page_visibility(findings) — The script user is not the widest reader of Page: an Admin read 2048 pages where the script read 1107 and 404s on the rest. An Artist read 107. Every level reads every person's override.
rules:doors/findings-read081_dotted_image(findings) — entity.Shot.image returns the Shot's thumbnail as a presigned S3 URL under attributes, same object, fresh signature, in the same call. image is_not null matched 50 Shots whose image reads null.
rules:doors/findings-read082_page_size_cap(findings) — page[size] takes 1 to 5000 inclusive; 5001 is 400 "size must be less than 5000". Omitted, it is 500. A page costs ~330 ms whatever its size up to 500, so read big pages.
rules:doors/findings-read088_project_template_defaults(findings) — The per-entity-type default is readable atProject.tracking_settings.default_task_template.<Type>, a{type, id, name, valid}dict.Project.task_templatesis a separate list, not the default.
rules:doors/findings-read016_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-filter017_filter_operators(findings) — is/is_not/contains/not_contains/starts_with/ends_with/in/not_in all work, on text fields and through dotted paths; an unknown operator 400s on all 21 data types, naming the valid list on 16.
rules:doors/findings-filter030_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-filter068_note_read_state(findings) — read_by_current_user is per person and missing from the schema;isandis_notare evaluated, whilein,not_inand an unknownisvalue all return the unread rows at 200.
rules:doors/findings-filter071_note_link_name_filter(findings) — Filter notes about a thing onnote_links.<Type>.cached_display_name: it resolves for every valid type,code400s on Booking andnameon all but Department. The path cannot be read back.
rules:doors/findings-filter069_client_note(findings) —client_notecannot be set over REST:trueon create is 400 and anyPUTis 400editable on create only.sg_note_type: "Client"is the one marker a caller can write.
rules:doors/findings-write083_task_template_on_create(findings) — A create withtask_templatemakes the Tasks inside the same call, byPOSTand by_batch, copying every field set on the template tasks and their dependency types and offsets.
rules:doors/findings-write087_dependency_cascade(findings) — An upstream date write reschedules every unpinned downstream Task, later and earlier alike; a null one moves none (097). A pinned Task stays put and flagsdependency_violationwhile broken.
rules:doors/findings-write089_task_delete_side_effects(findings) — Deleting a Task retires its TaskDependency rows, unlinks both neighbours without bridging them, and nullsVersion.sg_taskandPublishedFile.task. Revive restores all of it.
rules:doors/findings-write092_dependency_edge_reschedule(findings) — A new edge reschedules an unpinned downstream Task at once, whether POSTed or copied by a template apply on claim. A pinned one keeps its dates and flagsdependency_violation.
rules:doors/findings-write093_clear_dates_pin(findings) — On a dependent Task a start_date write pins it, null or real; a due_date write never does, null or real. A pinned null Task holds; PUT pinned:false refills both dates.
rules:doors/findings-write094_permission_preflight(findings) — Ask with a write that cannot land: a no-op PUT per field (update), a POST with a bad status (create), a _batch of [delete, 404 sentinel] (delete). Permission is checked first, and nothing is written.
rules:doors/findings-write095_dependency_remove_undo(findings) — Remove an edge withDELETEon its TaskDependency row: revive restores its type and offset. Aremoveonupstream_tasksordownstream_taskserases the row for good.
rules:doors/findings-write096_task_template_unmerge(findings) — A template write re-syncs every Task linked to it: fields but status reset, edges rewired. Undo: old template_task per Task first, then old task_template, then delete what B made.
rules:doors/findings-write097_null_dates_unpin(findings) — A Task with no upstream never pins, on a null or a real date write; nulling its dates leaves its downstream unmoved, and pinned:false refills nothing.
rules:doors/findings-write098_template_merge_in_one_batch(findings) — Recipe 015's merge fits one_batch: requests run in order, so atask_templatewrite sees claims made earlier in the batch, andnullthenTon the same Shot re-runs the apply.
rules:doors/findings-write099_template_apply_edge_copy_kept(findings) — A template apply copies a missing edge between two Tasks whosetemplate_taskalready match it, whether either was claimed, kept, or created by that same call.
rules:doors/findings-write100_duration_write_pin(findings) — Writing duration on a dependent Task does not pin it; a start_date write in the same run does. It keeps following the upstream, and a duration write upstream moves it.
rules:doors/findings-write101_template_edge_conflict(findings) — On a claimed pair, a template apply replaces an existing edge of another type, or the reverse edge, with the template's edge: the old row is erased, not retired, and the PUT is a plain 200.
rules:doors/findings-write102_task_template_resync(findings) — Writing task_template T re-syncs every Task linked to T: T's non-empty values overwrite, status kept, dates and assignees kept or filled if empty; edges between linked Tasks reset to T's.
rules:doors/findings-write103_batch_delete_revive(findings) — Adeleteinside_batchretires a Task or TaskDependency exactly asDELETEdoes: same retired read-back, and revive returns the same id, fields, edges andVersion.sg_task.
rules:doors/findings-write104_template_unmerge_in_one_batch(findings) — Recipe 019's undo fits one_batchwith the same end state, if the batch skips edges its own task_template write removes: deleting one 404s and rolls back all. Undo to null deletes them.
rules:doors/findings-write105_offset_days_null_vs_zero(findings) — TaskDependencyoffset_daysnull and 0 are stored and compared as different: a template apply deletes an entity edge with null against a template 0 (or the reverse) and re-creates it with a new id.
rules:doors/findings-write106_template_task_linked_twice(findings) — With two Tasks linked to one template task, an apply re-syncs and wires only one of them, picked unpredictably (not by id, age or edges); the other is left as is. No error, nothing duplicated.
rules:doors/findings-write107_dependency_three_task_loop(findings) — A three-Task loop is a 400 on a direct create and inside_batch, which rolls back whole. A template apply deletes a claimed Task's upstream edge from a Task outside the template, loop or not.
rules:doors/findings-write108_task_template_resync_empties(findings) — Re-sync to T: a numeric 0 on T's task overwrites (est, duration); milestone false and "" (stored null) keep the Task's value; a Task with only start or only due keeps its null duration.
rules:doors/findings-write109_template_apply_outside_edge(findings) — A template apply erases an edge where a linked Task depends on a Task not linked to the template (root or not, other template, other Shot); it kept the edge with the outside Task downstream.
rules:doors/findings-write110_template_task_after_revive(findings) — Revive restorestemplate_task. A task_template write while the Task is retired re-creates it, so a later revive leaves two Tasks on one template task. Revive first, then write.
rules:doors/findings-write111_template_undo_outside_edge(findings) — An undo's write back to template A erases every edge whose downstream Task is A-linked and that A lacks, pre-merge edges included; recipe 022 DELETEs one such edge, 404s and rolls back.
rules:doors/findings-write112_template_unmerge_linked_twice(findings) — Undo relinking two Tasks to one template task: A wires either one (11 of 14 picked the loser), nothing is made. Relink the loser after the task_template write: then it matches the one-link undo.
rules:doors/findings-write014_attach_file(findings) — Leave the field out of the _upload path and the file is stored as an Attachment on attachment_links; read it back with POST /entity/attachments/_search, never flat filter[].
rules:doors/findings-upload025_event_log(findings) — meta.old_value and meta.new_value answer "what was this before", but meta is unfilterable and unsortable: narrow on entity, event_type and attribute_name, sort -id, read meta yourself.
rules:doors/findings-observe049_script_events(findings) — A script's writes reach the event log only while its ApiUser has generate_event_log_entries True. The default is False and nothing errors when off. One create logs one row per field plus one _New.
rules:doors/findings-observe077_page_change_stamps(findings) — PageSetting has no updated_at; Page.updated_at moves when its layout is saved. Poll Page.updated_at; Shotgun_PageSetting_Change names which setting changed but its entity is null on 131 of 500.
rules:doors/findings-observe090_template_task_events(findings) — A template-generated Task logs like a hand-made one plus atemplate_taskchange row,in_createtrue, credited to the caller. Filterattribute_nametemplate_taskto find them.
rules:doors/findings-observe003_query_fields_and_pages(recipes) — Resolve a query field's value, and run the rows a saved Page shows
rules:doors/recipes004_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/recipes005_propagate_status(recipes) — Roll a status up from a parent's Tasks and Versions onto the parent, without racing a concurrent write
rules:doors/recipes007_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/recipes009_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/recipes010_status_picker(recipes) — List the statuses a project actually offers, each with the label, colour and icon needed to draw it
rules:doors/recipes014_notes_about(recipes) — Find the Notes about a Shot, Asset or Version by the name of the thing, and read what each Note is linked to
rules:doors/recipes015_apply_task_template_without_duplicates(recipes) — Apply a task template to an entity that already has Tasks, without duplicating the ones it already holds
rules:doors/recipes018_remove_and_restore_a_dependency(recipes) — Remove one dependency between two Tasks and put it back on undo, with its type and offset
rules:doors/recipes019_undo_task_template_merge(recipes) — Undo a task template merge, returning an entity's Tasks, fields and dependencies to their state before it
rules:doors/recipes020_apply_task_template_in_one_batch(recipes) — Apply a task template to an entity that already has Tasks, without duplicates, in one atomic call
rules:doors/recipes021_undo_a_batch_delete(recipes) — Delete Tasks or dependencies in one batch and undo it by reviving the same rows
rules:doors/recipes022_undo_task_template_merge_in_one_batch(recipes) — Undo a task template merge in one atomic call, returning Tasks, fields and dependencies to their state before it
rules:doors/recipes023_undo_task_template_merge_with_a_task_linked_twice(recipes) — Undo a task template merge when two Tasks pointed at the same old template task, without the server picking which one gets the edges
rules:doors/recipes003_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/reports008_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
post_entity_type_search— The only way to send a filter the query string cannot express, and it refusesapplication/jsonat 415 naming both vendor types.api3_arraycannot expressor;api3_hashnests.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.017_filter_operators— is/is_not/contains/not_contains/starts_with/ends_with/in/not_in all work, on text fields and through dotted paths; an unknown operator 400s on all 21 data types, naming the valid list on 16.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.068_note_read_state— read_by_current_user is per person and missing from the schema;isandis_notare evaluated, whilein,not_inand an unknownisvalue all return the unread rows at 200.025_event_log— meta.old_value and meta.new_value answer "what was this before", but meta is unfilterable and unsortable: narrow on entity, event_type and attribute_name, sort -id, read meta yourself.049_script_events— A script's writes reach the event log only while its ApiUser has generate_event_log_entries True. The default is False and nothing errors when off. One create logs one row per field plus one _New.005_propagate_status— Roll a status up from a parent's Tasks and Versions onto the parent, without racing a concurrent write009_multi_entity_safely— Add to and remove from a multi_entity field without destroying the links you did not mean to touch
corpus/endpoints/post_entity_type_search.md