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.
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
- Script Schedules
- 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
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.
Script Versions
Each script maintains a version history. When you save changes, a new version is created. You can view previous versions and their code from the script's version panel.
2. Return Values & Notifications
The return object from your main() function controls what happens after execution and what the user sees.
Return Object Properties
| Property | Type | Description |
|---|---|---|
success | boolean | Required. Indicates if the script succeeded |
script_message | string | User-facing message shown in UI notifications |
script_title | string | Optional title for toast notifications |
notification_level | string | "success", "warning", "error", or "info" |
data | object | Any additional data to return to the caller |
error | string | Error 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"
};3. Document Generation
Generate PDF documents using the global documents object. Documents are processed asynchronously through a worker queue.
Available Methods
| Method | Description |
|---|---|
documents.generate(template, fields) | Queue async generation, returns request ID |
documents.generateSync(template, fields, opts?) | Generate and wait for completion |
documents.getStatus(requestId) | Check status of pending generation |
Async Generation (Fire & Forget)
function main(context) {
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)
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) {
return {
success: true,
data: { url: result.documentUrl }
};
} else {
return {
success: false,
error: result.error
};
}
}Checking Status
const status = documents.getStatus(requestId);
// status = {
// status: "pending" | "processing" | "completed" | "failed",
// document_url: "...", // if completed
// error_message: "...", // if failed
// file_size_bytes: 12345
// }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
| Method | Description |
|---|---|
tables.get(table, key1, [key2]) | Get full entry by key(s), includes metadata |
tables.getValue(table, key1, [key2]) | Get just the value (shorthand) |
tables.exists(table, key1, [key2]) | Check if entry exists, returns boolean |
tables.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.
5. Serial Number Generation
Generate and manage serial numbers using the serial object. Serial number patterns are configured in Manufacturing → Traceability.
Available Methods
| Method | Description |
|---|---|
serial.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 once |
serial.list() | List all available serial patterns |
Basic Usage
function main(context) {
// Generate next serial number
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();
return {
success: true,
data: {
batch_serials: serials,
available_patterns: patterns.length
}
};
}Tip: Use serial.batch() for bulk operations to ensure atomic serial number allocation.
6. Calling Other Scripts
Organize your code by calling other scripts or including shared utility functions.
Available Methods
| Method | Description |
|---|---|
scripts.run(name, input) | Execute another script and get its result |
scripts.include(name) | Include shared code/functions (like import) |
Execute Another Script
function main(context) {
// Call another script by name
const result = scripts.run("SharedUtility", {
data: context.rawData
});
// 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
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.
7. Credentials Management
Access securely stored API keys and secrets using the credentials object. Credentials are configured in Settings → Credentials.
Credential Types
| Type | Description |
|---|---|
| API Key | Single API key value |
| Basic Auth | Username and password pair |
| OAuth2 | OAuth2 client credentials |
| Custom JSON | Arbitrary JSON object for complex credentials |
Using Credentials
function main(context) {
// Get API key stored in credentials
const apiKey = credentials.get("PLEX_API_KEY");
// Use in your API calls
return {
success: true,
data: { hasKey: !!apiKey }
};
}Security Note: Never hardcode API keys or secrets in your scripts. Always use the credentials system.
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) {
return {
success: true,
data: resp.data
};
} else {
return {
success: false,
error: resp.statusText
};
}
}Allowed Domains
Only whitelisted domains can be accessed from scripts. Configure permitted domains in Settings → Allowed Domains.
- Add domains to allow HTTP requests to external services
- Remove domains to revoke access
- All subdomains of allowed domains are also permitted
9. Manufacturing & Printing
Access printers and send print jobs using the manufacturing object. Printers must be registered through your site's connector.
Available Methods
| Method | Description |
|---|---|
manufacturing.getPrinters() | List all available printers |
manufacturing.getPrinter(name) | Get details of a specific printer |
manufacturing.printLabel(printer, zpl) | Send raw ZPL to a printer |
manufacturing.printFromTemplate(printer, template, data) | Print using a ZPL template |
manufacturing.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
manufacturing.printFromTemplate(
"Shipping-Printer",
"ShippingLabel",
{
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: {
batch_jobs: batchResult.job_ids,
labels_printed: batchResult.count
}
};
}Note: Printers must be registered in your site's connector. Configure in Connectors → Printers.
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
});
// Print it
manufacturing.printLabel("Zebra-01", zplCode);
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
| Method | Description |
|---|---|
helpers.formatDate(date, format) | Format date to string |
helpers.parseDate(str, format) | Parse string to Date object |
helpers.formatNumber(num, decimals) | Format number with decimal places |
helpers.formatCurrency(num, currency) | Format as currency |
helpers.deepClone(obj) | Deep copy an object |
helpers.uuid() | Generate a new UUID |
helpers.hash(str, algo) | Hash string (sha256, md5) |
helpers.base64Encode(str) | Encode string to Base64 |
helpers.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:
| Pattern | Meaning |
|---|---|
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();
// Deep clone (safe copy)
const copy = helpers.deepClone(context.data);
copy.modified = true; // Won't affect original
// Hashing
const hash = helpers.hash("secret", "sha256");
// Base64
const encoded = helpers.base64Encode("Hello World");
const decoded = helpers.base64Decode(encoded);
return {
success: true,
data: { id, hash }
};
}12. Script Schedules
Run scripts automatically on a recurring schedule using cron expressions.
Creating a Schedule
Navigate to DataMagik → Script Schedules to manage scheduled script executions.
| Field | Description |
|---|---|
| Name | Schedule display name |
| Script | Script to execute |
| Cron Expression | When to run (e.g., 0 9 * * MON-FRI) |
| Input Context | JSON data passed to the script's context |
| Enabled | Toggle the schedule on/off |
Common Cron Patterns
| Expression | Description |
|---|---|
*/15 * * * * | Every 15 minutes |
0 9 * * MON-FRI | 9 AM on weekdays |
0 */2 * * * | Every 2 hours |
0 0 1 * * | Midnight on the 1st of each month |
0 6,18 * * * | 6 AM and 6 PM daily |
Managing Schedules
- Enable/Disable — Toggle schedules without deleting them
- Execution History — View past executions with status, duration, and output
- Cancel Queued — Cancel pending executions that haven't started yet
- Validate Cron — Use the cron validator to preview when your schedule will run next
13. Keyboard Shortcuts
Speed up your development with these keyboard shortcuts:
| Shortcut | Action |
|---|---|
Ctrl+S | Save script |
Ctrl+Enter | Execute script (Test Run) |
Shift+F11 | Toggle fullscreen editor |
Escape | Exit fullscreen |
Ctrl+Space | Show autocomplete suggestions |
Ctrl+/ | Toggle line comment |
Ctrl+D | Select next occurrence |
Ctrl+Shift+K | Delete line |
Tip: Type main in the editor and press Tab for a template snippet.
14. 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 {
if (!context.orderId) {
return {
success: false,
error: "Order ID is required",
notification_level: "error"
};
}
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
};
}