DataMagik Script Engine

Updated Dec 13, 2025
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

  1. Getting Started
  2. Return Values & Notifications
  3. Document Generation
  4. User Defined Tables
  5. Serial Number Generation
  6. Calling Other Scripts
  7. Credentials Management
  8. HTTP Requests
  9. Manufacturing & Printing
  10. ZPL Label Templates
  11. Helper Functions
  12. Keyboard Shortcuts
  13. 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 success boolean
  • 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_message for 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
  };
}


Was this page helpful?