Skip to content

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.