Skip to content

YuzeScript

What is YuzeScript?

YuzeScript is a JavaScript-based scripting language for writing custom logic in Yuze. It runs on the V8 engine and supports modern JavaScript features including async/await.

YuzeScript is used in:

  • Workflow steps: Process and transform datapoints with custom logic
  • Schema Mapping Expressions: Calculate field values during data transformation
  • API Connectors: Handle incoming HTTP requests and produce datapoints

The editor provides IntelliSense with type definitions based on your schemas, making it easier to write correct code.

YuzeScript in Workflow Steps

In a workflow step, YuzeScript lets you write custom processing logic.

Available globals: context (consume/produce/workflowStep/vars), yuze (master data helpers), and console (logging).

source, target, and request are not available here. There is no HTTP client or fetch — outbound API calls are handled by connectors, not workflow scripts.

context.consume

Retrieves all datapoints from the consumed feed. Takes no arguments, returns an array matching the input feed schema.

javascript
const datapoints = await context.consume();

for (const dp of datapoints) {
  console.log(dp.EquipmentId);
}

context.produce

Writes datapoints to the produced feed. Accepts a single object or an array.

javascript
await context.produce({
  EquipmentId: 'PUMP-001',
  Status: 'Active'
});

// Or multiple datapoints
await context.produce([
  { EquipmentId: 'PUMP-001', Status: 'Active' },
  { EquipmentId: 'PUMP-002', Status: 'Inactive' }
]);

context.workflowStep

Identifies the current workflow step instance. Useful for logging or conditional logic.

javascript
console.log(context.workflowStep.Id);    // UUID string
console.log(context.workflowStep.Name);  // Workflow step name

TIP

context.yuzecase is a legacy alias for context.workflowStep and still works, but prefer context.workflowStep in new scripts.

context.vars

Reads variables defined on the workflow (see the workflow Settings tab). Reference a variable by name as context.vars.<name>. A name that isn't defined reads as undefined.

javascript
console.log('Reporting year: ' + context.vars.YEAR);

await context.produce({
  EquipmentId: 'PUMP-001',
  Year: context.vars.YEAR
});

TIP

The same variables can also be used in non-script settings via the double-curly-brace token form. Inside YuzeScript, use context.vars.YEAR instead — it's valid code the editor understands.

Complete Workflow Step Example

javascript
const datapoints = await context.consume();

const results = datapoints.map(dp => ({
  ...dp,
  processed: true,
  processedAt: new Date().toISOString()
}));

await context.produce(results);

Enriching with Master Data

When looking up master data for each item, use a for loop instead of .map() because each iteration requires await:

javascript
const items = await context.consume();
const enriched = [];
for (const item of items) {
  const ref = await yuze.createExternalMasterDataItemReference('Equipment', 'SAP', item.EquipmentId);
  const equipment = await yuze.lookupMasterDataItem(ref);
  enriched.push({
    ...item,
    EquipmentName: equipment?.displayName || 'Unknown'
  });
}
await context.produce(enriched);

YuzeScript in Schema Mappings

In schema mapping expressions, YuzeScript calculates field values during data transformation. You work with source (input data) and assign values to target fields.

Available globals: source (read-only input), target (writable output), yuze (master data helpers), and console (logging).

context and request are not available here. Only explicitly mapped fields assigned to target appear in the output.

source

The input data being transformed. Contains all fields from the source schema (read-only).

javascript
console.log(source.FirstName);  // 'John'
console.log(source.LastName);   // 'Doe'
console.log(source.Address.City);

target

The output data structure. Assign values to target properties to set the output fields.

javascript
target.FullName = source.FirstName + ' ' + source.LastName;
target.ProcessedAt = new Date().toISOString();

Complete Schema Mapping Example

javascript
target.FullName = source.FirstName + ' ' + source.LastName;
target.Address = [source.Street, source.City, source.Country].filter(Boolean).join(', ');
target.Priority = source.Severity > 7 ? 'High' : 'Low';

YuzeScript in API Connectors

API connectors use YuzeScript to handle incoming HTTP requests and produce datapoints.

Available globals: request (incoming HTTP request), context.produce() (write datapoints), yuze (master data helpers), and console (logging).

context.consume(), source, and target are not available here.

request

The incoming HTTP request object:

  • request.method — HTTP method ('POST', 'GET', etc.)
  • request.headers — Object with header names as keys
  • request.query — Query parameters from the URL
  • request.path — URL path after connector base URL
  • request.body — Parsed body. JSON is parsed natively. XML is auto-converted to JSON: all values become strings, single child elements are objects (not arrays), attributes become @name, text content becomes #text.
  • request.bodyRaw — Raw unparsed body string. Use when you need the exact original text or when the auto-converted JSON shape is not suitable.
javascript
console.log(request.method);              // 'POST'
console.log(request.headers['content-type']);
console.log(request.query.status);        // query parameter
console.log(request.path);                // URL path after connector base URL
console.log(request.body);                // parsed body (JSON or XML-to-JSON)
console.log(request.bodyRaw);            // raw unparsed body string

response (Return Value)

Return an object { status, body?, headers? } to define the HTTP response.

javascript
return {
  status: 200,
  body: { success: true }
};

// Error response
return {
  status: 400,
  body: { error: 'Invalid request' }
};

// With custom response headers
return {
  status: 200,
  body: { ok: true },
  headers: { 'X-Request-Id': request.headers['x-request-id'] || 'none' }
};

context.produce (API Connector)

Writes datapoints to the configured feed.

javascript
await context.produce(request.body);

Complete API Connector Example

javascript
await context.produce(request.body);

return {
  status: 200,
  body: { success: true }
};

Built-in Functions

The yuze object provides helper functions available in all contexts.

yuze.createExternalMasterDataItemReference

Creates a reference to a master data item from an external system.

javascript
const ref = await yuze.createExternalMasterDataItemReference(
  'Equipment',  // type
  'SAP',        // source system
  'PUMP-001'    // source ID
);
// Returns: 'master-data-ext:Equipment:SAP:PUMP-001'

yuze.createInternalMasterDataItemReference

Creates a reference to an internally managed master data item.

javascript
const ref = await yuze.createInternalMasterDataItemReference(
  'Location',   // type
  'SITE-A'      // ID
);
// Returns: 'master-data:Location:SITE-A'

yuze.lookupMasterDataItem

Retrieves a master data item by its identifier. Returns null if not found.

javascript
const item = await yuze.lookupMasterDataItem('master-data:Equipment:PUMP-001');

if (item) {
  console.log(item.displayName);
  console.log(item.properties.Status);
  console.log(item.mappingIdentifiers.SAP[0]);
}

yuze.searchMasterDataItems

Searches for master data items with filters. Returns paginated results with a continue token for fetching additional pages.

Parameters:

  • query: Search criteria
    • type: Master data type to search
    • onlyRootItems: (optional) Filter to only root items
    • metadata: (optional) Filter by metadata field values
  • pagination: Pagination options
    • pageSize: Number of items per page
    • nextPageToken: (optional) Token from previous result for next page

Returns: CursorPagingResult<MasterDataItem>

  • items: Array of matching master data items
  • nextPageToken: Token for fetching the next page (undefined if no more pages)

Basic Example

javascript
const result = await yuze.searchMasterDataItems(
  {
    type: 'Equipment',
    onlyRootItems: true,
    metadata: { Status: 'Active' }
  },
  { pageSize: 50 }
);

for (const item of result.items) {
  console.log(item.displayName);
}

Paginating Through All Results

Use a do-while loop to fetch all pages using the nextPageToken:

javascript
let nextPageToken = undefined;

do {
  const result = await yuze.searchMasterDataItems(
    {
      type: 'Equipment',
      metadata: { Status: 'Active' }
    },
    {
      pageSize: 100,
      nextPageToken: nextPageToken
    }
  );

  for (const item of result.items) {
    console.log(`Processing: ${item.displayName}`);
  }

  nextPageToken = result.nextPageToken;
} while (nextPageToken);

yuze.lookupMasterDataItemSystemKey

Gets the external system identifier for a master data item.

javascript
const sapId = await yuze.lookupMasterDataItemSystemKey(
  'master-data:Equipment:PUMP-001',
  'SAP'
);
// Returns: 'SAP-PUMP-001' or null

yuze.saveMasterDataItem

Creates or updates a master data item.

javascript
await yuze.saveMasterDataItem({
  identifier: 'master-data-ext:Equipment:SAP:PUMP-001',
  displayName: 'Main Cooling Pump',
  parentId: 'master-data:Location:PLANT-A',
  properties: {
    SerialNumber: 'SN-12345',
    Status: 'Active'
  },
  mappingIdentifiers: {
    SAP: ['10001234']
  }
});

yuze.deleteMasterDataItem

Deletes a master data item by its identifier. Children of the item are not deleted — they are left without a parent.

javascript
await yuze.deleteMasterDataItem('master-data:Equipment:PUMP-001');

yuze.saveMasterDataHierarchy

Saves one or more complete master data trees in a single call, keeping stored data in step with the source system that owns them. The items you pass are authoritative for the trees they describe: each item is created or updated as needed, and any item already stored under one of those trees that is not in your payload is removed. Use this when a source owns a whole hierarchy and you want the stored data to match its latest payload exactly — instead of dropping and re-creating the tree.

Pass the flat list of items (parent links are carried on each item via parentId, the same item shape as yuze.saveMasterDataItem above) plus an options object:

javascript
const result = await yuze.saveMasterDataHierarchy(
  [
    { identifier: 'master-data:Location:PLANT-A', displayName: 'Plant A' },
    {
      identifier: 'master-data:Location:LINE-1',
      displayName: 'Assembly Line 1',
      parentId: 'master-data:Location:PLANT-A'
    },
    {
      identifier: 'master-data:Location:CELL-1',
      displayName: 'Cell 1',
      parentId: 'master-data:Location:LINE-1'
    }
  ],
  { onMissing: { strategy: 'delete' } }
);

console.log(result); // { Inserted, Updated, Unchanged, Deleted, Unlinked, Archived }

Options:

  • onMissing — what to do with items still stored under a tree but missing from your payload. Pass an object { strategy, property }. Supported strategies:
    • 'delete' (default) — remove them, so stored data matches the source.
    • 'unlink' — keep them, detaching the removed branch from its parent so it survives as its own tree.
    • 'archive' — keep them exactly where they are and stamp the current timestamp into a metadata property so you can recognise them as archived. The property name is set via property (defaults to 'ArchivedAt'). Already-archived items keep their original timestamp — re-running the sync does not move it or re-archive them.
javascript
// Archive missing items into a custom property instead of deleting them
const result = await yuze.saveMasterDataHierarchy(
  items,
  { onMissing: { strategy: 'archive', property: 'RetiredOn' } }
);

Only items that actually changed are updated; unchanged items are left as-is. When anything inside a tree changes, its ancestors up to the root are marked as changed as well, so Incremental Sync re-syncs only the affected hierarchies rather than the entire dataset.

Choosing the archive property

The property you archive into is yours to choose, because "archived" means different things to different sources — some flag a status field, others stamp a retirement date. If you don't pass property, the timestamp lands in ArchivedAt. Reserved properties (DisplayName, Identifier, ParentId) can't be used.

Re-sending an archived item un-archives it

If an archived item later reappears in your payload, it is treated as a normal update and the archive marker is cleared — the item is live again. You don't need a separate "un-archive" step: sending the item back is the signal. (The marker is only cleared if your payload doesn't itself carry that property; whatever you send for it wins.)

Authority is per tree, not across trees

Removal only applies to the trees you actually send: items missing from a tree you send are removed, but a whole hierarchy you simply stop sending is left untouched — there is no signal to distinguish "deliberately removed" from "not included in this run." Decide per workflow how entire hierarchies get retired; the save can't infer it.

yuze.sha256Hash

Calculates a SHA-256 hash. Useful for deduplication.

javascript
const hash = await yuze.sha256Hash({ id: 123, name: 'Test' });
// Returns: 64-character hex string

yuze.searchDataPoints

Searches recorded data points with filters. Returns paginated results with a continue token for fetching additional pages.

Parameters:

  • query: Search criteria (all fields optional)
    • schema: A schema id (e.g. 'schema:emissions') or a schema version id (e.g. 'schema-version:emissions:1.0') — the form is detected automatically. A schema id matches data points of any version of that schema; a schema version id matches only that version.
    • fromDate / toDate: ISO-8601 timestamps bounding the data point's timestamp.
    • bucket: Restrict to a single bucket.
    • propertyFilters: Array of { propertyName, operator, value }. Operators: Equals, NotEquals, Contains, StartsWith, EndsWith, IsNull, IsNotNull (value is omitted for the last two).
  • pagination: Pagination options.
    • pageSize: Number of items per page (required).
    • nextPageToken: (optional) Opaque cursor to read from. Omit (or pass null) to start from the beginning; pass back either token from a previous result to continue from there. Results are always ordered oldest → newest.

Returns:

  • items: Array of matching data points (code, type, values, numberValue, stringValue, schemas, associatedWith, bucket, timeStamp, position, …)
  • nextPageToken: Cursor for the next page, or null once you have read everything. Loop while it is set to walk the whole result set.
  • resumeToken: Cursor for where you got to. Stays set even once you are caught up, so you can save it and pass it back on a later run to read only what has been written since. null only when nothing has ever matched your query.

Both are opaque — store them as-is and pass them back verbatim; don't parse or build them.

Paginate through everything with a do-while loop:

javascript
let nextPageToken = undefined;
const all = [];

do {
  const result = await yuze.searchDataPoints(
    { schema: 'schema:emissions' },
    { pageSize: 100, nextPageToken }
  );

  all.push(...result.items);
  nextPageToken = result.nextPageToken;
} while (nextPageToken);
javascript
const result = await yuze.searchDataPoints(
  {
    schema: 'schema:emissions',
    fromDate: '2026-01-01T00:00:00Z',
    propertyFilters: [
      { propertyName: 'Scope', operator: 'Equals', value: 'Scope 1' },
      { propertyName: 'Note', operator: 'IsNotNull' }
    ]
  },
  { pageSize: 50 }
);

for (const dataPoint of result.items) {
  console.log(dataPoint.code, dataPoint.numberValue);
}

Reading only what's new (remembering where you left off)

Use resumeToken when you want a script to pick up where it left off rather than re-read everything. Persist it, and next time you pass it back you continue from that point — so you only ever read data points written since.

  • First read / from scratch: omit nextPageToken (or pass null). Results come oldest-first.
  • Read only what's new: pass the previous run's resumeToken back.
  • Re-read everything: omit the token again.

resumeToken stays set once any data has matched, including on a page that came back empty — so it's always safe to save. That's the difference between the two tokens: nextPageToken goes null to end a paging loop, resumeToken survives one so it can be stored between runs.

javascript
// Example: an API connector that returns new emissions data points since the caller's last token.
const page = await yuze.searchDataPoints(
  { schema: 'schema-version:emissions:1.0' },
  {
    pageSize: 200,
    nextPageToken: request.query.token ?? null  // null on the first call
  }
);

return {
  status: 200,
  body: {
    items: page.items.map(x => x.values),
    // The caller persists this and sends it back next time to get only newer data points.
    token: page.resumeToken ?? null
  }
};

WARNING

This surfaces newly written data points only — not edits or deletes to existing ones. It's a "read what's new" feed, not a full change feed.

XML transformations

yuze.xml converts between XML and plain JavaScript objects — useful when an external system speaks XML but the rest of your script works with objects. These helpers are synchronous (no await needed).

yuze.xml.build

Builds an XML string from an object. Keys starting with @_ become attributes; arrays become repeated elements; text is entity-escaped automatically — never hand-concatenate XML.

javascript
const xml = yuze.xml.build({
  Order: {
    '@_id': 'A1',
    Customer: 'Foo & Sons',   // the & is escaped to &amp; for you
    Line: ['Widget', 'Gadget'] // an array becomes repeated <Line> elements
  }
}, { format: true });          // format: true pretty-prints with indentation

// <Order id="A1">
//   <Customer>Foo &amp; Sons</Customer>
//   <Line>Widget</Line>
//   <Line>Gadget</Line>
// </Order>
return xml;

yuze.xml.parse

Parses an XML string into an object. It throws if the XML is malformed, so you never get silent garbage.

Always list repeating elements in arrays. XML has no array syntax, so a single <Line> parses as an object but two <Line> elements parse as an array. That inconsistency breaks scripts that expect a list. Naming the element in arrays forces it to always be an array — even when there's only one.

javascript
const doc = yuze.xml.parse(xml, { arrays: ['Line'] });

const orderId = doc.Order['@_id'];      // attributes are read with the @_ prefix
const lines = doc.Order.Line;           // always an array thanks to the arrays option

WARNING

All parsed values are strings — even numbers. <Code>00123</Code> parses as the string "00123" (this deliberately preserves leading zeros in external-system IDs). Convert explicitly when you need a number: Number(doc.Item.Code).

yuze.xml.validate

Checks whether a string is well-formed XML without parsing it. Returns { valid: true } or { valid: false, error: '...' }.

javascript
const check = yuze.xml.validate(input);
if (!check.valid) {
  throw new Error('Bad XML from the supplier: ' + check.error);
}

Console Logging

Standard console methods are available and captured in execution logs:

javascript
console.log('Processing started');
console.warn('Missing optional field');
console.error('Failed to process item');
MethodLog Level
console.log()Information
console.warn()Warning
console.error()Error

Error Handling

Script errors include line numbers and stack traces. Use try-catch for graceful error handling:

javascript
try {
  const item = await yuze.lookupMasterDataItem(identifier);
  if (!item) {
    console.warn(`Item not found: ${identifier}`);
    return null;
  }
  return item.properties;
} catch (error) {
  console.error(`Lookup failed: ${error.message}`);
  throw error;
}

Tips

  • Use async/await: All yuze functions and context methods are asynchronous
  • Check for null: Lookup functions return null when items are not found
  • Use console.log: Debug your scripts by logging intermediate values