Templates and the form schema
A template is a schema, not a document. Understanding it is most of understanding DocDealer.
A template is not a Word file with holes in it. It is a schema with three parts, and the same schema drives the UI form, the AI generation and the API surface — which is why any client produces the same validated document.
The three parts
Prop
Type
Objects and fields
formSchema holds objects and fields. An object is a repeating group — "the parties to this
deed", "the properties being transferred" — and a field is a single value inside it.
That structure is exactly why formData keys look the way they do:
{
"comparecientes._count": 2,
"comparecientes.0.nombre": "Ana Torres",
"comparecientes.1.nombre": "Luis Gil"
}objectKey._count says how many entries the repeating group has; objectKey.index.fieldKey
addresses one value inside one entry. There is no nesting beyond that — the shape is deliberately
flat so it survives a JSON round-trip through any client.
Read the real keys rather than guessing: GET /api/templates/{templateId} returns formSchema
with objects and fields, and each field's fieldKey (falling back to its id) is what goes
in the key.
Field types
| Type | Value shape |
|---|---|
string | Text. |
number | Numeric. |
date | ISO date string. |
checkbox | Boolean. |
selector | One of a declared set of allowed values. |
file_attachment | An object wrapper — an uploaded file, optionally with an AI analysis of it. |
geoaddress | An object wrapper — a structured address with coordinates. |
connector_tool | An object wrapper — a value fetched from an organization connector. |
The first five are scalars: pass the value directly. The last three are object wrappers, because one field holds several derived values at once — an analysed ID document yields a name, a number and a date, and a geocoded address yields road, house number, postcode, city and coordinates.
Sections and example content
The template's content is divided into sections, and each section carries example content: real
prose from real documents of that kind. Generation shows the model the examples plus the relevant
form values and asks for one section. It is imitation against a known shape, not open-ended writing —
which is what makes it fast and cheap. See Generation.
A section can also be marked to skip AI entirely and emit its example content verbatim when
conditions hold. That is a <when result="static"> inside the case, covered in
Rules.
Response language
Every AI prompt appends a "respond in X" directive taken from the template, never from the
reader's UI language. The chain is template.locale → the organization's default locale → English.
So a Spanish template generates Spanish prose for an English-speaking operator, which is almost
always what you want for a legal document.
The authoring format: a catalogue of examples
Everything above describes the schema. This is what it looks like on disk.
A template serializes to one .html file: YAML frontmatter carrying the metadata and the
form schema, then the HTML body carrying the sections — each with its own <instructions>,
its <when> rules, an optional <default> and its <case> reference examples. There is no
separate rules block: a rule is a child of the thing it describes.
Each snippet below is a literal extract from a template that parses and validates — a test asserts both, so an example here cannot document syntax the validator would reject.
47 examples. Every snippet is cut verbatim from a template that parses and validates, so it is safe to copy. Agents get the same catalogue over MCP from get_template_examples.
Frontmatter
The YAML head of the file. Only these keys survive an export → import cycle.
Template metadata
frontmatter-metadatafrontmatterTitle, description, tags and the four instruction fields.
title: Contrato de arrendamiento
description: Plantilla mínima que muestra la forma del fichero y los campos escalares.
tags:
- Ejemplo
- Arrendamiento
validateDocumentInstructions: Comprueba que la renta mensual y la fianza son coherentes entre sí.
generateSectionInstructions: Usa un registro formal. Nunca inventes datos que no estén en el formulario.
titleGenerationInstructions: Usa el nombre del arrendatario y la fecha del contrato.
agentInstructions: |-
Pregunta primero por el inmueble y después por cada parte. Si el arrendatario
es una sociedad, pide también el CIF.
form:- These are the ONLY metadata keys that round-trip. `icon`, `formatConfig` and `locale` are not written to the file — setting them here does nothing and they are lost on the next export.
- The three `*Instructions` fields are appended to the server's own AI prompts. `agentInstructions` is different: it is handed to the external agent interviewing the user and never enters a prompt.
agentInstructions
frontmatter-agent-instructionsfrontmatterGuidance for the MCP agent gathering data from the user.
agentInstructions: |-
Pregunta primero por el inmueble y después por cada parte. Si el arrendatario
es una sociedad, pide también el CIF.
form:- Returned by `get_document_generation_skill({ templateId })`, so an agent reads it before it starts asking questions.
- Write it as instructions to a colleague taking the client call — what to ask first, what to double-check — not as instructions to a writing model.
Form objects
One object per entity. If it repeats, it is an object.
Singleton object
object-singletonfrontmatterDocument-wide data: one instance, always.
- id: obj_contrato
label: DATOS DEL CONTRATO
objectKey: contrato
description: Datos que valen para todo el documento.
minimumCount: 1
maximumCount: 1- `maximumCount: 1` is what makes it a singleton. Use one for data that applies to the whole document.
Repeatable object
object-repeatablefrontmatterAn entity the user can add any number of: parties, properties, line items.
- id: obj_parte
label: PARTES
objectKey: parte
description: Cada interviniente en el contrato.
minimumCount: 1
maximumCount: null- `maximumCount: null` means unlimited. Its form data is keyed `parte.0.nombre`, `parte.1.nombre`, … with a `parte._count`.
- If it repeats, it is an object — never a field holding a list.
Object validation rule
object-validation-rulefrontmatterA prose rule checked by the AI across every entry of an object.
validationRules:
- id: val_vendedor_nif
rule: Dos vendedores no pueden compartir el mismo NIF.- Written inline on the object. The parser injects `targetObjectId` from its position — never write that key yourself.
- Evaluated by AI, not by a regex, which is what lets it compare entries to each other.
Form fields
The eight input types and their per-type properties.
string field
field-stringfrontmatterFree text, single line or multiline.
- id: field_observaciones
label: Observaciones
type: string
fieldKey: observaciones
objectId: obj_parte
required: false
description: Notas libres sobre esta parte.
multiline: true
rows: 4- `multiline: true` renders a textarea; `rows` sizes it. Also takes `defaultValue`, `sampleValue`, `placeholder`, `maxLength`.
number field
field-numberfrontmatterA numeric value with optional bounds and a unit.
- id: field_renta
label: Renta mensual
type: number
fieldKey: renta
objectId: obj_contrato
required: true
description: Importe en euros.
min: 0
max: 100000
units: €- `units` is display only — the stored value is the bare number.
date field
field-datefrontmatterAn ISO date, optionally defaulting to today.
- id: field_fecha
label: Fecha del contrato
type: date
fieldKey: fecha
objectId: obj_contrato
required: true
description: Fecha de firma.
defaultValue: today- `defaultValue: today` resolves at document creation. Also takes `minDate` / `maxDate`.
selector field
field-selectorfrontmatterA closed set of allowed values.
- id: field_duracion
label: Duración
type: selector
fieldKey: duracion
objectId: obj_contrato
required: true
description: Plazo pactado.
options:
- Un año
- Tres años
- Cinco años- A condition testing this field must use one of the declared options — the validator checks it.
- Add `multiple: true` for a multi-select; the value is then an array.
checkbox field
field-checkboxfrontmatterA boolean, with an optional default.
- id: field_prorroga
label: Prórroga automática
type: checkbox
fieldKey: prorroga
objectId: obj_contrato
required: false
description: Marque si el contrato se prorroga sin preaviso.
defaultValue: true- In a body condition the value is the string `"true"` / `"false"`, e.g. `op="is" value="false"`.
file_attachment field
field-file-attachmentfrontmatterAn uploaded file the AI extracts structured data from.
- id: field_dni
label: DNI del poderdante
type: file_attachment
fieldKey: dni
objectId: obj_poderdante
required: true
description: Adjunte anverso y reverso.
kind: DNI
multiple: true
acceptedTypes: application/pdf,image/*
analysisInstructions: |-
Es un DNI español. Extrae número, nombre, apellidos, fecha de nacimiento
y domicilio.
needAcceptance: true
schema:
type: object
properties:
numero:
type: string
nombre:
type: string
fecha_nacimiento:
type: string
format: date
required:
- numero
- nombre- `analysisInstructions` tells the analyzer what to pull out; `schema` (JSON Schema) constrains the result and is validated on save.
- `needAcceptance: true` makes a human confirm the extraction before it counts as filled.
- Never set a `file_attachment` value yourself — upload with `create_attachment_upload_url`, then `analyze_attachments`.
- `allowedTools` is obsolete and ignored: attachment analysis calls no tools. To derive a value from an address, put `geocode_search` in the consuming section's `tools`.
geoaddress field
field-geoaddressfrontmatterAn address, stored as a structured object with coordinates.
- id: field_domicilio
label: Domicilio
type: geoaddress
fieldKey: domicilio
objectId: obj_poderdante
required: true
description: Dirección completa; se geocodifica al guardarla.
placeholder: Calle, número, población- Pass the object returned by `geocode_address`, not a plain string.
connector_tool field
field-connector-toolfrontmatterA value fetched from one of the organization's connectors.
- id: field_titularidad
label: Titularidad registral
type: connector_tool
fieldKey: titularidad
objectId: obj_inmueble
required: false
description: Se consulta en el registro conectado a la organización.
connectorId: 7c2c1c2e-1f4a-4a1b-9f3e-0c9a1d2b3c4d
toolName: buscar_finca
multiple: false- `connectorId` and `toolName` are set once by the author. When the caller supplies the live connector list, the validator checks the reference still resolves.
- Distinct from a section `tools` entry: this fetches a FORM value, that lets a section call a tool while writing.
Field and object rules
Inline visibility and validation, written on the field or object they govern.
show_if visibility rule
field-visibility-show-iffrontmatterShow a field only when a condition holds.
visibilityRules:
- id: vr_iban
mode: show_if
effect: hide
logicOperator: and
conditions:
- fieldId: field_forma_pago
operator: equals
value: Transferencia- Inline on the field. The parser injects `targetFieldId` from its position — do not write it.
- `required` is only enforced while the field is visible.
hide_if rule with effect: disable
field-visibility-hide-iffrontmatterGrey a field out rather than removing it, when any of several conditions holds.
visibilityRules:
- id: vr_vencimiento
mode: hide_if
effect: disable
logicOperator: or
conditions:
- fieldId: field_forma_pago
operator: not_equals
value: Aplazado
- fieldId: field_precio
operator: is_empty- `effect: disable` keeps the field visible but uneditable — prefer it when the reader benefits from knowing the field exists.
- `logicOperator: or` means any condition suffices; `and` requires all. There is no nesting.
always rule
field-visibility-alwaysfrontmatterUnconditionally hide a field — an internal note the client never sees.
visibilityRules:
- id: vr_nota_interna
mode: always
effect: hide
logicOperator: and
conditions: []- With `mode: always` the conditions are not evaluated — but the `conditions` key and `logicOperator` must still be present. An `always` rule with them missing is a validation error.
Field validation rule
field-validation-rulefrontmatterA prose constraint on one field, checked by AI.
validationRules:
- id: val_iban
rule: Debe ser un IBAN español válido de 24 caracteres.Form-level validation rule
form-validation-rulefrontmatterA rule about the form as a whole, targeting no single field.
validationRules:
- id: val_form_global
rule: El precio total debe ser coherente con el número de fincas transmitidas.- Sits on `form`, not on a field or object — that is what makes it form-level.
- The validator emits an informational warning ("no target (form-level rule)") for these. That is expected, not a defect in your template.
Sections
The unit of generation. `uses` is the load-bearing attribute.
uses
section-usesbodyDeclares which form objects a section's generation can see.
<section id="partes" name="PARTES" uses="obj_parte,obj_contrato">
<case id="partes_general">
<description>
Cuando interviene una persona física.
</description>
<p><strong>{nombre | ANA TORRES GIL}</strong>, mayor de edad, con domicilio en Barcelona.</p>
</case>
</section>- Forgetting `uses` is the single most common template bug: the AI simply never receives that data, and nothing errors at generation time. Once any section declares it, a section without it is a validation error.
- Comma-separated object ids, no spaces.
<default>
section-defaultbodyThe text a section starts with, before anything is generated.
<section id="diligencia" name="DILIGENCIA DE CIERRE" uses="obj_acta" editable="false">
<default>
<p>Y yo, el Notario, doy fe de cuanto antecede.</p>
</default>
<case id="diligencia_general">
<description>
Fórmula de cierre.
</description>
<p>Y yo, el Notario, doy fe de cuanto antecede y de haber identificado a los comparecientes.</p>
</case>
</section>- At most one per section, and it must come first inside the `<section>` — before any `<case>`.
- This section also shows `editable="false"`, which locks the generated text in the editor.
- This is the ONLY thing that puts text in a section on document creation. `<case>` content never does — a case is reference material.
- It takes no attributes: there is one per section and nothing can reference it.
Section visibility
section-when-hidebodyDrop a whole section when the form says it does not apply.
<section id="clausula_no_residente" name="CLÁUSULA DE NO RESIDENTE" uses="obj_vendedor">
<when result="hide">
<condition field="field_no_residente" op="is" value="false"></condition>
</when>- `result` is `hide` | `disable` | `optional` | `mandatory`. A section may carry several `<when>`s; the most restrictive match wins.
- `result="static"` is NOT legal here — that belongs on a `<case>`. The validator rejects it.
- Never write "skip this section if…" in `<instructions>`: the model does not decide visibility, this does.
optional and mandatory results
section-when-optionalbodyMake a section skippable, or required, depending on the form.
<section id="advertencia_aplazado" name="ADVERTENCIA DE APLAZAMIENTO" uses="obj_operacion">
<when result="optional">
<condition field="field_forma_pago" op="is-not" value="Aplazado"></condition>
</when>
<when result="mandatory">
<condition field="field_vencimiento" op="not-empty"></condition>
</when>
<case- `optional` may be generated and is skipped without complaint; `mandatory` must be generated. Set `visibility` on the section to change what happens when NO rule matches.
disable result, and the empty operator
section-when-disablebodyKeep a section in the document but do not generate it.
<section id="nota_registral" name="NOTA REGISTRAL" uses="obj_finca,obj_operacion">
<when result="disable">
<condition field="field_referencia_catastral" op="empty"></condition>
</when>- `disable` leaves the section present but ungenerated — use it when the reader should see that the section exists and does not apply.
- `empty` / `not-empty` take no `value`.
Hiding a section on entry count
section-count-visibilitybodyDrop a section that only makes sense with more than one entry.
<section id="pluralidad_fincas" name="PLURALIDAD DE FINCAS" uses="obj_finca">
<instructions>
Enumera las fincas por su referencia catastral.
</instructions>
<when result="hide">
<condition object="obj_finca" op="lte" value="1"></condition>
</when>- Count operators need `object=`, never `field=`. A count threshold is a number, written as an attribute string.
visibility, temperature, editable
section-attributesbodyPer-section generation settings.
<section id="valoracion" name="VALORACIÓN" uses="obj_acta" visibility="optional" temperature="0.2" tools="connector:7c2c1c2e-1f4a-4a1b-9f3e-0c9a1d2b3c4d:precio_referencia">- `visibility` is the fallback when no `<when>` matches — `mandatory` (the default), `optional`, `disable` or `hide`.
- `editable="false"` locks the generated text in the editor. `model` and `temperature` override the generation defaults.
Cases
Reference variants the AI picks between, or emits verbatim.
<description>
case-descriptionbodyProse that tells the AI when to pick this variant.
<case id="partes_general">
<description>
Cuando interviene una persona física.
</description>
<p><strong>{nombre | ANA TORRES GIL}</strong>, mayor de edad, con domicilio en Barcelona.</p>
</case>- A case with only a `<description>` is AI-selected and AI-rewritten — the content is reference material, not literal output.
- `<description>` and `<instructions>` are ELEMENTS, not attributes, and hold no formatting marks.
- A `<case>` takes exactly one attribute, `id`.
<when result="static">
case-staticbodyEmit this case verbatim, with no AI rewrite, when the condition holds.
<case id="precio_transferencia">
<description>
Pago mediante transferencia bancaria.
</description>
<when result="static">
<condition field="field_forma_pago" op="is" value="Transferencia"></condition>
</when>
<p>El precio se abona mediante transferencia bancaria a la cuenta designada por la parte vendedora.</p>
</case>- The presence of the `<when>` is the only thing that makes a case static — there is no separate flag.
- Write a `<description>` too, even on a static case. Staticness is decided per DOCUMENT: if no static rule in the section matches, the whole section falls through to AI generation and every case is offered to the model by its description.
- A case holds at most one `<when>`.
match="all"
case-static-match-allbodyRequire every condition instead of any one.
<when result="static" match="all">
<condition field="field_forma_pago" op="is" value="Cheque bancario"></condition>
<condition field="field_precio" op="gt" value="1000"></condition>
</when>- Only the literal `match="all"` means "and". Anything else — including `match="any"` — means "or".
- Do not write `match="any"`: it is the default and is never serialized, so it disappears on the next save.
Conditions
The test grammar shared by every rule in the body.
Field condition
condition-isbodyThe common case: a field equals a value.
<condition field="field_forma_pago" op="is" value="Transferencia">- Operators: `is`, `is-not`, `contains`, `not-contains`, `gt`, `lt`, `empty`, `not-empty`.
- `field` names the field ID, not its label or key.
- Against a `selector`, the value must be one of the declared options.
Object count condition
condition-object-countbodyTest how many entries a repeating object has.
<condition object="obj_vendedor" op="gte" value="3">- Use `object=` rather than `field=`, and the count operators `gte`, `lte`, `eq`.
- The count operators work ONLY with `object=`. Writing `field="x" op="gte"` silently drops the condition — it is not an error, and the rule then behaves as if the condition were not there.
Condition on an analysis result
condition-pathbodyBranch on something the AI extracted from an uploaded file.
<condition field="field_pasaporte" path="nacionalidad" op="is" value="Española">- `path` is a dot path into the `file_attachment` field's analysis result, so document structure can follow the contents of an attachment.
- Ignored unless the field is a `file_attachment`.
The remaining operators
condition-operators-referencebodyOne case per operator that has no other example here.
<section id="operadores" name="OPERADORES RESTANTES" uses="obj_operacion,obj_vendedor,obj_finca">
<instructions>
Sección de referencia que reúne los operadores que no aparecen arriba.
</instructions>
<case id="op_contains">
<description>
El nombre del vendedor contiene una forma societaria.
</description>
<when result="static">
<condition field="field_nombre_vendedor" op="contains" value="S.L."></condition>
</when>
<p>La parte vendedora es una sociedad mercantil debidamente constituida.</p>
</case>
<case id="op_not_contains">
<description>
El nombre del vendedor no contiene forma societaria.
</description>
<when result="static">
<condition field="field_nombre_vendedor" op="not-contains" value="S.L."></condition>
</when>
<p>La parte vendedora interviene en su propio nombre y derecho.</p>
</case>
<case id="op_lt">
<description>
Operaciones de importe reducido.
</description>
<when result="static">
<condition field="field_precio" op="lt" value="60000"></condition>
</when>
<p>La operación no supera el umbral de comunicación reforzada.</p>
</case>
<case id="op_count_gte">
<description>
Tres o más vendedores.
</description>
<when result="static">
<condition object="obj_vendedor" op="gte" value="3"></condition>
</when>
<p>Los vendedores comparecen conjuntamente y por partes iguales.</p>
</case>
<case id="op_count_eq">
<description>
Exactamente un vendedor.
</description>
<when result="static">
<condition object="obj_vendedor" op="eq" value="1"></condition>
</when>
<p>La parte vendedora está formada por un único titular.</p>
</case>
</section>- Substring operators (`contains`, `not-contains`) test text; `gt` / `lt` compare numbers; `gte` / `lte` / `eq` count object entries.
- This section exists to exercise the grammar. In a real template you would not group unrelated variants like this.
Content
What may appear inside a section, and placeholders.
Static text outside a section
content-static-textbodyText that always appears verbatim, with no AI involvement.
<p><strong>CONTRATO DE ARRENDAMIENTO</strong></p>- Plain HTML outside any `<section>`. Do NOT wrap always-present, unconditional text in a section.
- A section exists to let the AI choose between variants, or to be governed by visibility rules. If neither applies, it should not be a section.
{label | sample} placeholder
content-placeholder-authoringbodyMark a dynamic value inside reference content.
<p>En Barcelona, a {fecha | tres de julio de dos mil veinticinco}.</p>- The part after `|` is a realistic sample so the model can see the expected shape. Never let a sample reach the finished document.
- When the value is missing at generation time the model emits a placeholder chip instead.
Placeholder chip
content-placeholder-chipbodyThe node the generator emits for a value it does not have.
<span data-type="placeholder" data-name="BASE IMPONIBLE" data-description="Importe sobre el que se liquida el impuesto" data-field="escritura.base_imponible">BASE IMPONIBLE</span>- `data-field` carries the canonical `objectKey.fieldKey`; filling the chip then writes that form field. Omit it and the value goes to document memory instead.
- `data-description` explains what is missing — it is what the person filling the gap reads.
Headings, lists, tables, page breaks
content-rich-blocksbodyThe block content a section may hold beyond paragraphs.
<h2>Exposición</h2>
<p>Comparece <span data-bookmark="OTORGANTE"><strong>{otorgante | ANA TORRES GIL}</strong></span>, cuyos datos constan al inicio.</p>
<ul>
<li>Primera manifestación.</li>
<li>Segunda manifestación.</li>
</ul>
<ol>
<li>Primer acuerdo.</li>
<li>Segundo acuerdo.</li>
</ol>- Allowed inside a section: paragraph, heading (h1–h4), bullet list, ordered list, blockquote, horizontal rule, table, image, page break.
- Bold is `<strong>`. Separate paragraphs with `<p>`.
Page break
content-page-breakbodyForce the exported DOCX onto a new page.
<div data-page-break="true">
</div>- Never fake one with empty paragraphs — they reflow when the content above changes length.
Bookmarks and field references
Never hardcode a page, folio or cross-reference — these resolve on export.
Bookmark
reference-bookmarkbodyAnchor a fragment so other parts of the document can point at it.
<span data-bookmark="OTORGANTE"><strong>{otorgante | ANA TORRES GIL}</strong></span>- The name is yours to choose; it is what every `data-bookmark-name` refers to.
REF — repeat bookmarked text
reference-refbodyInsert the text of a bookmark instead of retyping it.
<span data-field-ref="true" data-field-type="REF" data-bookmark-name="OTORGANTE"></span>- Resolved on DOCX export. Retyping the text by hand is how two copies drift.
PAGEREF and FOLIOREF
reference-pagerefbodyThe page or folio a bookmark falls on.
<p>Consta en la página <span data-field-ref="true" data-field-type="PAGEREF" data-bookmark-name="PROTOCOLO"></span> y en el folio <span data-field-ref="true" data-field-type="FOLIOREF" data-bookmark-name="PROTOCOLO"></span>.</p>NUMPAGES, NUMFOLIOS and their _TEXT forms
reference-countsbodyTotals for the whole document. These need no bookmark.
<p>La presente escritura consta de <span data-field-ref="true" data-field-type="NUMPAGES"></span> páginas (<span data-field-ref="true" data-field-type="NUMPAGES_TEXT"></span>) y <span data-field-ref="true" data-field-type="NUMFOLIOS"></span> folios (<span data-field-ref="true" data-field-type="NUMFOLIOS_TEXT"></span>).</p>- A `*_TEXT` variant renders the number in words — "tres" rather than "3".
- A phrase like "extendida en tres folios" must use `NUMFOLIOS_TEXT`, never literal text.
PREV_PAGES and PREV_FOLIOS
reference-prevbodyHow many pages or folios precede this point. No bookmark needed.
<p>Extendida en las <span data-field-ref="true" data-field-type="PREV_PAGES"></span> páginas anteriores (<span data-field-ref="true" data-field-type="PREV_PAGES_TEXT"></span>) y en los <span data-field-ref="true" data-field-type="PREV_FOLIOS"></span> folios anteriores (<span data-field-ref="true" data-field-type="PREV_FOLIOS_TEXT"></span>).</p>- A phrase like "los cuatro folios anteriores" must use `PREV_FOLIOS_TEXT`.
REMAINING_PAGES
reference-remainingbodyHow much of the document follows a bookmark.
<p>Restan <span data-field-ref="true" data-field-type="REMAINING_PAGES" data-bookmark-name="PROTOCOLO"></span> páginas (<span data-field-ref="true" data-field-type="REMAINING_PAGES_TEXT" data-bookmark-name="PROTOCOLO"></span>).</p>PAGES_BEFORE and FOLIOS_BEFORE
reference-beforebodyHow much of the document precedes a bookmark.
<p>Antes del marcador hay <span data-field-ref="true" data-field-type="PAGES_BEFORE" data-bookmark-name="OTORGANTE"></span> páginas (<span data-field-ref="true" data-field-type="PAGES_BEFORE_TEXT" data-bookmark-name="OTORGANTE"></span>) y <span data-field-ref="true" data-field-type="FOLIOS_BEFORE" data-bookmark-name="OTORGANTE"></span> folios (<span data-field-ref="true" data-field-type="FOLIOS_BEFORE_TEXT" data-bookmark-name="OTORGANTE"></span>).</p>- Measured to a bookmark, unlike `PREV_*`, which measures to the current position.
PAGES_BETWEEN and FOLIOS_BETWEEN
reference-betweenbodyCounts measured between two bookmarks.
<p>Entre ambos marcadores median <span data-field-ref="true" data-field-type="PAGES_BETWEEN" data-bookmark-name="PROTOCOLO" data-bookmark-name-end="OTORGANTE"></span> páginas (<span data-field-ref="true" data-field-type="PAGES_BETWEEN_TEXT" data-bookmark-name="PROTOCOLO" data-bookmark-name-end="OTORGANTE"></span>) y <span data-field-ref="true" data-field-type="FOLIOS_BETWEEN" data-bookmark-name="PROTOCOLO" data-bookmark-name-end="OTORGANTE"></span> folios (<span data-field-ref="true" data-field-type="FOLIOS_BETWEEN_TEXT" data-bookmark-name="PROTOCOLO" data-bookmark-name-end="OTORGANTE"></span>).</p>- The `*_BETWEEN` types are the only ones that take a second bookmark, `data-bookmark-name-end`.
Section tools
Extra AI tools a section may call while generating.
tools
section-toolsbodyLet a section call an extra AI tool while it generates.
<section id="localizacion" name="LOCALIZACIÓN" uses="obj_acta" tools="geocode_search">- `math` and `update_memory` are always available and must NOT be listed. `geocode_search` is opt-in, which is what this attribute is for.
- An unknown token is a validation error. Call `list_section_tools` to see what this organization can use — connector tokens differ per organization.
- A section reads the document's shared memory but does not write the form: values that map to a field are emitted as placeholders instead.
Connector tool token
section-tools-connectorfrontmatterGive a section access to one tool on an organization connector.
tools="connector:7c2c1c2e-1f4a-4a1b-9f3e-0c9a1d2b3c4d:precio_referencia"- The token is `connector:<connectorId>:<toolName>`. Get real ones from `list_section_tools`.
- A token whose connector has been deleted or disabled is dropped at generation time; the validator warns about it when it can see the organization's connector list.