Reference Traversal
Reference traversal lets you filter thogits based on the properties of other thogits they link to. Instead of looking up a referenced thogit’s ID and filtering by it, you write a single filter expression that crosses the reference boundary using the -> operator.
How Reference fields work
Section titled “How Reference fields work”A Reference field stores a ULID pointing to another thogit. When you define a schema like this:
{ "assignee": "Reference", "project": "Reference"}The values are thogit IDs:
{ "assignee": "01JQXYZ000PERSON00000ALICE", "project": "01JQXYZ000PROJECT0000API"}On its own, you can filter by exact ID: {"Task.assignee": "01JQXYZ000PERSON00000ALICE"}. But that requires knowing the ID upfront. Reference traversal lets you filter by properties of the target instead.
The -> syntax
Section titled “The -> syntax”The arrow operator -> chains a Reference field to a filter on the target thogit:
SourceTag.ref_field->TargetTag.target_fieldFor example:
{"Task.project->Project.name": {"contains": "API"}}This reads as: “Find tasks whose project reference points to a thogit tagged Project where the name field contains ‘API’.”
The server resolves this by:
- Looking at each Task thogit’s
projectfield value (a ULID) - Loading the referenced thogit
- Checking if it has the
Projecttag - Checking if its
namefield contains “API” - Including the original Task thogit in results if the check passes
Multi-hop traversal
Section titled “Multi-hop traversal”You can chain multiple -> operators to traverse through several references. Each hop follows a Reference field to a new thogit and applies the next segment.
{"Task.project->Project.lead->Person.department": {"match": "Engineering"}}This reads as: “Find tasks whose project’s lead is a person in Engineering.”
The chain resolves left to right:
Task.project— follow the task’s project reference->Project.lead— on that project, follow the lead reference->Person.department— on that person, check the department field
Target filters
Section titled “Target filters”The final segment after the last -> can be:
| Target | Example | Description |
|---|---|---|
| Tag field | ->Project.name | Filter on a field of the target thogit |
| Thogit name | ->name | Filter on the target thogit’s name |
| Thogit description | ->description | Filter on the target thogit’s description |
| Tag presence | ->has_tag | Check if the target has a specific tag |
Complete walkthrough
Section titled “Complete walkthrough”This section walks through setting up a full reference graph and querying across it.
-
Create the Person tag
Section titled “Create the Person tag”Terminal window curl -X POST https://app.thogits.com/api/tags \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"name": "Person","fields": {"department": {"type": "Select","variants": ["Engineering","Design","Product","Marketing"]},"title": "String"}}'Save the
tag_idfrom the response — used below asPERSON_TAG_ID. -
Create the Project tag
Section titled “Create the Project tag”Terminal window curl -X POST https://app.thogits.com/api/tags \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"name": "Project","fields": {"name": "String","lead": "Reference","status": {"type": "Select","variants": ["Planning","Active","Complete"]}}}'Save as
PROJECT_TAG_ID. -
Create the Task tag
Section titled “Create the Task tag”Terminal window curl -X POST https://app.thogits.com/api/tags \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"name": "Task","fields": {"status": {"type": "Select","variants": ["Todo","In Progress","Done"]},"priority": "Number","project": "Reference","assignee": "Reference"}}'Save as
TASK_TAG_ID. -
Create people
Section titled “Create people”Terminal window # Alice - Engineeringcurl -X POST https://app.thogits.com/api/thogits \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"name": "Alice Chen","tags": [{"tag_ref": {"Existing": "PERSON_TAG_ID"},"field_values": {"department": {"variant": "Engineering"},"title": "Staff Engineer"}}]}'# Response: {"thogit_id": "ALICE_ID"}# Bob - Designcurl -X POST https://app.thogits.com/api/thogits \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"name": "Bob Park","tags": [{"tag_ref": {"Existing": "PERSON_TAG_ID"},"field_values": {"department": {"variant": "Design"},"title": "Senior Designer"}}]}'# Response: {"thogit_id": "BOB_ID"} -
Create projects
Section titled “Create projects”Terminal window # API project led by Alicecurl -X POST https://app.thogits.com/api/thogits \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"name": "API Redesign","tags": [{"tag_ref": {"Existing": "PROJECT_TAG_ID"},"field_values": {"name": "API Redesign","lead": "ALICE_ID","status": {"variant": "Active"}}}]}'# Response: {"thogit_id": "API_PROJECT_ID"}# Brand project led by Bobcurl -X POST https://app.thogits.com/api/thogits \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"name": "Brand Refresh","tags": [{"tag_ref": {"Existing": "PROJECT_TAG_ID"},"field_values": {"name": "Brand Refresh","lead": "BOB_ID","status": {"variant": "Active"}}}]}'# Response: {"thogit_id": "BRAND_PROJECT_ID"} -
Create tasks
Section titled “Create tasks”Terminal window # Task on API project, assigned to Alicecurl -X POST https://app.thogits.com/api/thogits \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"name": "Design new endpoint schema","tags": [{"tag_ref": {"Existing": "TASK_TAG_ID"},"field_values": {"status": {"variant": "In Progress"},"priority": 1,"project": "API_PROJECT_ID","assignee": "ALICE_ID"}}]}'# Task on Brand project, assigned to Alicecurl -X POST https://app.thogits.com/api/thogits \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"name": "Review brand guidelines","tags": [{"tag_ref": {"Existing": "TASK_TAG_ID"},"field_values": {"status": {"variant": "Todo"},"priority": 2,"project": "BRAND_PROJECT_ID","assignee": "ALICE_ID"}}]}'# Task on API project, unassignedcurl -X POST https://app.thogits.com/api/thogits \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"name": "Write migration scripts","tags": [{"tag_ref": {"Existing": "TASK_TAG_ID"},"field_values": {"status": {"variant": "Todo"},"priority": 3,"project": "API_PROJECT_ID"}}]}' -
Query with reference traversal
Section titled “Query with reference traversal”Now use the
->operator to query across these relationships.Find tasks on projects led by someone in Engineering:
Terminal window curl -X POST https://app.thogits.com/api/thogits/search \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"filter": {"Task.project->Project.lead->Person.department": {"match": "Engineering"}}}'This returns “Design new endpoint schema” and “Write migration scripts” — both tasks on the API Redesign project, which is led by Alice (Engineering). The “Review brand guidelines” task is excluded because its project is led by Bob (Design).
Find tasks on projects whose name contains “API”:
Terminal window curl -X POST https://app.thogits.com/api/thogits/search \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"filter": {"Task.project->Project.name": {"contains": "API"}}}'Combine traversal with direct filters:
Terminal window curl -X POST https://app.thogits.com/api/thogits/search \-H "Content-Type: application/json" \-b cookies.txt -c cookies.txt \-d '{"filter": {"and": [{"Task.priority": {"lte": 2}},{"Task.project->Project.lead->Person.department": {"match": "Engineering"}},{"not": {"Task.status": {"match": "Done"}}}]}}'This finds high-priority, not-done tasks on Engineering-led projects. Only “Design new endpoint schema” matches (priority 1, In Progress, on the Alice-led API project).
Edge cases
Section titled “Edge cases”Dangling references — If a Reference field points to a thogit that has been deleted, the traversal treats the filter as not matching (the thogit is excluded from results). No error is raised.
Null references — If the Reference field is null (not set), the traversal does not match. Use is_null to explicitly find thogits with unset references:
{"Task.assignee": {"is_null": true}}Non-Reference fields — Using -> on a field that is not a Reference type produces a validation error. The server checks the schema before executing the query.
Performance considerations
Section titled “Performance considerations”Each hop in a traversal requires loading the referenced thogit and checking its fields. For large datasets:
- Keep traversal chains short (1-2 hops is ideal)
- Combine traversals with direct filters in
andblocks to narrow the candidate set first - Use
has_tagas the first condition to limit which thogits are evaluated