Skip to main content

JavaScript Scripts

JavaScript scripts execute custom logic in a sandboxed JavaScript engine. A script can be defined inline or loaded from a .js file in the repository.

Definition

{
"name": "calculatePremium",
"type": "javascript",
"scope": "OBJECT",
"trigger": "UPDATE",
"javascriptFile": "scripts/calculatePremium"
}
PropertyTypeDescription
javascriptFilestringPath to a .js file in the repository (without extension).
javascriptstringInline JavaScript source. Use javascriptFile for anything non-trivial.

Inline vs file-based

For short scripts, define the JavaScript inline with javascript. For anything non-trivial, use javascriptFile to reference a .js file in the repository:

my-project/
├── index.json
├── scripts/
│ └── calculatePremium.js
{
"name": "calculatePremium",
"type": "javascript",
"scope": "OBJECT",
"trigger": "UPDATE",
"javascriptFile": "scripts/calculatePremium"
}

Function signature

Every script file must define a function named execute:

function execute(api, dataObject, params) {
// your logic here
}
ArgumentDescription
apiThe script API — read and write data, log messages, send notifications. See below.
dataObjectThe current object as a plain JavaScript map. All field values are accessible by their camelCase names. The special field _reference holds the object's URI and can be passed directly to API methods.
paramsResolved script parameters as a key-value map.

The api object

Reading data

api.get(uri)

Returns a single object by URI, or null if not found.

var company = api.get(dataObject.company);

api.query(typePluralName, filters)

api.query(typePluralName, filters, sortBy)

Returns all objects of a given type matching the filters. filters is a key-value map of field names to values — pass null for no filters. Optionally sort by a field name.

var activeContracts = api.query('employmentAgreements', { status: 'ACTIVE' });
var sorted = api.query('invoices', null, 'invoiceDate');

api.getRelatedObjects(uri, fromTypePluralName, typePluralName)

api.getRelatedObjects(uri, fromTypePluralName, typePluralName, filters, sortBy)

Returns all related objects of a given type for the object at uri. fromTypePluralName is the type that owns the relation definition — a data object can have multiple types, and the relation may be defined on a different type than the one in the URI.

var lines = api.getRelatedObjects(
dataObject._reference,
'invoices', // type that owns the 'lines' relation
'invoiceLines' // type to retrieve
);

With filters and sorting:

var expenseLines = api.getRelatedObjects(
dataObject._reference,
'invoices',
'invoiceLines',
{ type: 'EXPENSE' },
'amount'
);

Writing data

api.create(typePluralName, data)

Creates a new object and returns the created record as a map.

var task = api.create('tasks', {
name: 'Follow up with ' + dataObject.name,
dueDate: dataObject.expiryDate,
assignedTo: dataObject.accountManager
});

api.update(uri, data)

Patches an existing object. Only the provided fields are updated; all other fields are left unchanged.

api.update(dataObject._reference, {
status: 'COMPLETED',
completedDate: new Date().toISOString().slice(0, 10)
});

api.saveMany(records)

Creates or updates multiple objects in a single batch. Each entry must have either:

  • _reference — patches the existing object at that URI
  • _type — creates a new object of that plural type name

Background recalculations are deduplicated across the batch, making this more efficient than calling create / update in a loop.

// Batch update
var updates = lines.map(function(line) {
return { _reference: line._reference, processed: true };
});
api.saveMany(updates);

// Batch create
api.saveMany([
{ _type: 'tasks', name: 'Task A', dueDate: '2025-06-01' },
{ _type: 'tasks', name: 'Task B', dueDate: '2025-06-15' }
]);

api.remove(uri)

Deletes the object at the given URI.

api.remove(obsoleteTask._reference);

Utilities

api.runScript(scriptName, dataObjectMap, parameters)

Runs another script by name. Pass the object map for OBJECT-scope scripts, or null for GENERAL-scope scripts.

api.runScript('sendConfirmationEmail', dataObject, { template: 'welcome' });

api.createBinary(fileName, contentType, content)

Creates a binary file from a UTF-8 string and returns its URI. The returned URI can be assigned directly to a FILE-typed field.

var csv = 'name,amount\nLine A,100\nLine B,200';
var fileUri = api.createBinary('export.csv', 'text/csv', csv);
api.update(dataObject._reference, { exportFile: fileUri });

api.stripHtml(html)

Removes HTML tags and decodes HTML entities. Useful when processing TEXTBLOCK values before including them in generated documents.

var plainText = api.stripHtml(dataObject.description);

api.log(message)

Logs a message to the server log, visible in application logs.

api.log('Processing ' + dataObject._reference);

api.notify(message, level)

Sends a notification to the user who triggered the script. Levels: INFO, WARNING, ERROR.

api.notify('No matching records found.', 'WARNING');

Example: set a completion date on status change

A script on my-project.task that records the date when a task is completed. Using triggerValues ensures it only runs when the status field changes to COMPLETED.

{
"name": "setCompletedDate",
"type": "javascript",
"scope": "OBJECT",
"trigger": "UPDATE",
"triggerValues": ["COMPLETED"],
"javascriptFile": "scripts/setCompletedDate"
}
function execute(api, dataObject, params) {
if (dataObject.completedDate) return; // already set, don't overwrite

api.update(dataObject._reference, {
completedDate: new Date().toISOString().slice(0, 10)
});
}

Use a formula instead

If the total only depends on field values of related objects, a formula using sum is simpler and more efficient — it recalculates automatically without a manual trigger or script overhead. Use a JavaScript script for this pattern only when the logic cannot be expressed as a formula, for example when the calculation involves external data or conditional branching that cannot be established using sumIf.

A script on my-project.invoice that sums the amounts of all invoice lines and writes the total back to the parent invoice.

function execute(api, dataObject, params) {
var lines = api.getRelatedObjects(
dataObject._reference,
'invoices',
'invoiceLines'
);

var total = 0;
for (var i = 0; i < lines.length; i++) {
total += lines[i].amount ? lines[i].amount.amount : 0;
}

api.update(dataObject._reference, {
totalAmount: { amount: total, currency: 'EUR' }
});

api.log('Invoice total updated: ' + total);
}

Example: manual action with parameters

A script triggered manually by the user. Parameters are resolved from the current object before the script runs.

{
"name": "sendReminder",
"type": "javascript",
"scope": "OBJECT",
"trigger": "MANUAL",
"javascriptFile": "scripts/sendReminder",
"parameters": [
{ "name": "recipientEmail", "dataType": "EMAIL", "formula": "emailAddress" },
{ "name": "dueDate", "dataType": "DATE", "formula": "expiryDate" }
],
"preconditions": ["emailAddress != null"]
}
function execute(api, dataObject, params) {
var email = params.recipientEmail;
var due = params.dueDate;

if (!email) {
api.notify('No email address on this record.', 'WARNING');
return;
}

api.create('reminderLogs', {
sentTo: email,
sentAt: new Date().toISOString(),
dueDate: due,
contract: dataObject._reference
});

api.notify('Reminder sent to ' + email, 'INFO');
}