Skip to main content
Version: 2.4

Script-to-Script Bindings

Why you need it — the problem it solves

Large SpEL automations quickly end up copy-pasting common snippets (currency conversion, e-mail templates, date math …). The script-to-script binding lets you refactor that boiler-plate into a single helper script and call it from anywhere with a one-liner:

@script.finance.convert(#amount, 'USD', 'CZK')

Quick start in 60 seconds

StepWhat you doWhere
1Create a helper script
Code = Finance.Convert
Source = #amount * 22.0
Scripts register (Script reference)
2Create a binding row with:
fullName = finance.convert
script = Finance.Convert
Scripts / Bindings / Script Invocations
3Call it from any other script:
@script.finance.convert(100)
anywhere

Result: the caller receives 2200.


Register-row schema (Scripts.Bindings.ScriptInvocations)

FieldTypeRequiredDefaultDescription
fullNamestringThe fully-qualified name you'll type after @script.
scriptstringLOV to the target script code in the Scripts register.
positionalArgsbooleanfalseTurn ON if the caller should be allowed to invoke the binding positionally.
paramNamesarray<string>when positionalArgs=trueOrdered mapping from invocation arguments to named parameters passed to the target script.
addCallerContextbooleantrueMerge caller's #variables into the callee's context. Explicit binding arguments win on key clash.

Call syntax

// named variables 
@script.<package>.<code>({
key1: "value1",
key2: "value2",
})

// positional argumemts
@script.<package>.<code>( arg1, arg2, … )
  • If positionalArgs=false: Pass one map@script.util.slugify({ "text" : #title })
  • If positionalArgs=true: the binding translates the positional call into a named parameter map using paramNames.

Example:

{
"fullName": "ticket.addComment",
"script": "Ticket.AddComment",
"positionalArgs": true,
"paramNames": ["orderId", "comment"]
}
@script.ticket.addComment(#order.id, 'Created from gateway')

The callee receives:

{
"orderId": "...",
"comment": "Created from gateway"
}

The target script then works only with named params such as #orderId and #comment.

Return value = whatever the callee script's last expression evaluates to.


Context variables inside the callee

VariableAvailable whenContains
named params from the bindingpositionalArgs=truevalues mapped by paramNames, for example #orderId, #comment
keys from caller mappositionalArgs=falsewhatever the caller passed in the single map argument
(caller variables)addCallerContext=trueevery #variable the caller had, unless shadowed by an explicit argument key

Example with named-argument map

Helper scriptString.Pad

#text.padEnd(#width, #char ?: ' ')

Binding row

{
"fullName": "string.pad",
"script": "String.Pad",
"positionalArgs": false
}

Caller script

#padded = @script.string.pad({
"text" : #name,
"width" : 20,
"char" : '.'
})

#padded"Invoice………………"


Example with positional-to-named translation

Helper scriptTicket.AddComment

#ticketService.addComment(#orderId, #comment)

Binding row

{
"fullName": "ticket.addComment",
"script": "Ticket.AddComment",
"positionalArgs": true,
"paramNames": ["orderId", "comment"]
}

Caller script

@script.ticket.addComment(#ticket.id, 'Validated by workflow')

The target script receives the named params #orderId and #comment.


Error handling

ExceptionThrown whenTypical fix
ApiConfigurationExceptionno binding row matches fullNameAdd or correct the row in Script Invocations.
ApiConfigurationExceptionpositionalArgs=true but paramNames is missing or invalidDefine config.paramNames as a list of unique target param names.
ApiValidationExceptionargument style mismatch (too many positional params, wrong type)Pass a single map or set positionalArgs=true.
ApiValidationExceptionpositional call count does not match the number of configured paramNamesAdjust the call or the binding mapping.

Performance notes

  • Bindings are cached for 5 minutes. Binding edits become visible after the cache expires.
  • The callee runs in the same transaction / thread as the caller (no extra overhead).
Transaction context

Since the callee shares the caller's transaction, any entity changes made by the callee are committed or rolled back together with the caller. For more on transaction boundaries, see SpEL and Transactions.


Using paramsFormCode and resultFormCode

When a target script defines paramsFormCode and/or resultFormCode, those tSM Forms act as a typed contract for the binding:

Script fieldEffect on script-to-script binding
paramsFormCodeThe JSON Schema embedded in the form validates the parameter map at call time. Callers get autocomplete and inline documentation in the SpEL Console.
resultFormCodeDocuments the return structure so callers know what to expect. Currently informational — no runtime enforcement.

Because a tSM Form is simultaneously a JSON Schema (data model) and a UI definition, a single artefact covers three concerns at once:

  1. Validation — parameters are checked against the schema before execution; type mismatches surface immediately.
  2. Documentation — field labels, descriptions, and constraints serve as living API docs for anyone calling the script.
  3. UI — the SpEL Console (and BPMN modeler) renders the form so users can fill in parameters interactively instead of writing raw JSON.
tip

Always define paramsFormCode on scripts that are bound as reusable helpers. It makes the binding self-documenting and prevents callers from passing invalid data.


See also