Jinja2 Templating
Secretary uses Jinja2 for dynamic values in action value fields, match replace strings, note messages, and template conditions.
Rendering context
Every rule execution provides the following built-in context:
Lookup functions
These functions resolve Paperless-ngx entity names to their internal IDs:
| Function | Description |
|---|---|
correspondent(name) |
Resolve a correspondent name → Paperless ID |
document_type(name) |
Resolve a document type name → Paperless ID |
storage_path(name) |
Resolve a storage path name → Paperless ID |
tag(name) |
Resolve a tag name → Paperless ID |
custom_field(name) |
Resolve a custom field name → Paperless ID |
Auto-wrapping
Plain string values for classifier fields (correspondent, document_type, storage_path) and tag actions are automatically wrapped in the appropriate lookup function. You don't need to write {{ correspondent('ACME GmbH') }} manually — just write "ACME GmbH".
Scope variables
Variables defined by actions (match, set_variable, lookup) are available in subsequent action templates within the same rule execution:
actions:
- match: '(\d{4})/(\d{2})'
field: title
replace: "{{ m1 }}"
as: year
- set_field: title
value: "Archive {{ year }}" # uses the 'year' variable from match
Capture groups in match
The match action exposes regex capture groups as m1, m2, … in the replace template:
- match: 'Invoice No\.\s+(\w+)\s+dated\s+(\d{4})-(\d{2})'
field: content
replace: "{{ m1 }} ({{ m2 }}/{{ m3 }})"
as: invoice_ref
If the pattern does not match, the as variable is left undefined. Use exists: in conditions or is defined in templates to guard against this:
- if: "{{ invoice_ref is defined }}"
actions:
- set_field: title
value: "{{ invoice_ref }}"
else:
- add_tag: "Problem"
Template syntax
Any string value that contains {{, {%, or {# is treated as a Jinja2 template. Plain strings are used as-is (except classifier auto-wrapping described above).
# Plain string — used literally
- set_field: title
value: "My Document"
# Jinja2 template — rendered at execution time
- set_field: title
value: "{{ year }}/{{ month }} {{ correspondent_name }}"
The _vars.yml file
A file named exactly _vars.yml placed in the rules.d directory is treated as a shared Jinja2 template rendered before each rule file in that directory. YAML variables defined in it are available in all sibling rule files.
This is useful for shared constants, lookup tables, and computed mappings.
Example
# rules.d/_vars.yml
---
ERROR_TAG: "Problem"
NO_TITLE_MSG: "The title could not be determined automatically."
{% set _months = {
"01": ["January", "Jan"],
"02": ["February", "Feb"],
"03": ["March", "Mar"],
} %}
month_name_to_num:
{% for k, names in _months.items() %}
{% for name in names %}
"{{ name }}": "{{ k }}"
{% endfor %}
{% endfor %}
In a rule file in the same directory:
# rules.d/my-rule.yml
---
- id: example
triggers: [all]
conditions:
- not:
- field: tags
contains: "{{ ERROR_TAG }}"
actions:
- note: "{{ NO_TITLE_MSG }}"
- save
Scope
_vars.yml variables are scoped to the directory they reside in. Sub-directories have their own _vars.yml (optional).
Previewing _vars.yml
The Web UI has a built-in preview for _vars.yml files — click the Preview button in the editor to see the rendered YAML output.