Cookbook
Twelve tasks that appear in almost every process at Musterfirma GmbH, each with the finished expression to copy. Every recipe names the task, the field in the app, the expression and the result. Which language a field expects is explained on Which language where?; the basics of files and dates under Files in Pantarey and Date and time.
Invoice number with counter and year
Task: Assign a consecutive invoice number such as RE-2026-00017.
Where: Document name in the Data Designer (placeholders) or Input expression (JSONata) of the step that writes the invoice.
RE-$fn('str_padStart', $fn('nextId', 'rechnung'), 5, '0')
"RE-" & $substring($now(), 0, 4) & "-" & $fn('str_padStart', $fn('nextId', 'rechnung'), 5, '0')
Result: RE-00017 or RE-2026-00017.
Note: The counter rechnung is created on first use. In the JSONata editor and in the dry run it is only read; only the real run increments it, see Counters.
Sum over line items
Task: Calculate the total of a list of invoice items.
Where: Input expression (JSONata).
$sum(positionen.(menge * preis))
Result: a number, for example 1250.
Rounding net and gross
Task: Round an amount to two decimal places.
Where: Input expression (JSONata) or placeholder field.
$fn('number_round', netto * 1.19, 2)
Result: 1487.5 instead of 1487.4999999.
Note: For invoices with several VAT rates, Generate invoice PDF (with calculation) takes care of totals and rounding; there app_vat_rounding: "per_rate" and app_price_basis: "gross" control the calculation.
Formatting a date the German way
Task: Output 2026-03-15 as 15.03.2026.
Where: Input expression (JSONata), placeholder fields such as Display text or Subject, HTML template.
$fn('date_toGermanDate', antrag_db.von)
$fn('date_toGermanDate', $substring(antrag_db.$$context.executedAt, 0, 10))
{{toGermanDate rechnungsdatum}} – {{dateTimeFormat erstellt "de-DE" "Europe/Berlin"}}
Result: 15.03.2026; with dateTimeFormat 15.03.2026, 10:30:00.
Note: date_toGermanDate expects YYYY-MM-DD; shorten timestamps first with $substring(x, 0, 10). The Handlebars helper toGermanDate shortens by itself.
Renaming a file
Task: Attach or store a generated PDF under a meaningful name.
Where: Input expression (JSONata) of the following step, for example the e-mail.
{
"referenceId": rechnung_pdf.referenceId,
"filename": "Rechnung " & rechnung.nummer & ".pdf",
"contentType": rechnung_pdf.contentType
}
Result: The new name applies to the e-mail attachment, HTTP upload, ZIP and record; the file in the storage stays the same, see Files.
Setting a status in the record
Task: After approval, set the status of a stored request to "approved".
Where: Database element with action Update, field Input expression (JSONata), Discard result enabled. The request is stored under the result path antrag_db of the saving step.
$merge([$sift(antrag_db, function($v, $k) { $k != '$$context' }), { "status": "genehmigt" }])
Result: The whole record is written again with id and data_schema_id; only status changes.
Note: The update replaces the record; therefore take the stored state as the basis, not just the status field. Missing required fields stop the step. $sift removes the carried $$context.
Filtering and grouping a list
Task: Keep only items with a quantity and sum the amounts per category.
Where: Input expression (JSONata).
positionen[menge > 0]
positionen{kategorie: $sum(preis)}
Result: a filtered list or an object such as { "Hardware": 1200, "Schulung": 450 }.
Note: [condition] filters, {key: value} after a list groups by the key; inside the group preis is the list of all prices of that category.
Composing text from several fields
Task: Compose names or address lines without dangling commas for empty fields.
Where: Placeholder fields such as Display text, Business label, Document name; in JSONata without $(…).
$fn('str_joinNonEmpty', ', ', $(nachname), $(vorname))
$fn('str_coalesce', $(rufname), $(vorname), 'Unbekannt')
$fn('str_ifPresent', $(storniert_am), 'STORNIERT', 'aktiv')
Result: Müller, Hans, with an empty first name Müller; str_coalesce returns the first present value, str_ifPresent one of two texts depending on whether a value exists.
Note: 0 and '0' count as present; empty texts, null and missing fields do not.
Using a value from value management
Task: Maintain the company address or bank details centrally and use them in the process.
Where: all fields in the Process Designer, including JSONata and SQL fields; not in form data.
{
"absender": {{value:firma}},
"strasse": {{value:firma.strasse}},
"iban": {{value:global:bank_iban}}
}
Result: Pantarey inserts the stored value before evaluation; outside quotation marks as a JSON value (object, number, text), inside as escaped text.
Note: Names consist of lower-case letters, digits and underscore and begin with a letter. Everything about it under Values in the process.
Condition on the pressed button
Task: At the gateway, choose the path according to the action button of the user task.
Where: Condition on the outgoing sequence flow of the gateway (JavaScript with placeholders). The task has the result path genehmigung, its button the value approve.
$(genehmigung.$$context.selectedAction) === 'approve'
Result: true for the approval path; the second flow is marked as default flow or checks === 'reject'.
Note: With a Mapping path on the button, the value is additionally available in a field of the message, for example $(genehmigung.entscheidung). More under Gateway and conditions.
Processing files from a multi-upload one by one
Task: Analyze every uploaded file separately, for example all receipts of a request.
Where: Loop section of the activity (wrench → Parallel or Sequential Multi Instance), Input expression (JSONata), Result path analysen.
[uploadedFiles.{ "pdf": $ }]
Result: One run of Analyze PDF per file with { "pdf": { … } } as its message; afterwards analysen holds the list of the individual results.
Note: The loop requires a list. The square brackets make sure that a single file also arrives as a list; otherwise the step reports "Loop activity input must be an array". With the start One process for all files the list is called uploadedFiles, with a Multiple field in the form for example formular.belege. Defaults are at most 1000 elements and 40 parallel runs.
Reminder after a time limit
Task: If a task is left for three days, send a reminder and create the task again.
Where: Time limit of the user task (result path pruefung): Mode Duration 3 days, Behaviour on timeout Continue (proceed automatically). After it a gateway with Condition:
$(pruefung.$$context.timedOut) === true
Result: After three days the instance continues, timedOut is true, the reminder path sends the e-mail and leads back to the task. If the task was completed, timedOut is missing and the normal path applies.
Note: If the deadline should come from the data, choose Date from process data with $(rechnung_db.frist); a plain date is valid until 23:59:59 UTC. A fixed pause without a task is the Timer intermediate event with Waiting time (seconds) 259200, see Time, errors and signals.
Next step
Back to the map Which language where?: which field expects JSONata, placeholders, JavaScript, Handlebars or SQL.