> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kayanos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Document-template expression context

> Use KayanOS expressions in document templates with the correct record roots, controlled lookups, timestamps, and caller/admin execution behavior.

## Start with the record context

When KayanOS renders a managed document template for an entity record, it supplies:

![KayanOS managed document-template settings with a record-based filename expression and caller-permissions mode.](https://kayanos.app/docs-images/en/reference/expressions-document-template-context.png)

| Root                | Meaning                                                           |
| ------------------- | ----------------------------------------------------------------- |
| `$record`           | Flattened current entity record.                                  |
| `$inverseRelations` | Configured inverse-relation records, grouped by entity and field. |
| `$organization`     | Organization object with `id`.                                    |
| `$member`           | Calling member object with `id`.                                  |
| `$now`              | ISO timestamp captured for this render context.                   |

```jexl theme={null}
`Acknowledgement {{ $record.request_id }} — {{ $record.status || "received" }}`
```

Use `$record`, not the entity-rule `$` root. An interpolation that resolves to `null` or `undefined` becomes blank, so add a reviewed fallback in public or official documents.

## Stable time and formatting

Use `$now` when every part of a document must show the same captured timestamp. `now()` is also available as a function, but it is evaluated by the renderer and can differ slightly between expressions.

```jexl theme={null}
dateFormat($now, "YYYY-MM-DD")
```

Document templates share the general helper catalog, including date, text, list, comparison, project, and planning helpers. They do **not** inherit shared-only legacy aliases or form-only functions. See [Expression language syntax](/reference/expressions/language-syntax) and the individual helper availability notes.

## Controlled record lookups

The template-only `getRecords` function accepts one object argument:

```jexl theme={null}
getRecords({
  entityKey: "service_requests",
  filters: [["status", "eq", "approved"]],
  limit: 20,
  sorting: [{ field: "created", direction: "desc" }]
})
```

Supported object properties are `entityKey`, optional `q`, `filters`, `limit`, `sorting`, and `search`. A `sorting` value is an array of objects, each with `field` and `direction: "asc" | "desc"`; it is not an object keyed by field name. Each call clamps `limit` to a value between 1 and 500. Keep a lookup narrow, select only the data needed for the document, and make an empty result understandable.

```jexl theme={null}
getRecords({
  entityKey: "service_requests",
  filters: [["applicant_id", "eq", $record.applicant_id]],
  limit: 5
})
```

### Caller and admin execution modes

In caller mode, the lookup is evaluated as the calling member. In admin mode, it uses an administrative execution identity. This is a material data-governance decision, not a formatting preference.

An administrative lookup is restricted only when its allowed-entity list is **non-empty**. An omitted or empty list is not deny-all: it permits lookup against every entity in the organization that the administrative execution identity can read. Do not select admin mode until the service owner has reviewed that broader boundary. When admin mode is necessary, add a short, approved non-empty list of entity keys and treat any later change to it as an access change.

Before publishing a template that uses `getRecords`:

1. Choose caller mode unless the service owner has approved a narrower administrative need.
2. In admin mode, configure a **non-empty** allowed-entity list as tightly as possible; do not leave it blank expecting a restriction.
3. Test with a member who should see the least amount of data, then test an entity outside the non-empty list and confirm that lookup is refused.
4. Check the rendered document for unintended personal, financial, or operational data.
5. Keep an empty lookup from producing a misleading statement.

The renderer has a 500-row cap per lookup call, a 10,000 total loop-iteration limit, a 20-level nesting limit, a five-level subtemplate limit, and a 15-second render timeout. Do not assume a separate aggregate lookup-row limit that is not part of the public contract.

## Related data and loops

Configured inverse relations are suitable for a small related-record section. A document-template loop belongs in the document tag syntax, while its expression supplies the collection. The renderer currently exposes `$relations` as an empty reserved object, so do not use it as a direct-relation data contract. Put a directly needed value on the record where appropriate, use a configured inverse relation, or use a reviewed `getRecords` lookup.

```jexl theme={null}
$inverseRelations.inspections.request_id
```

Use a relation only after confirming the entity/field grouping that the template context exposes. An unconfigured inverse relation is not a reliable signal that no related records exist.

## Filename expressions

Filename expressions use the same document-template expression context. Keep filenames stable, readable, and free of sensitive values that do not need to appear in a download name.

```jexl theme={null}
$record.request_id + "-acknowledgement"
```

The output type appends the document extension as required. Do not embed a path, access token, national identifier, or free-form private note in a filename.

## Test a template as a record, not a paragraph

| Test                       | Expected outcome                                                                                      |
| -------------------------- | ----------------------------------------------------------------------------------------------------- |
| Complete record            | Every intended tag resolves with the correct format.                                                  |
| Missing optional value     | A reviewed fallback or an intentional blank is shown.                                                 |
| Empty relation/list        | The document remains truthful and readable.                                                           |
| Caller mode                | The template does not expose data beyond that member’s service purpose.                               |
| Admin mode—restricted list | A lookup for an entity outside the non-empty approved list is refused.                                |
| Admin mode—empty list      | The reviewer recognizes that an empty/missing list is broad administrative access, not a restriction. |
| Large but valid data       | Loop, nesting, output size, and timeout limits are respected.                                         |

## Related guides

* [Document templates](/build/document-templates)
* [Document-template tags and rendering](/build/document-template-tags-and-rendering)
* [Document-template lookup helper](/reference/expressions/helpers/document-templates)
* [Expression contexts and availability](/reference/expressions/contexts)
