My content type won’t create
You’re callingdefine_content_type and getting an error.
| What you tried | Why it failed | Error |
|---|---|---|
Used uppercase, spaces, or underscores in code | Codes must be lowercase alphanumeric with hyphens only: ^[a-z0-9]+(?:-[a-z0-9]+)*$. Examples: nda, service-agreement. | 422 |
| Created a child with the same code as an existing sibling | Codes must be unique among siblings (same parent). | 409 |
| Created a root with a code that already exists | Root codes must be unique per company. | 409 |
Created a path that already exists (e.g. contract:nda) | Full paths must be unique per company. | 409 |
| Added a 5th level to the tree | Trees can be at most 4 levels deep. | 400 |
| Path exceeds 768 characters | Maximum path length is 768 characters. | 400 |
- A company can have multiple independent trees (different roots). They don’t interact.
- Content types are company-scoped, not workspace-scoped.
- You cannot reparent a node. To move it, delete it with
undefine_content_typeand recreate it under the new parent. Deleting cascades: see What happens when I delete something? below.
My attribute won’t save
You’re callingdefine_attribute and getting an error.
| What you tried | Why it failed | Error |
|---|---|---|
Changed an attribute’s type (e.g. text → number) | attribute_type is immutable once created. Delete the attribute and recreate it. | 400 |
| Used a name that already exists on this content type | Attribute names must be unique within a content type. | 409 |
| Used a name that exists elsewhere in the same tree | Names can’t conflict across the tree. This prevents ambiguity when attributes are inherited. | 400 |
Used a reserved name like content_type, status, or page | Some names are reserved by the system. | 400 |
Created a select or multi-select without choices | These types require a non-empty choices array. | 400 |
- If you omit
label, it’s auto-generated fromname:fiscal_yearbecomes"Fiscal Year". - 7 attribute types are available:
text,number,date,boolean,select,multi-select,rich-text.
My classification was rejected
You’re callingclassify on a file and getting an error.
| What you tried | Why it failed | Error |
|---|---|---|
Classified a file that’s already classified in the same tree under a sibling path (e.g. contract:nda → contract:service-agreement) | A file can only have one classification per tree. You can’t jump between siblings. Unclassify first, then classify the new path. | 400 |
Called set_value on a file that isn’t classified yet | You must classify a file before setting any attribute values. | 400 |
Reclassification: what’s allowed
When a file is already classified and you callclassify with a different path in the same tree, the result depends on the relationship:
| You’re moving to… | What happens |
|---|---|
| The same path | Nothing changes. Idempotent, returns 200. |
A descendant (e.g. contract → contract:nda) | Auto-migrates. All attribute values are preserved. |
An ancestor (e.g. contract:nda → contract) | Auto-migrates. Child-specific values (like is_mutual) are deleted because they don’t exist on the ancestor. |
A sibling (e.g. contract:nda → contract:service-agreement) | Rejected. Unclassify first, then classify the new path. |
contract:nda classification and one regulation:gdpr classification).
My value was rejected
You’re callingset_value and getting a 422.
| Attribute type | What’s accepted | What’s rejected |
|---|---|---|
text, rich-text | String | Anything else |
number | Integer, float, or numeric string (50000, "1.5") | Booleans, non-numeric strings |
date | ISO 8601 string, normalized to YYYY-MM-DD | Non-date strings |
boolean | true or false (real booleans) | "true", 1, "yes" |
select | Single string matching a defined choice | String not in choices |
multi-select | List of strings, each matching a defined choice | Single string, values not in choices |
clear_value. Passing null to set_value is rejected with 422.
What happens when I delete something?
Deleting things in Facets cascades. Here’s exactly what gets removed:| You delete… | What’s also removed |
|---|---|
A content type (undefine_content_type) | Its entire subtree, all file classifications referencing those paths, and all their attribute values. |
A classification (unclassify) | All attribute values stored under that classification on that file. |
An attribute (undefine_attribute) | All stored values for that attribute across every file. |
Choices from a select/multi-select | Any stored values that referenced the removed choices are deleted across all files. |
What can I filter on?
Not all attribute types support filtering viaGET /api/v3/files?attribute=.
| Type | Filterable? | Why |
|---|---|---|
text | Yes | Indexed in value_text |
number | Yes | Indexed in value_number |
date | Yes | Indexed in value_date |
boolean | Yes | Indexed in value_boolean |
select | Yes | Indexed in value_text |
multi-select | No | Not indexed. Use select if you need filtering |
rich-text | No | Not indexed. Use text if you need filtering |
Multi-select containment
When filtering on amulti-select attribute, the filter uses containment: jurisdiction:FR matches files with ["FR", "DE"], ["FR"], or any list containing "FR".
Date shortcuts and ambiguity
LightOn expands partial dates into ranges:effective_date:2025→ any date in 2025 (Jan 1 – Dec 31)effective_date:2025-03→ any date in March 2025effective_date:2025-03-01→ exact date
2025 is ambiguous: it could be a year, a number, or text. LightOn searches across all typed columns with an OR. Use full YYYY-MM-DD format for exact date matches.
How does inheritance work?
Attribute inheritance is resolved at query time, not stored. When you read a file’s facets, LightOn walks up the tree and collects attributes from each ancestor withinherit_attributes: true.
| Scenario | What happens |
|---|---|
| You add an attribute to a parent node | Immediately available on all descendants. No migration needed. |
You set inherit_attributes: false on a node | Breaks the chain. That node and its descendants won’t inherit attributes from above. |
| A child defines an attribute with the same name as an inherited one | The child’s definition shadows the parent’s. The child’s type, choices, and required flag take precedence. |
Batch behavior
When you send a JSON array of actions, LightOn processes them in order and fails fast on the first error:- Actions before the failure are committed.
- Actions after the failure are skipped.
- BM25 reindex is scheduled once per batch, not per action.
adopt, define_content_type, define_attribute, undefine_*) are idempotent and safe to replay.