Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developer.kodexa.ai/llms.txt

Use this file to discover all available pages before exploring further.

A SCRIPT step runs JavaScript inside an Activity. It is the right tool when the workflow needs custom logic that is too specific for a declarative step. Script steps are commonly used to inspect documents, normalize extracted data, assign knowledge features, call Service Bridges, and decide which branch of the Activity Plan should run next.

When to Use a SCRIPT Step

Use a SCRIPT step when you need to:
  • Route the Activity based on document content, metadata, extraction results, or task status
  • Make several checks before deciding whether human review is needed
  • Update document metadata, labels, tags, data objects, or attributes
  • Assign knowledge features to document families
  • Call one or more Service Bridges and combine their responses
  • Emit a named action for downstream dependencies
Do not use a script just to make one external API call. Use a Service Bridge step for that so the external call has its own step status, logs, retry behavior, and result.

Step Configuration

{
  "slug": "route-invoice",
  "kind": "SCRIPT",
  "dependsOn": ["extract"],
  "config": {
    "scriptActions": [
      { "name": "review" },
      { "name": "post" },
      { "name": "reject" }
    ],
    "scriptSidecars": ["acme-finance/invoice-script-helpers"],
    "scriptBody": "return { action: 'review' };"
  }
}
Config keyDescription
scriptBodyInline JavaScript source
scriptActionsNamed actions the script can return
scriptSidecarsJavaScript module refs loaded before the script runs
The script must return an object with an action property when downstream routing depends on the step.
return { action: "review" };
Downstream steps can depend on that action:
{
  "slug": "analyst-review",
  "kind": "CREATE_TASK",
  "dependsOn": ["route-invoice:review"]
}

Activity SCRIPT Runtime

Activity Plan SCRIPT steps run in the server-side Goja runtime. The runtime exposes the same business objects the Activity is working on, plus helper namespaces for task state, document families, document content, Service Bridges, LLM calls, and knowledge features.
ObjectUse
taskSnapshot of the Task that started the Activity, when the Activity is task-backed
familiesSnapshot array of document families attached to the Task
orgCurrent organization { id, slug }
tasksQuery and mutate task records in the current project
documentsQuery and mutate document family records in the current project
loadDocument(familyId) / documents.load(familyId)Load a KDDB document for content nodes, tags, data objects, metadata, labels, validations, and exceptions
lookupFeature*Find or create knowledge features for classification and routing
serviceBridgeCall project-scoped Service Bridges from custom decision logic
llmInvoke configured LLM services with platform cost tracking
log / consoleWrite script logs visible from the Activity step details
The current Activity Plan script context is centered on the Task and its document families. Use prior step outputs through declarative step mappings, then pass the needed values into document metadata, task data, or downstream step configuration instead of assuming a generic inputs object exists in the script runtime.

Runtime Limits

LimitValue
Script timeout15 seconds
Document loads5 per script execution
Task reads / document family reads50 per namespace
Task mutations / document family mutations20 per namespace
Task/document mutation payload256 KB per call
Document changes are saved after the script completes. Modified KDDB documents are persisted as new content object versions with the SCRIPT_MODIFICATION transition type.

Task Namespace

Use tasks when a script needs to inspect or update human review work inside the current project.
MethodDescription
tasks.current()Load the current Task
tasks.parent()Load the current Task’s parent, if any
tasks.subTasks()List subtasks of the current Task
tasks.subTasksOf(taskId)List subtasks for another Task in the same project
tasks.get(taskId)Load a Task by ID
tasks.query(filter)Query Tasks by status, statusType, parentTaskId, templateSlug, and limit
tasks.lookupStatus(slug)Resolve a project Task status
tasks.listStatuses()List project Task statuses
tasks.setStatus(taskId, statusSlug)Change Task status and trigger Activity advancement when the status type is DONE
tasks.setProperties(taskId, props)Merge properties into Task data
tasks.setMetadata(taskId, metadata)Merge Task metadata
tasks.setTitle(taskId, title)Update Task title
tasks.setDescription(taskId, description)Update Task description
tasks.lock(taskId) / tasks.unlock(taskId)Lock or unlock a Task

Document Family Namespace

Use documents when a script needs to manage document family state without loading the full KDDB document.
MethodDescription
documents.taskFamilies()List document families attached to the current Task
documents.get(familyId)Load a document family summary
documents.getByPath(storeSlug, path)Find a document family by store and path
documents.query(filter)Query families by storeSlug, path, pathContains, label, mixin, status, featureSlug, locked, and limit
documents.contentObjects(familyId)List content object versions
documents.latestContentObject(familyId, contentType?)Get the latest content object, optionally DOCUMENT or NATIVE
documents.load(familyId)Load the KDDB document, equivalent to loadDocument(familyId)
documents.lookupStatus(slug) / documents.listStatuses()Resolve document statuses
documents.setMetadata(familyId, metadata)Merge metadata into the family
documents.addLabel(familyId, label) / documents.removeLabel(familyId, label)Manage family labels
documents.setStatus(familyId, statusSlug)Update document status
documents.lock(familyId) / documents.unlock(familyId)Lock or unlock the family
documents.attachToTask(familyId, taskId?) / documents.detachFromTask(familyId, taskId?)Manage Task-family links

Routing Example

This script loads the first Task document, inspects its text, and routes the Activity to review, rejection, or straight-through posting.
var family = families[0];
var doc = loadDocument(family.id);
var text = doc.getRootNode().getAllContent(" ", true).toLowerCase();

if (text.indexOf("duplicate invoice") !== -1) {
  log.warn("Duplicate invoice language detected");
  return { action: "reject" };
}

var invoice = doc.findFirstDataObjectByPath("invoice");
var amount = invoice ? Number(invoice.getFirstAttributeValue("total_amount")) : 0;

if (!amount || amount > 10000) {
  return { action: "review" };
}

return { action: "post" };

Updating Documents

Document changes made by a script are persisted after the step completes.
var family = families[0];
var doc = loadDocument(family.id);
doc.setMetadata("scriptStep", "tag-total-lines");
doc.addLabel("invoice-intake");

var root = doc.getRootNode();
var matches = root.select("//line[contains(@content, 'TOTAL')]");

for (var i = 0; i < matches.length; i++) {
  matches[i].tag("invoice/total-line", {
    confidence: 0.95,
    value: matches[i].getContent()
  });
}

return { action: "tagged" };
Do not call doc.close() in Activity Plan scripts. Kodexa persists document changes after the script completes.

Calling Service Bridges from a Script

Use serviceBridge.call() inside a SCRIPT step when the decision requires more than a single declarative bridge call.
var supplier = serviceBridge.call(
  "finance-reference-data",
  "lookup-supplier",
  { supplierId: task.data.supplierId }
);

if (!supplier || supplier.status !== "active") {
  log.warn("Supplier is not active:", task.data.supplierId);
  return { action: "review" };
}

var duplicate = serviceBridge.call(
  "finance-reference-data",
  "check-duplicate-invoice",
  {
    supplierId: task.data.supplierId,
    invoiceNumber: task.data.invoiceNumber
  }
);

return { action: duplicate && duplicate.found ? "review" : "post" };
Service bridge credentials stay in the platform. Scripts reference the configured bridge and endpoint; they do not handle secrets directly.

Shared Script Sidecars

Use scriptSidecars to pre-load reusable JavaScript helpers.
{
  "slug": "normalize-fields",
  "kind": "SCRIPT",
  "config": {
    "scriptSidecars": ["acme-finance/invoice-script-helpers"],
    "scriptActions": [{ "name": "normalized" }],
    "scriptBody": "normalizeInvoiceFields(families[0].id); return { action: 'normalized' };"
  }
}
Sidecars are useful for shared validation functions, string cleanup, request mapping, and common routing decisions. Keep sidecars small and version them like any other project resource.

Design Guidance

  • Declare every returned action in scriptActions.
  • Keep scripts focused on one decision or one transformation.
  • Prefer BRIDGE_CALL for a single external API call that operators should see as its own step.
  • Use logs at important decision points.
  • Make scripts idempotent when they can be retried.
  • Avoid long loops and repeated document loads.

Next Steps

Script API Reference

See the full JavaScript context, document helpers, LLM calls, and logging reference.

Service Bridge Steps

Learn when to model an external API call as its own Activity Plan step.