Schema Types
Tags define structured schemas — each field in a tag schema has a type that determines what values are valid. Thogits stores 7 field types. All fields are nullable by default: a null value or a missing key in field_values is always valid regardless of type.
Type Reference
Section titled “Type Reference”| Type | Schema JSON | Value JSON | Validation |
|---|---|---|---|
| String | "String" | "hello" | Must be a JSON string |
| Number | "Number" | 42 or 3.14 | Must be a JSON number (f64) |
| Boolean | "Boolean" | true or false | Must be a JSON boolean |
| Date | "Date" | "2024-01-15" or "2024-01-15T10:30:00" | YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS |
| Reference | "Reference" | "01JQXK6V9XCSV5K9Z1MQSK5RBT" | Valid ULID; target thogit must exist |
| Select | {"type":"Select","variants":[...]} | {"variant":"Name"} | Variant name must match a defined variant |
| MultiSelect | {"type":"MultiSelect","variants":["A","B"]} | ["A","B"] | All items must be valid variant names; no duplicates |
String
Section titled “String”The simplest type. Accepts any JSON string value.
| Property | Detail |
|---|---|
| Schema definition | "String" |
| Valid values | Any JSON string: "hello", "", "a long paragraph..." |
| Invalid values | 42, true, ["a"] |
// Schema{ "title": "String" }
// Value{ "title": "My First Thogit" }Number
Section titled “Number”Accepts any JSON number. Stored as a 64-bit float (f64). Integers and decimals are both valid.
| Property | Detail |
|---|---|
| Schema definition | "Number" |
| Valid values | 42, 3.14, -100, 0, 1e10 |
| Invalid values | "42" (string), true, null counts as missing |
// Schema{ "priority": "Number" }
// Value{ "priority": 3 }Boolean
Section titled “Boolean”Accepts JSON true or false only. The strings "true" and "false" are not valid.
| Property | Detail |
|---|---|
| Schema definition | "Boolean" |
| Valid values | true, false |
| Invalid values | "true", 1, 0, "yes" |
// Schema{ "archived": "Boolean" }
// Value{ "archived": false }Accepts date strings in two formats. Time-of-day is optional.
| Property | Detail |
|---|---|
| Schema definition | "Date" |
| Format (date only) | YYYY-MM-DD — e.g. "2024-01-15" |
| Format (datetime) | YYYY-MM-DDTHH:MM:SS — e.g. "2024-01-15T10:30:00" |
| Invalid values | "Jan 15 2024", "2024/01/15", 1705276200 (epoch) |
// Schema{ "due_date": "Date" }
// Value{ "due_date": "2024-06-30" }Reference
Section titled “Reference”A pointer to another thogit, stored as a ULID string. The referenced thogit must exist at validation time.
| Property | Detail |
|---|---|
| Schema definition | "Reference" |
| Valid values | A ULID string like "01JQXK6V9XCSV5K9Z1MQSK5RBT" |
| Validation | ULID format check and existence check against thogit lookup context |
| Invalid values | "not-a-ulid", a ULID pointing to a deleted/non-existent thogit |
// Schema{ "parent": "Reference" }
// Value{ "parent": "01JQXK6V9XCSV5K9Z1MQSK5RBT" }Select
Section titled “Select”A single-choice field from a list of named variants. Variants can optionally define sub-fields — additional typed fields that become active when that variant is selected.
| Property | Detail |
|---|---|
| Schema definition | {"type":"Select","variants":["A",{"name":"B","fields":{...}}]} |
| Value format | {"variant":"VariantName"} |
| Validation | variant must match one of the defined variant names |
Variant structure
Section titled “Variant structure”Each variant in the variants array is an object:
| Key | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The variant identifier |
fields | object | No | Map of sub-field names to schema types |
Sub-field storage
Section titled “Sub-field storage”Sub-field values are stored flat in the parent field_values map alongside the select value itself. They are not nested inside the variant object.
{ "status": { "type": "Select", "variants": [ "Open", { "name": "InProgress", "fields": { "assignee": "String" } }, { "name": "Closed", "fields": { "resolution": "String", "closed_at": "Date" } } ] }}{ "status": { "variant": "Open" }}{ "status": { "variant": "Closed" }, "resolution": "Fixed in v2.1", "closed_at": "2024-03-15"}Sub-field values (resolution, closed_at) sit at the same level as status in field_values, not inside the variant object.
MultiSelect
Section titled “MultiSelect”A multi-choice field from a list of string variants. Unlike Select, MultiSelect variants are plain strings without sub-fields.
| Property | Detail |
|---|---|
| Schema definition | {"type":"MultiSelect","variants":["Low","Medium","High"]} |
| Value format | JSON array of strings: ["Low","High"] |
| Validation | Every item must be a defined variant name; no duplicates allowed |
| Empty selection | [] is valid |
// Schema{ "labels": { "type": "MultiSelect", "variants": ["Bug", "Feature", "Docs", "Urgent", "Breaking"] }}
// Value{ "labels": ["Bug", "Urgent"] }Nullability
Section titled “Nullability”All fields are nullable by default. There is no required modifier. A field is considered null when:
| Condition | Valid? |
|---|---|
Key is missing from field_values | Yes (treated as null) |
Key is present with value null | Yes (explicitly null) |
| Key is present with wrong type | No — validation error |
You can filter for null/non-null fields using the exists and is_null operators. See the Filter Operators reference.
Complete Example
Section titled “Complete Example”A tag using all 7 field types, and a thogit with values for each.
{ "name": "Project Tracker", "schema": { "title": "String", "story_points": "Number", "is_epic": "Boolean", "due_date": "Date", "parent_project": "Reference", "status": { "type": "Select", "variants": [ "Backlog", { "name": "Active", "fields": { "started_at": "Date" } }, { "name": "Done", "fields": { "completed_at": "Date" } } ] }, "labels": { "type": "MultiSelect", "variants": ["Frontend", "Backend", "Infra", "Design", "Docs"] } }}{ "name": "Implement schema validation", "description": "<p>Add runtime validation for all 7 field types.</p>", "tags": ["01JQXK6V9XCSV5K9Z1MQSK5RBT"], "field_values": { "title": "Schema Validation Sprint", "story_points": 8, "is_epic": false, "due_date": "2024-07-01", "parent_project": "01JR2M8K3XNWV7P4Q6YT0HABCD", "status": { "variant": "Active" }, "started_at": "2024-06-15", "labels": ["Backend", "Infra"] }}Note how started_at (a sub-field of the “Active” variant) is stored flat in field_values, not nested inside the status object.