Custom Functions
A Custom Function is a reusable, versioned snippet of JSONata logic — a composable primitive for business logic. Define it once and call it from any expression in your templates or blueprints — valueExpression, displayCondition, data.transform, or onComplete mappers.
Functions let you encapsulate complex business logic (currency formatting, tax calculations, date math) in a single place. When the logic changes, update the function — not every expression that uses it. Like all SignStack primitives, functions are versioned and authored wherever you prefer — Studio, the CLI as YAML in your repo, or the API.
YAML Format
apiVersion: signstack/v1beta2
kind: JsonataFunction
metadata:
key: format_currency # Required, snake_case
version: 1.0.0 # Required, semver
name: Format Currency # Required
description: Formats a number as USD currency string
labels:
category: formatting
spec:
params: # Optional: function parameters
- name: amount # Valid JS identifier
type: number # string, number, boolean, or object
# schema: address_schema@1.0.0 # Required when type is object
returnType: # Required
type: string
# schema: address_schema@1.0.0 # Required when type is object
body: "'$' & $formatNumber(amount, '#,##0.00')" # Required: the JSONata expression
Spec Fields
| Field | Required | Description |
|---|---|---|
params |
No | Input parameters. Each has name, type, and optional schema (for object types). |
returnType |
Yes | What the function returns. Has type and optional schema. |
body |
Yes | JSONata expression implementing the function. Can call any JSONata built-in ($formatNumber, $round, $now, ...) or another declared SignStack function (see functions below). Max 10,000 chars. Use YAML | for multi-line. |
functions |
No | References to other custom functions called via $<alias>() in the body. |
Parameter Types
| Type | Description |
|---|---|
string |
Text values |
number |
Numeric values |
boolean |
True/false |
array |
Array of values |
object |
Complex object (optionally validated against a schema) |
Examples
Simple String Function
apiVersion: signstack/v1beta2
kind: JsonataFunction
metadata:
key: get_full_name
version: 1.0.0
name: Get Full Name
spec:
params:
- name: firstName
type: string
- name: lastName
type: string
returnType:
type: string
body: "firstName & ' ' & lastName"
Currency Formatter
apiVersion: signstack/v1beta2
kind: JsonataFunction
metadata:
key: format_currency
version: 1.0.0
name: Format Currency
description: Formats a number as USD currency string
spec:
params:
- name: amount
type: number
returnType:
type: string
body: "'$' & $formatNumber(amount, '#,##0.00')"
Math with Rounding
apiVersion: signstack/v1beta2
kind: JsonataFunction
metadata:
key: calculate_tax
version: 1.0.0
name: Calculate Tax
spec:
params:
- name: amount
type: number
- name: taxRate
type: number
returnType:
type: number
body: '$round(amount * taxRate, 2)'
Object Parameter with Schema
apiVersion: signstack/v1beta2
kind: JsonataFunction
metadata:
key: format_address
version: 1.0.0
name: Format Address
description: Formats an address object as a single line string
spec:
params:
- name: address
type: object
schema: address_schema@1.0.0
returnType:
type: string
body: |
$.address.street & ', ' &
$.address.city & ', ' &
$.address.state & ' ' &
$.address.zipCode
Multi-Line with Block Expression
apiVersion: signstack/v1beta2
kind: JsonataFunction
metadata:
key: calculate_probation_end
version: 1.0.0
name: Calculate Probation End Date
spec:
params:
- name: startDate
type: string
- name: durationMonths
type: number
returnType:
type: string
body: |
(
$start := $toMillis(startDate);
$msPerMonth := 30 * 24 * 60 * 60 * 1000;
$endMs := $start + (durationMonths * $msPerMonth);
$fromMillis($endMs, '[Y]-[M01]-[D01]')
)
Composing Custom Functions
A function can call another SignStack function once it's declared in the functions block:
apiVersion: signstack/v1beta2
kind: JsonataFunction
metadata:
key: format_total_compensation
version: 1.0.0
name: Format Total Compensation
spec:
params:
- name: base
type: number
- name: bonus
type: number
returnType:
type: string
functions:
money: format_currency@1.0.0 # Local alias → versioned function reference
body: '$money(base + bonus)'
$money(...) resolves to format_currency@1.0.0 at runtime — same call syntax as JSONata built-ins, just declared first.
Function Calling Another Function
apiVersion: signstack/v1beta2
kind: JsonataFunction
metadata:
key: create_audit_entry
version: 1.0.0
name: Create Audit Entry
spec:
params:
- name: action
type: string
- name: userId
type: string
returnType:
type: object
schema: audit_entry@1.0.0
functions:
get_full_name: get_full_name@1.0.0
body: |
{
"action": action,
"userId": userId,
"timestamp": $now(),
"id": $string($random())
}
Using Functions in Expressions
Register functions in a template or blueprint's functions map, then call them via $<alias>():
# In a template or blueprint
spec:
functions:
format_currency: format_currency@1.0.0
get_full_name: get_full_name@1.0.0
Then in any JSONata expression:
# In a template field value
value:
expression: '$format_currency($.position.salary)'
# In a data transform
data:
transform: |
{
"fullName": $get_full_name($.employee.firstName, $.employee.lastName),
"salary": $format_currency($.position.salary)
}
# In a blueprint onComplete mapper
onComplete:
updates:
- key: employee
transform: |
$merge([
$.inputs.employee,
{
"probationEnd": $calculate_probation_end($.inputs.position.startDate, 3)
}
])
Versioning
Functions use semantic versioning. A function moves from Draft to a concrete published version (1.0.0); to make further changes, you cut a new version (1.1.0, 2.0.0). Published versions are immutable — every template or blueprint pinning format_currency@1.0.0 keeps invoking the exact logic it was designed against, even after 2.0.0 ships.
Draft → 1.0.0 (published, immutable)
↓ edit + publish
1.1.0 (published, immutable)
↓ edit + publish
2.0.0 (published, immutable)
Pick the version bump based on the change:
- MAJOR (2.0.0) — Breaking changes (signature change, return type change, behavioral semantics flipped)
- MINOR (1.1.0) — Backwards-compatible additions (new optional behavior)
- PATCH (1.0.1) — Bug fixes (rounding correction, off-by-one)
Best Practices
- One concern per function — Keep functions focused.
format_currencydoes one thing well. - Use descriptive parameter names —
amountandtaxRateare clearer thanaandr. - Document with descriptions — Helps your team and AI tools generate correct usage.
Related Concepts
- JSONata Expressions Reference — Full JSONata syntax reference
- Templates — Using functions in template expressions
- Blueprints — Using functions in blueprint mappers
