DataMagik Script Engine
Script Engine User Guide
Complete reference for automating workflows, generating documents, and integrating with external systems using DataMagik's JavaScript-based Script Engine.
📸 Screenshot Needed: Script Engine main interface showing the left sidebar with script list, center Monaco code editor, and right-side documentation panel. Navigate to Manufacturing → Script Engine to capture this.
Table of Contents
- Getting Started
- Return Values & Notifications
- Document Generation
- User Defined Tables
- Serial Number Generation
- Calling Other Scripts
- Credentials Management
- HTTP Requests
- Manufacturing & Printing
- ZPL Label Templates
- Helper Functions
- Keyboard Shortcuts
- Best Practices
1. Getting Started
Every script in the Script Engine must have a main(context) function. This function receives input data and returns a result object.
Your First Script
function main(context) {
console.log("Hello from Script Engine!");
return {
success: true,
script_message: "Script executed successfully!",
data: { result: "your data here" }
};
}
Key Concepts:
- context - An object containing all input data passed to your script
- return object - Must include at minimum a
successboolean - console.log() - Outputs appear in the execution history for debugging
📸 Screenshot Needed: The Monaco code editor with a simple "Hello World" script, showing the syntax highlighting and line numbers.
Testing Your Script
Use the Test Run button to execute your script before saving. You can provide test input context using the Test Input Context panel.
📸 Screenshot Needed: The Test Input Context modal showing sample JSON input data.
2. Return Values & Notifications
The return object from your main() function controls what happens after execution and what the user sees.
Return Object Properties
PropertyTypeDescriptionsuccessbooleanRequired. Indicates if the script succeededscript_messagestringUser-facing message shown in UI notificationsscript_titlestringOptional title for toast notificationsnotification_levelstring"success", "warning", "error", or "info"dataobjectAny additional data to return to the callererrorstringError message if success is false
Toast Notification Examples
Success Notification:
return {
success: true,
script_title: "Order Processed",
script_message: "Order #12345 was created successfully",
notification_level: "success"
};
Warning Notification:
return {
success: true,
script_title: "Missing Data",
script_message: "Customer code not provided, using default",
notification_level: "warning"
};
Error Notification:
return {
success: false,
script_title: "Processing Failed",
script_message: "Unable to connect to external API",
notification_level: "error"
};
📸 Screenshot Needed: A toast notification appearing in the browser extension or UI, showing a success message with green styling.
3. Document Generation
Generate PDF documents using the global documents object. Documents are processed asynchronously through a worker queue.
Available Methods
MethodDescriptiondocuments.generate(template, fields)Queue async generation, returns request IDdocuments.generateSync(template, fields, opts?)Generate and wait for completiondocuments.getStatus(requestId)Check status of pending generation
Async Generation (Fire & Forget)
Use this when you don't need to wait for the document to be created:
function main(context) {
// Queue a document generation job
const reqId = documents.generate(
"template-uuid-or-name",
{
customerName: context.name,
orderDate: "2025-12-05",
items: context.lineItems
}
);
console.log("Document queued:", reqId);
return {
success: true,
script_message: "Document generation started",
data: { document_request_id: reqId }
};
}
Synchronous Generation (Wait for Result)
Use this when you need the document URL immediately:
function main(context) {
const result = documents.generateSync(
"Invoice Template",
{
customerName: context.name,
total: context.total
},
{
returnType: "url", // "url" or "binary" (base64)
timeout: 60000 // milliseconds to wait
}
);
if (result.success) {
console.log("Document URL:", result.documentUrl);
console.log("File size:", result.fileSizeBytes);
return {
success: true,
data: { url: result.documentUrl }
};
} else {
return {
success: false,
error: result.error
};
}
}
Checking Status
const status = documents.getStatus(requestId);
// status object structure:
// {
// status: "pending" | "processing" | "completed" | "failed",
// document_url: "...", // if completed
// error_message: "...", // if failed
// file_size_bytes: 12345
// }
📸 Screenshot Needed: The Document Designer template list showing several templates that can be referenced by the Script Engine.
4. User Defined Tables
User Defined Tables (UDTs) allow you to store and retrieve customer-specific configurations, mappings, and data. Access them using the tables object.
Note: Manage tables via Manufacturing → User Defined Tables in the navigation menu.
Available Methods
MethodDescriptiontables.get(table, key1, [key2])Get full entry by key(s), includes metadatatables.getValue(table, key1, [key2])Get just the value (shorthand)tables.exists(table, key1, [key2])Check if entry exists, returns booleantables.set(table, key1, [key2], value)Create or update an entry (upsert)tables.list(table, [key1])List all entries, optionally filtered by key1
Reading Data
function main(context) {
// Full entry lookup (includes metadata)
const customer = tables.get("customer_settings", context.customerCode);
const templateId = customer ? customer.value.template_id : null;
// Direct value lookup (simpler!)
const config = tables.getValue("customer_settings", context.customerCode);
const template = config ? config.template_id : "default";
// Composite key lookup (e.g., customer + location)
const shippingDoc = tables.getValue(
"customer_shipping_docs",
context.customerCode,
context.shipToCode
);
// Check existence for blocklists
if (tables.exists("blocklist", context.ip)) {
return { success: false, script_message: "Blocked" };
}
return { success: true, data: { template: templateId } };
}
Writing Data
function main(context) {
// Update order status (creates if doesn't exist)
tables.set("order_status", context.orderId, {
status: "processed",
updated_at: new Date()
});
// Get all shipping locations for a customer
const allLocations = tables.list(
"customer_shipping_docs",
context.customerCode
);
return {
success: true,
data: { locations: allLocations }
};
}
Tip: Table functions are synchronous. Use tables.getValue() for cleaner code when you only need the value object.
📸 Screenshot Needed: The User Defined Tables interface showing a table with sample data entries, including the key columns and value column.
5. Serial Number Generation
Generate and manage serial numbers using the serial object. Serial number patterns are configured in Manufacturing → Traceability.
Available Methods
MethodDescriptionserial.next(series, [context])Generate next serial (increments counter)serial.preview(series, [context])Preview next serial (no increment)serial.info(series)Get pattern details (format, counter)serial.batch(series, count, [context])Generate multiple serials at onceserial.list()List all available serial patterns
Basic Usage
function main(context) {
// Generate next serial number
// Context object replaces tokens like {plant} or {line}
const sn = serial.next("WorkOrders", {
plant: "DET",
line: "L1"
});
// Preview without consuming
const nextSn = serial.preview("WorkOrders", {
plant: "DET",
line: "L1"
});
return {
success: true,
data: { serial_number: sn }
};
}
Batch Generation
function main(context) {
// Get pattern information
const info = serial.info("batch_serial");
// info = { name, format, description, current_value, prefix, suffix }
// Generate multiple serials for a batch run
const serials = serial.batch("part_serial", 5, { line: "L1" });
// serials = ["SN-2025-00001", "SN-2025-00002", ...]
// List all available patterns
const patterns = serial.list();
// patterns = [{ name, format, description }, ...]
return {
success: true,
data: {
batch_serials: serials,
available_patterns: patterns.length
}
};
}
Tip: Use serial.batch() for bulk operations to ensure atomic serial number allocation.
📸 Screenshot Needed: The Traceability Management page showing a list of serial number series with their format patterns.
6. Calling Other Scripts
Organize your code by calling other scripts or including shared utility functions.
Available Methods
MethodDescriptionscripts.run(name, input)Execute another script and get its resultscripts.include(name)Include shared code/functions (like import)
Execute Another Script
function main(context) {
// Call another script by name
// The second argument is the input context for the child script
const result = scripts.run("SharedUtility", {
data: context.rawData
});
// Note: scripts.run returns the 'data' object from the child script's return
return {
success: true,
data: { processed: result }
};
}
Include Shared Code
// In your main script:
function main(context) {
// Include shared utility functions (once per execution)
scripts.include("SharedHelpers");
// Now you can use functions defined in SharedHelpers
const formatted = formatCustomerName(context.customer);
const validated = validateOrder(context.order);
return {
success: validated,
data: { name: formatted }
};
}
// SharedHelpers script contains:
// function formatCustomerName(c) {
// return c.first + " " + c.last;
// }
// function validateOrder(o) {
// return o.items && o.items.length > 0;
// }
Tip: Use scripts.include() for shared utilities, and scripts.run() when you need a full script's output and don't want to expose internal functions.
7. Credentials Management
Access securely stored API keys and secrets using the credentials object. Credentials are configured in Settings → Credentials.
function main(context) {
// Get API key stored in credentials
const apiKey = credentials.get("PLEX_API_KEY");
// Use in your API calls
// (The apiKey can be used with http.get or other integrations)
return {
success: true,
data: { hasKey: !!apiKey }
};
}
Security Note: Never hardcode API keys or secrets in your scripts. Always use the credentials system.
📸 Screenshot Needed: The Settings → Credentials page showing how to add a new credential (without showing actual secret values).
8. HTTP Requests
Make external API calls using the http object. Only domains configured in the allowed list can be accessed.
function main(context) {
// Synchronous GET request
const resp = http.get("https://api.example.com/data");
// Response structure:
// {
// status: 200,
// statusText: "OK",
// data: { ... } // Parsed JSON body
// }
if (resp.status === 200) {
console.log("Received data:", resp.data.id);
return {
success: true,
data: resp.data
};
} else {
console.error("Error:", resp.statusText);
return {
success: false,
error: resp.statusText
};
}
}
Security Note: Only allowed domains can be accessed. Configure permitted domains in Settings → Allowed Domains.
📸 Screenshot Needed: The Settings → Allowed Domains page showing how to whitelist external API domains.
9. Manufacturing & Printing
Access printers and send print jobs using the manufacturing object. Printers must be registered through your site's connector.
Available Methods
MethodDescriptionmanufacturing.getPrinters()List all available printersmanufacturing.getPrinter(name)Get details of a specific printermanufacturing.printLabel(printer, zpl)Send raw ZPL to a printermanufacturing.printFromTemplate(printer, template, data)Print using a ZPL templatemanufacturing.printBatch(printer, template, items)Print multiple labels in one job
List and Print
function main(context) {
// List available printers
const printers = manufacturing.getPrinters();
console.log("Available printers:", printers.map(p => p.name));
// Get specific printer details
const printer = manufacturing.getPrinter("Line1-Zebra");
if (!printer) {
return { success: false, error: "Printer not found" };
}
// Print raw ZPL
const result = manufacturing.printLabel(
"Line1-Zebra",
"^XA^FO50,50^A0N,50,50^FDHello World^FS^XZ"
);
return {
success: result.success,
data: { job_id: result.job_id }
};
}
Template-Based Printing
function main(context) {
// Print using a template (data replaces {placeholders})
const result = manufacturing.printFromTemplate(
"Shipping-Printer",
"ShippingLabel", // Template name
{
orderNumber: context.order_id,
customerName: context.customer,
address: context.ship_to
}
);
// Batch print multiple labels
const batchResult = manufacturing.printBatch(
"Line1-Zebra",
"PartLabel",
context.parts.map(p => ({
partNumber: p.number,
serialNumber: p.serial,
quantity: p.qty
}))
);
return {
success: true,
data: {
single_job: result.job_id,
batch_jobs: batchResult.job_ids,
labels_printed: batchResult.count
}
};
}
Note: Printers must be registered in your site's connector. Configure in Manufacturing → Printers.
📸 Screenshot Needed: The Manufacturing → Printers page showing a list of configured printers with their connection status.
10. ZPL Label Templates
Render ZPL label templates with data substitution using the zpl object.
function main(context) {
// Render a ZPL template to a string (without printing)
const zplCode = zpl.fromTemplate("PartLabel", {
partNumber: "PN-12345",
description: "Widget Assembly",
serialNumber: serial.next("PartSerial"),
barcode: context.barcode_data
});
// Now you can:
// 1. Print it directly
manufacturing.printLabel("Zebra-01", zplCode);
// 2. Return it for external use
return {
success: true,
data: { zpl: zplCode }
};
}
Tip: Use zpl.fromTemplate() to preview or modify ZPL before printing.
11. Helper Functions
Common utility functions are available via the helpers object.
Available Methods
MethodDescriptionhelpers.formatDate(date, format)Format date to stringhelpers.parseDate(str, format)Parse string to Date objecthelpers.formatNumber(num, decimals)Format number with decimal placeshelpers.formatCurrency(num, currency)Format as currencyhelpers.deepClone(obj)Deep copy an objecthelpers.uuid()Generate a new UUIDhelpers.hash(str, algo)Hash string (sha256, md5)helpers.base64Encode(str)Encode string to Base64helpers.base64Decode(str)Decode Base64 to string
Date & Number Formatting
function main(context) {
const now = new Date();
// Format dates (Go-style format: 2006-01-02 15:04:05)
const dateStr = helpers.formatDate(now, "2006-01-02"); // "2025-12-05"
const timeStr = helpers.formatDate(now, "15:04:05"); // "14:30:00"
const fullStr = helpers.formatDate(now, "Jan 2, 2006"); // "Dec 5, 2025"
// Parse dates
const parsed = helpers.parseDate("2025-12-25", "2006-01-02");
// Format numbers
const price = helpers.formatNumber(1234.567, 2); // "1234.57"
const usd = helpers.formatCurrency(1234.56, "USD"); // "$1,234.56"
const eur = helpers.formatCurrency(1234.56, "EUR"); // "€1,234.56"
return {
success: true,
data: { date: dateStr, price: usd }
};
}
Date Format Reference
DataMagik uses Go-style format patterns:
2006= year (4 digits)01= month (2 digits)02= day (2 digits)15= hour (24-hour, 2 digits)04= minute (2 digits)05= second (2 digits)
Other Utilities
function main(context) {
// Generate UUID
const id = helpers.uuid(); // "550e8400-e29b-41d4-a716-446655440000"
// Deep clone (safe copy)
const copy = helpers.deepClone(context.data);
copy.modified = true; // Won't affect original
// Hashing
const hash = helpers.hash("secret", "sha256");
const md5 = helpers.hash("data", "md5");
// Base64
const encoded = helpers.base64Encode("Hello World"); // "SGVsbG8gV29ybGQ="
const decoded = helpers.base64Decode(encoded); // "Hello World"
return {
success: true,
data: { id, hash }
};
}
12. Keyboard Shortcuts
Speed up your development with these keyboard shortcuts:
ShortcutActionCtrl+SSave scriptCtrl+EnterExecute script (Test Run)Shift+F11Toggle fullscreen editorEscapeExit fullscreenCtrl+SpaceShow autocomplete suggestionsCtrl+/Toggle line commentCtrl+DSelect next occurrenceCtrl+Shift+KDelete line
Tip: Type main in the editor and press Tab for a template snippet!
13. Best Practices
Code Organization
- Validate context early — Check required fields exist before using them
- Use User Defined Tables — Don't hardcode customer-specific values
- Store secrets in credentials — Never hardcode API keys in scripts
- Add console.log statements — They appear in execution history for debugging
- Return meaningful messages — Use
script_messagefor user visibility - Test before saving — Use Test Run to avoid breaking production scripts
Error Handling Pattern
function main(context) {
try {
// Validate inputs
if (!context.orderId) {
return {
success: false,
error: "Order ID is required",
notification_level: "error"
};
}
// Your business logic here
const result = processOrder(context.orderId);
return {
success: true,
script_message: "Order processed successfully",
data: result
};
} catch (error) {
console.error("Script failed:", error);
return {
success: false,
script_title: "Processing Error",
script_message: String(error),
notification_level: "error"
};
}
}
Complete Example: Customer-Specific Document Generation
function main(context) {
// Look up customer's preferred template
const customer = tables.get("customer_templates", context.customerCode);
// Use customer template or fallback to default
const templateId = customer
? customer.value.template_id
: "default-invoice-template";
// Queue document generation
documents.generate(templateId, {
customerName: context.customerName,
orderNumber: context.orderNumber,
items: context.lineItems
});
return {
success: true,
script_message: "Document queued for " + context.customerCode
};
}