Use this file to discover all available pages before exploring further.
The Propal components DSL is a typed JSON grammar that produces valid
TipTap editor content without exposing the raw editor JSON. Agents author
sections by composing typed blocks; the server validates against Zod schemas,
applies structural rules, and assembles the final document atomically.
A proposal is a suite of sections. Each visual block of the document
(intro, goals, pricing, testimonials…) is wrapped in a section. Anything
outside a section uses page-level styling.Structural rules (enforced by Zod + a runtime validator with explicit
error messages):
The root of any tree submitted via the DSL is a section or section[].
A section cannot contain another section — flat hierarchy at the root.
Inside a section, only blocks are allowed: paragraph, heading, columns,
cards, mini_table, gallery, etc.
columns.cols must equal columns.length.
image / icon / video are placeholders — no src accepted.
embed is the only block that takes an external URL (with provider-host
allowlist).
Card-grouped components (testimonial_cards, process_cards, …) always
assemble to a columns block with one card per column. Proprietary card
containers are never produced.
For each card type the DSL exposes both a standalone form and a
grouped form. Grouped cards always assemble to a columns block with one
card per column — proprietary containers (testimonialsContainer,
accordionContainer, etc.) are deprecated and never produced.
Three accepted forms for section.section_style, in order of preference:
'default' — “Page” : the section inherits page-level colors. No
section ambience.
Named slot id (recommended) — e.g. 'soft', 'deep'. Resolved
server-side via theme.section_styles[].id. Survives reordering of the
theme’s slots.
Positional index — '1', '2', '3', … 1-indexed reference into the
theme’s section_styles[] array. In stock themes: '1' = Default,
'2' = Highlight, '3' = Contrast.
The legacy section.theme (white / gray / black / outlined) is
inert when a custom theme is applied to the proposal — it only controls
the legacy bodyTheme fallback on themeless proposals. Prefer section_style.See theming for the full picture.
The server validates the tree, resolves named refs (here "highlight" if a
matching slot id exists), assembles the TipTap nodes, and atomically appends
them with optimistic concurrency control.
Reading a proposal via get_proposal_tree returns the same DSL shape you
wrote — assembler defaults are stripped on read so the round-trip is a
fixed point. Caveats:
Node types not in the DSL come back as
{ type: 'unknown', tiptap_type, text_preview, child_count }.
Old proposals containing legacy *Container nodes are flattened to the
grouped DSL form (testimonial_cards, questions, …) on read only —
they are never written back in that form.
Headings with level > 3 come back as unknown (the DSL only supports 1-3).