Scripting Reference
This document provides a comprehensive reference for writing scripts in workflow rules. Scripts allow you to implement advanced automation logic using JavaScript.
Overview
The Run Script action in workflow rules executes JavaScript code to automate complex ticket handling scenarios. Scripts run in a sandboxed environment with access to ticket data, message context, and helper functions.
Key features:
- Read and modify ticket properties
- Access customer and owner information
- Work with custom fields
- Use conditional logic and loops
- 10-second execution timeout for safety
Ticket Object
The ticket object is the primary context for your scripts. It provides both read and write access to ticket data.
Read-Only Properties
// Basic ticket information
ticket.id // UUID string
ticket.subject // Ticket subject line
// Current status (object)
ticket.status.id // Status UUID
ticket.status.code // Status code (e.g., "open", "closed")
ticket.status.name // Status display name
// Current priority (object)
ticket.priority.id // Priority UUID
ticket.priority.name // Priority display name
// Current department (object)
ticket.department.id // Department UUID
ticket.department.name // Department display name
// Assigned owner (object, may be undefined)
ticket.owner.id // User UUID
ticket.owner.email // User email
ticket.owner.firstname // User first name
ticket.owner.lastname // User last name
// Customer information (object)
ticket.customer.id // Customer UUID
ticket.customer.email // Customer email
ticket.customer.firstname // Customer first name
ticket.customer.lastname // Customer last name
ticket.customer.name // Customer full name
// Folder and tags
ticket.folder // Current folder: "INBOX", "ASSIGNED", "SPAM", "ARCHIVE"
ticket.tags // Array of tag strings
Writable Properties
You can modify these properties by direct assignment:
// Set status by code or ID
ticket.status = "closed";
ticket.status = "550e8400-e29b-41d4-a716-446655440000";
// Set priority by name or ID
ticket.priority = "High";
ticket.priority = "550e8400-e29b-41d4-a716-446655440001";
// Set department by name or ID
ticket.department = "Sales";
ticket.department = "550e8400-e29b-41d4-a716-446655440002";
// Set owner by email or ID
ticket.owner = "[email protected]";
ticket.owner = "550e8400-e29b-41d4-a716-446655440003";
// Set folder
ticket.folder = "ASSIGNED";
ticket.folder = "SPAM";
ticket.folder = "ARCHIVE";
ticket.folder = "INBOX";
// Set tags (replaces all existing tags)
ticket.tags = ["urgent", "vip"];
Custom Fields
Custom fields can be read and written using their code (not the display name).
Reading Custom Fields
// Access custom field value directly
var orderId = ticket.order_id; // Text field
var amount = ticket.order_amount; // Number field
var isVip = ticket.is_vip; // Boolean field
var region = ticket.region; // Select field
Writing Custom Fields
// Set text custom field
ticket.order_id = "ORD-12345";
// Set number custom field
ticket.order_amount = 199.99;
// Set boolean custom field
ticket.is_vip = true;
// Set select custom field
ticket.category = "billing";
// Set date custom field (ISO 8601 format)
ticket.due_date = "2024-12-31T23:59:59Z";
ticket.due_date = "2024-12-31";
Message Object
The message object is available when the rule is triggered by a message event (New Customer Reply, New Staff Reply, etc.).
if (message) {
message.id // Message UUID
message.body_text // Message content (plain text)
message.body_html // Message content (HTML)
message.createdAt // ISO 8601 timestamp
}
Example:
if (message && message.body_text) {
var bodyLower = message.body_text.toLowerCase();
if (bodyLower.includes("cancel subscription")) {
ticket.department = "Retention";
ticket.priority = "High";
}
}
Helper Functions
getStatuses()
Returns an array of all available statuses.
var statuses = getStatuses();
// Returns: [{ id: "...", code: "open", name: "Open" }, ...]
// Find a specific status
for (var i = 0; i < statuses.length; i++) {
if (statuses[i].code === "pending") {
ticket.status = statuses[i].id;
break;
}
}
getPriorities()
Returns an array of all available priorities.
var priorities = getPriorities();
// Returns: [{ id: "...", name: "Low" }, { id: "...", name: "High" }, ...]
getDepartments()
Returns an array of all available departments.
var departments = getDepartments();
// Returns: [{ id: "...", name: "Support" }, { id: "...", name: "Sales" }, ...]
getUsers()
Returns an array of all available users (agents).
var users = getUsers();
// Returns: [{ id: "...", email: "[email protected]", firstname: "John", lastname: "Doe" }, ...]
fetch()
Makes HTTP requests to external APIs. Synchronous - returns result directly.
var response = fetch(url, options);
// response: { ok, status, statusText, text, json }
Options: method (GET/POST/PUT/DELETE), headers (object), body (string or object)
Example: Fetch from CRM and update custom fields
var response = fetch("https://api.example.com/customers/" + ticket.customer.email, {
headers: { "Authorization": "Bearer YOUR_API_KEY" }
});
if (response.ok && response.json) {
ticket.customer_tier = response.json.tier;
ticket.lifetime_value = response.json.lifetime_value;
if (response.json.tier === "enterprise") {
ticket.priority = "High";
}
}
Example: POST to webhook
var response = fetch("https://hooks.example.com/helpdesk", {
method: "POST",
body: { ticket_id: ticket.id, customer_email: ticket.customer.email }
});
Notes: HTTPS only, 5s timeout, 1MB response limit, internal networks blocked.
Common Patterns
Conditional Assignment
// Set priority based on subject
if (ticket.subject.toLowerCase().includes("urgent")) {
ticket.priority = "High";
} else if (ticket.subject.toLowerCase().includes("question")) {
ticket.priority = "Low";
}
Department Routing
var subject = ticket.subject.toLowerCase();
if (subject.includes("billing") || subject.includes("payment") || subject.includes("invoice")) {
ticket.department = "Billing";
} else if (subject.includes("technical") || subject.includes("bug") || subject.includes("error")) {
ticket.department = "Technical Support";
} else if (subject.includes("sales") || subject.includes("pricing") || subject.includes("quote")) {
ticket.department = "Sales";
}
Customer Domain-Based Logic
if (ticket.customer && ticket.customer.email) {
var email = ticket.customer.email;
var domain = email.split("@")[1];
// Enterprise customer handling
if (domain === "bigcorp.com" || domain === "enterprise.org") {
ticket.priority = "High";
ticket.department = "Enterprise Support";
ticket.is_enterprise = true;
}
// Educational institution
if (domain.endsWith(".edu")) {
ticket.segment = "education";
}
}
Tag Management
// Add tags without losing existing ones
var existingTags = ticket.tags || [];
existingTags.push("processed");
existingTags.push("automated");
ticket.tags = existingTags;
Working with Messages
if (message && message.body_text) {
var body = message.body_text.toLowerCase();
// Detect frustration keywords
var frustrationKeywords = ["frustrated", "angry", "terrible", "worst", "unacceptable"];
for (var i = 0; i < frustrationKeywords.length; i++) {
if (body.includes(frustrationKeywords[i])) {
ticket.priority = "High";
ticket.needs_attention = true;
break;
}
}
}
Round-Robin Assignment
var users = getUsers();
if (users.length > 0) {
// Simple round-robin based on ticket ID hash
var ticketHash = 0;
for (var i = 0; i < ticket.id.length; i++) {
ticketHash += ticket.id.charCodeAt(i);
}
var assigneeIndex = ticketHash % users.length;
ticket.owner = users[assigneeIndex].email;
}
Error Handling
Scripts are executed in a safe environment:
- Syntax errors are caught and logged; the script is skipped.
- Invalid field values (e.g., non-existent status name) are ignored.
- Timeouts (10 seconds) prevent infinite loops.
- Errors don't cascade to other rules or system components.
Best practices:
// Check for null/undefined before accessing properties
if (ticket.customer && ticket.customer.email) {
// Safe to access ticket.customer.email
}
if (message) {
// Safe to access message properties
}
// Use try-catch for complex logic
try {
var data = JSON.parse(ticket.custom_json);
ticket.parsed_field = data.value;
} catch (e) {
// Handle error gracefully
}
Limitations
- Execution timeout: Scripts are terminated after 10 seconds.
- No file system access: File operations are not available.
- No persistent state: Variables don't persist between script executions.
- Read-only fields:
ticket.id,ticket.subject,ticket.customercannot be modified. - ES5 JavaScript: Modern JavaScript features (arrow functions, const/let, etc.) may not be available.