> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://help.buildworkforce.ai/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# JavaScript Tool

### What is a JavaScript Tool Action?
A JavaScript Tool Action lets you run custom JavaScript code within your tool. This gives you powerful data processing capabilities - you can transform data, perform calculations, make decisions, format outputs, and implement custom logic that goes beyond what standard actions provide.
**Common use cases:**
* Transform data from one format to another
* Perform calculations and mathematical operations
* Filter and manipulate arrays and objects
* Format text, dates, and numbers
* Implement conditional logic (if/then/else)
* Parse and extract information from complex data
* Combine data from multiple sources
* Validate data before sending to other actions
**What you can do:**
* Access tool inputs and previous action outputs
* Run any JavaScript code (with some security restrictions)
* Return structured data (objects, arrays) or simple values
* Use built-in JavaScript functions and libraries
* Log debugging information
* Make network requests using fetch or other standard APIs
**What you cannot do:**
* Access files or databases directly (except for network-accessible resources)
* Use browser-specific APIs (DOM, window, etc.)
* Access external npm packages (only built-in JavaScript features)

### Adding a JavaScript Action to Your Tool
1. In your Custom Tool, click **"Add Action"**
2. Select **"JavaScript Code"** as the action type
3. Enter a descriptive **action name** (e.g., `processData`, `calculateTotal`, `formatOutput`)
4. Write your JavaScript code in the code editor
5. Click **"Add"** or **"Save"**
![](https://storage.crisp.chat/users/helpdesk/website/-/c/8/1/5/c8154f3e149fd000/image_1kovvy7.png)
![](https://storage.crisp.chat/users/helpdesk/website/-/c/8/1/5/c8154f3e149fd000/image_1oo5ytc.png)

### Writing Your JavaScript Code

#### Accessing Inputs and Previous Action Outputs
Your JavaScript code has access to a special object called `ctx` (short for "context") that contains:
* All tool inputs
* All outputs from previous actions
**Structure of&#160;`ctx`:**
```
{
  inputName1: "value from input",
  inputName2: 123,
  actionName1: { /* result from first action */ },
  actionName2: { /* result from second action */ }
}
```
**Example:**
If your tool has:
* Input: `customerId` with value `"12345"`
* Previous action: `fetchUser` that returned `{ "name": "John", "email": "john@example.com" }`
Your `ctx` object will be:
```
{
  customerId: "12345",
  fetchUser: {
    name: "John",
    email: "john@example.com"
  }
}
```

#### Two Ways to Return Data

##### Option 1: Using `console.log()` (Simple)
For simple string outputs, just use `console.log()`:
```
console.log("Hello, World!");
```
**What you get back:** A single string with everything you logged.
**Example: Basic calculation**
```
const total = ctx.price * ctx.quantity;
console.log(`Total cost: $${total}`);
```
**Output:** `"Total cost: $150"`
##### Option 2: Using `main()` function (Structured)
For complex outputs or structured data, define a `main()` function that returns a value:
```
async function main() {
  // Your code here
  return { /* your result */ };
}
```
**What you get back:** Whatever your `main()` function returns (objects, arrays, numbers, strings, etc.)
**Example: Returning an object**
```
async function main() {
  const firstName = ctx.fullName.split(' ')[0];
  const lastName = ctx.fullName.split(' ')[1];

  return {
    firstName: firstName,
    lastName: lastName,
    email: ctx.email,
    timestamp: new Date().toISOString()
  };
}
```
**Output:**
```
{
  "firstName": "John",
  "lastName": "Doe",
  "email": "john@example.com",
  "timestamp": "2024-01-15T10:30:00.000Z"
}
```
**When to use which method:**
* Use `console.log()` for: Simple text outputs, debugging messages, quick calculations
* Use `main()` for: Structured data, complex transformations, returning objects/arrays

### Common JavaScript Patterns

#### Pattern 1: Transforming API Responses
Extract and restructure data from a previous API call:
```
async function main() {
  const apiResponse = ctx.fetchCustomer.responseBody;

  return {
    id: apiResponse.customer.id,
    name: `${apiResponse.customer.firstName} ${apiResponse.customer.lastName}`,
    email: apiResponse.customer.contact.email,
    status: apiResponse.customer.isActive ? "Active" : "Inactive"
  };
}
```
#### Pattern 2: Calculating Values
Perform calculations on numeric inputs or previous results:
```
async function main() {
  const subtotal = ctx.price * ctx.quantity;
  const tax = subtotal * 0.08; // 8% tax
  const shipping = subtotal > 100 ? 0 : 10; // Free shipping over $100
  const total = subtotal + tax + shipping;

  return {
    subtotal: subtotal.toFixed(2),
    tax: tax.toFixed(2),
    shipping: shipping.toFixed(2),
    total: total.toFixed(2)
  };
}
```
#### Pattern 3: Filtering and Mapping Arrays
Process lists of data:
```
async function main() {
  const orders = ctx.fetchOrders.responseBody.orders;

  // Filter only completed orders
  const completedOrders = orders.filter(order => order.status === "completed");

  // Extract just the order IDs and totals
  const orderSummary = completedOrders.map(order => ({
    orderId: order.id,
    total: order.totalAmount,
    date: order.completedDate
  }));

  return {
    count: completedOrders.length,
    orders: orderSummary
  };
}
```
#### Pattern 4: Conditional Logic
Make decisions based on data:
```
async function main() {
  const score = ctx.getUserScore.responseBody.score;

  let category;
  let message;

  if (score >= 90) {
    category = "Excellent";
    message = "Outstanding performance!";
  } else if (score >= 70) {
    category = "Good";
    message = "Solid work!";
  } else if (score >= 50) {
    category = "Fair";
    message = "Room for improvement.";
  } else {
    category = "Needs Improvement";
    message = "Additional support recommended.";
  }

  return {
    score: score,
    category: category,
    message: message
  };
}
```

#### Pattern 5: String Manipulation
Format and process text:
```
async function main() {
  const email = ctx.userEmail.toLowerCase().trim();
  const name = ctx.userName
    .split(' ')
    .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
    .join(' ');

  const username = email.split('@')[0];
  const domain = email.split('@')[1];

  return {
    email: email,
    name: name,
    username: username,
    domain: domain
  };
}
```
#### Pattern 6: Date and Time Operations
Work with dates:
```
async function main() {
  const startDate = new Date(ctx.startDate);
  const endDate = new Date(ctx.endDate);

  const diffTime = Math.abs(endDate - startDate);
  const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));

  return {
    startDate: startDate.toISOString(),
    endDate: endDate.toISOString(),
    durationDays: diffDays,
    durationWeeks: Math.floor(diffDays / 7)
  };
}
```
#### Pattern 7: Data Validation
Check if data meets requirements:
```
async function main() {
  const errors = [];

  if (!ctx.email || !ctx.email.includes('@')) {
    errors.push("Invalid email address");
  }

  if (!ctx.phone || ctx.phone.length < 10) {
    errors.push("Phone number must be at least 10 digits");
  }

  if (ctx.age < 18) {
    errors.push("Must be 18 or older");
  }

  return {
    isValid: errors.length === 0,
    errors: errors,
    data: {
      email: ctx.email,
      phone: ctx.phone,
      age: ctx.age
    }
  };
}
```
#### Pattern 8: Combining Multiple Data Sources
Merge data from different actions:
```
async function main() {
  const user = ctx.fetchUser.responseBody;
  const orders = ctx.fetchOrders.responseBody;
  const preferences = ctx.fetchPreferences.responseBody;

  return {
    profile: {
      userId: user.id,
      name: user.name,
      email: user.email
    },
    activity: {
      totalOrders: orders.length,
      lastOrderDate: orders[0]?.date || null
    },
    settings: {
      notifications: preferences.notificationsEnabled,
      theme: preferences.theme
    }
  };
}
```

### Debugging Your JavaScript Code

#### Using `console.log()` for debugging
Add `console.log()` statements to see what your code is doing:
```
async function main() {
  console.log("Starting calculation...");
  console.log("Input value:", ctx.inputValue);

  const result = ctx.inputValue * 2;
  console.log("Result:", result);

  return result;
}
```
When you test the action, you'll see all the logged messages along with the final output.

#### Common debugging techniques

**1. Log the entire context:**
```
console.log("Full context:", JSON.stringify(ctx, null, 2));
```
**2. Check if a value exists:**
```
if (!ctx.expectedValue) {
  console.log("Warning: expectedValue is missing!");
}
```
**3. Log intermediate steps:**
```
const step1 = processData(ctx.input);
console.log("After step 1:", step1);

const step2 = transform(step1);
console.log("After step 2:", step2);

return step2;
```
**4. Catch and log errors:**
```
async function main() {
  try {
    return performComplexOperation(ctx);
  } catch (error) {
    console.log("Error occurred:", error.message);
    return { error: error.message };
  }
}
```

### Testing Your JavaScript Action

**To test an individual action:**
1. Click the **"Test"** button at the bottom of the action editor
2. Provide sample values for:
    * Tool inputs
    * Previous action outputs (provide mock JSON data)
3. Click **"Run Test"**
4. Review the output
**To test the entire tool:**
1. Click the **"Test Tool"** button at the top right of the page
2. Provide values for all tool inputs
3. Review the complete execution flow and results

**Tips for testing:**
* Start with simple code and gradually add complexity
* Test with realistic data that matches production scenarios
* Verify the output structure matches what later actions expect
* Check for edge cases (empty arrays, null values, etc.)

### Troubleshooting JavaScript Actions

**Problem: "Cannot read property 'X' of undefined"**
* **Cause:** Trying to access a property that doesn't exist
* **Solution:** Check that the previous action or input exists and has the property you're accessing
```
// Instead of:
const value = ctx.action.data.field;

// Use safe access:
const value = ctx.action?.data?.field || "default value";
```
**Problem: Output is&#32;`[object Object]`&#32;instead of actual data**
* **Cause:** Using `console.log()` with objects without JSON.stringify
* **Solution:** Either use `main()` function or stringify objects:
```
console.log(JSON.stringify(myObject, null, 2));
```
**Problem: Code works in testing but fails in production**
* **Cause:** Test data structure doesn't match real data
* **Solution:**
    * Review actual API responses from previous actions
    * Add defensive checks for missing/undefined values
    * Use try/catch blocks to handle unexpected data
**Problem: Unexpected output format**
* **Cause:** Mixing `console.log()` and `main()` return
* **Solution:** Choose one approach:
    * For simple outputs: Only use `console.log()`, don't define `main()`
    * For structured outputs: Use `main()` and return the value
**Problem: "Timeout" or code takes too long**
* **Cause:** Infinite loops, very large data processing, or inefficient code
* **Solution:**
    * Check for infinite loops
    * Process data in smaller chunks
    * Optimize algorithms (reduce nested loops)
    * Consider if some processing could be done in the API instead
**Problem: Date operations return unexpected results**
* **Cause:** Time zone differences or invalid date strings
* **Solution:**
    * Use ISO 8601 format for dates: `"2024-01-15T10:30:00Z"`
    * Be explicit about time zones
    * Validate date strings before parsing

### JavaScript Action Best Practices

**1. Keep it simple**
* Write clear, readable code
* Avoid overly complex logic
* Break complex operations into multiple actions if needed
**2. Handle errors gracefully**
```
async function main() {
  try {
    // Your code here
    return result;
  } catch (error) {
    return {
      error: true,
      message: error.message
    };
  }
}
```
**3. Validate inputs**
```
async function main() {
  if (!ctx.requiredInput) {
    return { error: "Missing required input" };
  }

  // Continue with logic...
}
```
**4. Document your code**
```
async function main() {
  // Calculate total price including tax and shipping
  const subtotal = ctx.price * ctx.quantity;
  const tax = subtotal * 0.08; // 8% sales tax
  const total = subtotal + tax;

  return { subtotal, tax, total };
}
```
**5. Use descriptive variable names**
```
// Good
const userEmail = ctx.fetchUser.responseBody.email;
const orderTotal = calculateTotal(items);

// Bad
const e = ctx.fetchUser.responseBody.email;
const t = calculateTotal(items);
```
**6. Return consistent structures**
```
// Always return the same shape
return {
  success: true,
  data: { /* your data */ },
  message: "Operation completed"
};
```

### Security Considerations for JavaScript Actions

**1. Don't expose sensitive data in logs**
```
// ❌ Bad - logs might contain passwords/tokens
console.log("Full context:", ctx);

// ✅ Good - log only what's needed
console.log("Processing user:", ctx.userId);
```
**2. Validate and sanitize data**
```
// Remove potentially dangerous characters from user input
const sanitized = ctx.userInput.replace(/[<>]/g, '');
```
**3. Be careful with eval() or Function()**
* **Never** use `eval()` or `new Function()` with user-provided strings
* This can execute arbitrary code and is a security risk
**4. Limit data exposure**
```
// Don't return more than necessary
return {
  userId: user.id,
  name: user.name
  // Don't include: password, apiKey, internalId, etc.
};
```
 