Nested notes
Declare a hierarchical notes object, write sub-notes from the editor, move one between trees and share a whole tree from its root.
A note often grows into several. A hierarchical object lets a note sit under another note of the same object, so a meeting note keeps its follow-ups, and a project note keeps its meetings. This cookbook declares the object, writes sub-notes from the editor and from the API, moves one into another tree, and shares a whole tree by sharing its first note.
What you will build
A notes object whose records form trees, a tree of three notes, and a share that gives a colleague the whole tree at once.
Declare the object
.hierarchical() is the only difference from a flat object. Sync the bundle as usual.
import { object, richtext, text } from "@standards/client";
export const notes = object("notes", "Note")
.labelExpression("{{ title }}")
.attribute(text("title").required())
.attribute(richtext("body"))
.hierarchical();A runtime object takes it too: schemaObjects.create with hierarchical: true, or the Architect agent in the same draft that creates the object. Already have one? Turn the option on from the app's schema settings, from schemaObjects.update with hierarchical: true, or by asking the Architect agent to update the object. The records you already have become top-level records of the tree.
Write sub-notes from the editor
Open a note. Its page shows a breadcrumb of its ancestors, and its details carry two relation attributes, Parent and Sous-notes (parentId and childIds; the second is named after the object, Sub-notes in English), placed by the view like any other field; a record view that places neither still shows both. Picking a note in either moves it, removing one detaches it, and Ajouter in Sous-notes creates a sub-note. In the editor:
/sous-notecreates an empty sub-note under the note you are writing in, inserts its chip where the cursor is, and opens the new sub-note.- Type
@and a title: next to the notes it finds, + Créer “…” comme sous-note creates the note under the open one and opens it once its chip is in the text. The@menu stops at the first space; for a title with several words, use/sous-noteand type the title there.
The table and list views of the object show the top level; a chevron marks a row that has sub-notes and opens them.
Write sub-notes from the API
A parent is set when the record is created, with options.parentId. The ancestorIds of the answer is the path from the top-level note.
curl https://api.standards.new/v1/records/notes \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "title": "Kick-off" }, "options": { "parentId": "<launch-plan-id>" } }'update, the bulk routes and imports refuse a parent: a parent changes only through a move. List the top level and a note's sub-notes with the parentId filter:
{ "filters": [{ "attribute": "parentId", "operator": "is_empty", "value": null }] }{ "filters": [{ "attribute": "parentId", "operator": "is", "value": "<launch-plan-id>" }] }Move a note
Drag a row onto another in the list, or pick a new parent in the note's Parent field. Moving takes the note and its sub-notes together. Through the API, preview the access change, then confirm with what you were shown:
curl https://api.standards.new/v1/records/notes/<note-id>/move-impact \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "parentId": "<other-project-id>" }'curl https://api.standards.new/v1/records/notes/<note-id>/move \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "parentId": "<other-project-id>", "expected": <the impact answered above> }'parentId: null detaches the note to the top level. If someone changed the sharing in between, the move is refused with RECORD_MOVE_IMPACT_CHANGED and the response carries the new impact to show again. Moving needs own on the note's current top-level note and write on the new parent.
Share the tree
Make the first note of the tree private, then share it. Shares live on that note, and its sub-notes follow, as Sharing describes. Open a sub-note's share panel and it reads « Accès hérité de “Launch plan” » and sends you to the root.
import { getStandards } from "./client";
const notes = getStandards().from("notes");
await notes.grantAccess(launchPlanId, colleagueActorId, "editor");A note moved into a tree takes that tree's audience, and the move dialog says so when more people can read it afterwards. A note detached from a tree keeps the audience it had.
What you cannot do
- Give a note a parent from another object, or a parent more than 32 levels deep:
RECORD_PARENT_INVALID. - Share or change the visibility of a sub-note on its own:
RECORD_HIERARCHY_WRITE. - Turn
hierarchicaloff while a note has a parent:RECORD_HIERARCHY_OFF_REFUSED, with the count. - Delete a note and keep its sub-notes in place: they go to the trash with it, after a confirmation that asks for the note's name. Restoring the note brings them back.
An agent files a note with organize (file_under, parent: null to detach) and creates a sub-note with write and parent. A move that would let more people read the tree always asks you first: the card names the new parent and who gains the tree, in the move confirmation's words.
Next steps
- Objects: the builder method, the tree fields and the routes.
- Sharing: inherited access, moves and detaching.
- HTTP API / Errors: the codes above.
- Records: the typed query, and
grantAccessused above. - Book a demo
Vibecoding on Standards
One TypeScript file becomes a back office, a typed API and a tool your agent can query. Follow the model catalogue every Standards deployment reads at boot, from schema to production.
Set up the client
Install @standards/client, create one server-side client, and know what it covers before you write your first object.