> ## Documentation Index
> Fetch the complete documentation index at: https://docs.langdock.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandbox Utility Functions

> Complete reference for built-in JavaScript utility functions available in custom integrations, actions, triggers, and workflow code nodes

## Overview

When writing JavaScript code for integrations, actions, triggers, or workflow code nodes, you have access to a set of built-in utility functions. These functions run in a secure sandboxed JavaScript environment that provides essential capabilities without requiring external libraries.

<Note>
  These utilities are available in JavaScript code only. Python Code nodes use a
  separate runtime and do not support `ld.*` functions. For Python behavior, see
  the [Code node](/en/using-langdock/workflows/nodes/code-node) page.
</Note>

### What is the Sandbox?

The sandbox is a secure, isolated JavaScript execution environment that:

* **Runs untrusted code safely** - Memory-limited and timeout-enforced execution
* **Provides essential utilities** - HTTP requests, data conversions, cryptography
* **Prevents security risks** - No file system access, no dangerous globals like `eval` or `process`
* **Requires no dependencies** - No npm packages or external imports needed

<Warning>
  Custom integration code runs in a secure sandboxed environment. **You cannot
  install or import external libraries (npm, pip, etc.)** - only the built-in
  JavaScript/Node.js APIs documented here are available. For advanced processing
  (e.g., PDF parsing, image manipulation), use external APIs or services and
  call them from your integration code.
</Warning>

### Where These Utilities Are Available

The sandbox utilities are available in:

* **Custom integration actions** - Code that interacts with external APIs
* **Custom integration triggers** - Code that monitors for events
* **Authentication flows** - OAuth and API key validation code
* **Workflow code nodes** - Custom JavaScript in workflow automations. For Python, see the [Code node](/en/using-langdock/workflows/nodes/code-node) page.

## HTTP & Networking

### ld.request()

Make HTTP requests to external APIs with automatic JSON handling and error management.

**Parameters:**

```typescript theme={null}
{
  method: string;           // HTTP method: 'GET', 'POST', 'PUT', 'PATCH', 'DELETE'
  url: string;              // Full URL to request
  headers?: object;         // Request headers
  params?: object;          // URL query parameters
  body?: object | string;   // Request body (auto-stringified if object)
  responseType?: string;    // 'json', 'text', 'stream', or 'binary' for file downloads
}
```

**Returns (default):**

```typescript theme={null}
{
  status: number;           // HTTP status code
  headers: object;          // Response headers
  json: any;                // Response body parsed as JSON (undefined if not valid JSON)
  text: string;             // Response body as text
}
```

**Returns (with `responseType: "stream"` or `"binary"`):**

```typescript theme={null}
{
  status: number;           // HTTP status code
  headers: object;          // Response headers
  buffer: ArrayBuffer;      // Response body as ArrayBuffer
  success: boolean;         // true on success
}
```

<Info>
  The response shape depends on `responseType`. By default, you get `json` and `text` properties. When using `responseType: "stream"` or `"binary"`, you get a `buffer` property instead — `json` and `text` are not available in this mode.
</Info>

**Example: GET Request**

```javascript theme={null}
const options = {
  method: "GET",
  url: "https://api.example.com/users/123",
  headers: {
    Authorization: `Bearer ${data.auth.access_token}`,
    Accept: "application/json",
  },
};

const response = await ld.request(options);
return response.json;
```

**Example: POST Request with Body**

```javascript theme={null}
const options = {
  method: "POST",
  url: "https://api.example.com/tickets",
  headers: {
    Authorization: `Bearer ${data.auth.api_key}`,
    "Content-Type": "application/json",
  },
  body: {
    title: data.input.title,
    description: data.input.description,
    priority: "high",
  },
};

const response = await ld.request(options);
return {
  ticketId: response.json.id,
  url: response.json.url,
};
```

**Example: File Download**

```javascript theme={null}
const options = {
  method: "GET",
  url: `https://api.example.com/files/${data.input.fileId}/download`,
  headers: {
    Authorization: `Bearer ${data.auth.access_token}`,
  },
  responseType: "stream", // or 'binary'
};

const response = await ld.request(options);

// Convert ArrayBuffer to base64
const bytes = new Uint8Array(response.buffer);
const binaryString = String.fromCharCode(...bytes);

return {
  files: {
    fileName: "document.pdf",
    mimeType: "application/pdf",
    base64: btoa(binaryString),
  },
};
```

**Example: Form Data Upload**

```javascript theme={null}
const formData = new FormData();
formData.append("file", data.input.file.base64, data.input.file.fileName);
formData.append("description", "Uploaded via Langdock");

const options = {
  method: "POST",
  url: "https://api.example.com/upload",
  headers: {
    Authorization: `Bearer ${data.auth.access_token}`,
  },
  body: formData,
};

const response = await ld.request(options);
return response.json;
```

<Tip>
  The `body` parameter is automatically stringified if you pass an object. For
  `application/x-www-form-urlencoded` content type, the body is automatically
  converted to the appropriate format.
</Tip>

### ld.awsRequest()

Make AWS SigV4-signed requests to AWS services like S3, API Gateway, or custom AWS APIs.

**Parameters:**

```typescript theme={null}
{
  method: string;           // HTTP method
  url: string;              // AWS service URL
  headers?: object;         // Additional headers
  body?: object | string;   // Request body
  region: string;           // AWS region (e.g., 'us-east-1')
  service: string;          // AWS service name (e.g., 's3', 'execute-api')
  credentials: {            // AWS credentials
    accessKeyId: string;      // AWS access key ID
    secretAccessKey: string;  // AWS secret access key
    sessionToken?: string;    // AWS session token (for temporary credentials)
  }
}
```

**Example: S3 File Upload**

```javascript theme={null}
const options = {
  method: "PUT",
  url: `https://my-bucket.s3.us-east-1.amazonaws.com/${data.input.fileName}`,
  headers: {
    "Content-Type": data.input.mimeType,
  },
  body: Buffer.from(data.input.file.base64, "base64"),
  region: "us-east-1",
  service: "s3",
  credentials: {
    accessKeyId: data.auth.aws_access_key_id,
    secretAccessKey: data.auth.aws_secret_access_key,
  },
};

const response = await ld.awsRequest(options);
return {
  success: true,
  url: options.url,
};
```

## Data Format Conversions

### ld.csv2parquet()

Convert CSV text to Parquet format with optional compression and array support.

**Parameters:**

```typescript theme={null}
{
  csvText: string;          // CSV data as text
  compression?: string;     // 'gzip', 'snappy', 'brotli', 'lz4', 'zstd' (default), or 'uncompressed'
}
```

**Returns:** `{ base64: string, success: boolean }`

**Example:**

```javascript theme={null}
const csvText = `name,age,skills
Alice,30,"[Python,JavaScript]"
Bob,25,"[Java,Go]"`;

const result = await ld.csv2parquet(csvText, {
  compression: "gzip",
});

return {
  files: {
    fileName: "data.parquet",
    mimeType: "application/vnd.apache.parquet",
    base64: result.base64,
  },
};
```

<Info>
  CSV columns containing array-like strings (e.g., `"[Python,JavaScript]"`) are
  automatically detected and converted to Parquet List columns.
</Info>

### ld.parquet2csv()

Convert Parquet format to CSV text, handling List columns appropriately.

**Parameters:**

```typescript theme={null}
base64Parquet: string;    // Base64-encoded Parquet file
```

**Returns:** `{ base64: string, success: boolean }`

**Example:**

```javascript theme={null}
const parquetBase64 = data.input.parquetFile.base64;
const result = await ld.parquet2csv(parquetBase64);

return {
  files: {
    fileName: "data.csv",
    mimeType: "text/csv",
    base64: result.base64,
  },
};
```

### ld.arrow2parquet()

Convert Arrow IPC Stream format to Parquet.

**Parameters:**

```typescript theme={null}
{
  buffer: Buffer;           // Arrow IPC Stream buffer
  compression?: string;     // Compression type (same as csv2parquet)
}
```

**Returns:** Base64-encoded Parquet file

**Example:**

```javascript theme={null}
const arrowBuffer = Buffer.from(data.input.arrowFile.base64, "base64");

const parquetBase64 = await ld.arrow2parquet(arrowBuffer, {
  compression: "snappy",
});

return {
  files: {
    fileName: "data.parquet",
    mimeType: "application/vnd.apache.parquet",
    base64: parquetBase64,
  },
};
```

### ld.json2csv()

Convert JSON data to CSV format using the nodejs-polars library.

**Parameters:**

```typescript theme={null}
jsonData: Array<object>;  // Array of objects to convert
```

**Returns:** CSV text

**Example:**

```javascript theme={null}
const users = [
  { name: "Alice", email: "alice@example.com", age: 30 },
  { name: "Bob", email: "bob@example.com", age: 25 },
  { name: "Charlie", email: "charlie@example.com", age: 35 },
];

const csvText = await ld.json2csv(users);

return {
  files: {
    fileName: "users.csv",
    mimeType: "text/csv",
    text: csvText,
  },
};
```

## Database & SQL

### ld.validateSqlQuery()

Validate SQL query syntax to ensure it's non-empty and contains a single statement.

**Parameters:**

```typescript theme={null}
query: string;            // SQL query to validate
```

**Returns:** The trimmed query string if valid, throws error otherwise

**Example:**

```javascript theme={null}
const query = data.input.sqlQuery;

try {
  ld.validateSqlQuery(query);

  // Query is valid, proceed with execution
  const response = await ld.request({
    method: "POST",
    url: "https://api.example.com/query",
    headers: {
      Authorization: `Bearer ${data.auth.access_token}`,
    },
    body: { query },
  });

  return response.json;
} catch (error) {
  return {
    error: `Invalid SQL query: ${error.message}`,
  };
}
```

### ld.ensureReadOnlySqlQuery()

Enforce that a SQL query is read-only by checking its execution type.

**Parameters:**

```typescript theme={null}
query: string;            // SQL query to validate
```

**Returns:** The trimmed query string if read-only, throws error otherwise

**Example:**

```javascript theme={null}
const query = data.input.sqlQuery;

try {
  ld.ensureReadOnlySqlQuery(query);

  // Query is read-only, safe to execute
  const response = await ld.request({
    method: "POST",
    url: `${data.auth.database_url}/query`,
    headers: {
      Authorization: `Bearer ${data.auth.access_token}`,
    },
    body: { query },
  });

  return response.json.results;
} catch (error) {
  return {
    error: "Only read-only queries (SELECT) are allowed",
  };
}
```

<Warning>
  This function checks the query execution type to ensure it's LISTING or
  INFORMATION only. Queries that modify data (INSERT, UPDATE, DELETE) will be
  rejected.
</Warning>

## Cryptography

### ld.signWithRS256()

Create RSA-SHA256 digital signatures, commonly used for JWT signing and OAuth flows.

**Function Signature:**

```javascript theme={null}
ld.signWithRS256(data, privateKey, options)
```

**Parameters:**

* `data` (string): Data to sign
* `privateKey` (string): PEM-formatted RSA private key
* `options` (object, optional):
  * `encoding` (string): Output encoding - `'base64'` (default) or `'hex'`

**Returns:** `{ signature: string }` — object containing the signature in specified encoding

**Example: JWT Signing**

```javascript theme={null}
const header = {
  alg: "RS256",
  typ: "JWT",
};

const payload = {
  iss: "your-client-id",
  sub: "user@example.com",
  aud: "https://api.example.com",
  exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
  iat: Math.floor(Date.now() / 1000),
};

const encodedHeader = btoa(JSON.stringify(header));
const encodedPayload = btoa(JSON.stringify(payload));
const signingInput = `${encodedHeader}.${encodedPayload}`;

const result = ld.signWithRS256(signingInput, data.auth.private_key, {
  encoding: "base64",
});

const jwt = `${signingInput}.${result.signature}`;

// Use JWT for authentication
const response = await ld.request({
  method: "POST",
  url: "https://api.example.com/auth/token",
  body: {
    grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
    assertion: jwt,
  },
});

return response.json;
```

<Warning>
  The private key must be in PEM format. Invalid keys will throw user-friendly
  error messages. Never expose private keys in logs or return values.
</Warning>

## Utility Functions

### ld.log()

Output debugging information to the execution logs, visible below the "Test Action" button.

**Parameters:**

```typescript theme={null}
...args: any[];           // Any number of values to log
```

**Example:**

```javascript theme={null}
ld.log("Starting ticket creation");
ld.log("Input data:", data.input);

const response = await ld.request(options);

ld.log("Response status:", response.status);
ld.log("Ticket created:", response.json);

return response.json;
```

<Tip>
  Use `ld.log()` liberally during development to debug your integration code. Logs
  are automatically redacted to hide sensitive authentication values.
</Tip>

### ld.wait()

Pause execution for a specified number of milliseconds. Useful for rate limiting or retry logic.

**Parameters:**

```typescript theme={null}
ms: number;               // Milliseconds to wait (0-30000)
```

**Example:**

```javascript theme={null}
// Retry with delay
let retries = 3;
while (retries > 0) {
  const response = await ld.request(options);
  if (response.status === 429) {
    await ld.wait(2000); // Wait 2 seconds before retrying
    retries--;
    continue;
  }
  return response.json;
}
return { error: "Rate limit exceeded after retries" };
```

### atob() / btoa()

Base64 encoding and decoding functions, available globally without imports.

**atob()** - Decode base64 string to binary string
**btoa()** - Encode binary string to base64

**Example: Base64 URL Decoding**

```javascript theme={null}
function base64UrlDecode(base64Url) {
  // Convert base64url to standard base64
  const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
  return atob(base64);
}

const token = data.input.jwt_token;
const [header, payload, signature] = token.split(".");

const decodedPayload = JSON.parse(base64UrlDecode(payload));
ld.log("Token payload:", decodedPayload);

return decodedPayload;
```

**Example: Basic Authentication**

```javascript theme={null}
const credentials = btoa(`${data.auth.username}:${data.auth.password}`);

const response = await ld.request({
  method: "GET",
  url: "https://api.example.com/data",
  headers: {
    Authorization: `Basic ${credentials}`,
  },
});

return response.json;
```

### Buffer.from()

A minimal polyfill for converting between typed array formats. It accepts a single argument (an Array, Uint8Array, or ArrayBuffer) and returns a `Uint8Array`.

<Warning>
  This is NOT the full Node.js Buffer. It only accepts Array, Uint8Array, or ArrayBuffer — **not strings**. Passing a string like `Buffer.from("hello")` will throw an error. The returned `Uint8Array` does not have a `.toString("base64")` method. For base64 encoding/decoding, use `btoa()`/`atob()` instead.
</Warning>

**Example: Convert ArrayBuffer from ld.request()**

```javascript theme={null}
const response = await ld.request({
  method: "GET",
  url: `https://api.example.com/files/${data.input.fileId}`,
  headers: { Authorization: `Bearer ${data.auth.access_token}` },
  responseType: "stream",
});

// Convert ArrayBuffer to Uint8Array for processing
const bytes = Buffer.from(response.buffer);
```

**Example: Base64 encode a downloaded file**

```javascript theme={null}
// Use btoa() for base64 encoding since Buffer.toString() is not available
const binaryString = String.fromCharCode(...new Uint8Array(response.buffer));
const base64Data = btoa(binaryString);

return {
  files: {
    fileName: "document.pdf",
    mimeType: "application/pdf",
    base64: base64Data,
  },
};
```

### FormData

Create multipart form data for file uploads and complex request bodies.

**Example: File Upload with Metadata**

```javascript theme={null}
const formData = new FormData();
formData.append("file", data.input.file.base64, data.input.file.fileName);
formData.append("title", data.input.title);
formData.append("category", data.input.category);
formData.append("tags", JSON.stringify(data.input.tags));

const response = await ld.request({
  method: "POST",
  url: "https://api.example.com/documents",
  headers: {
    Authorization: `Bearer ${data.auth.access_token}`,
  },
  body: formData,
});

return response.json;
```

### Standard JavaScript APIs

The sandbox also provides access to standard JavaScript built-ins:

**JSON**

* `JSON.stringify()` - Convert objects to JSON strings
* `JSON.parse()` - Parse JSON strings to objects

**Date**

* `new Date()` - Create date objects
* `Date.now()` - Get current timestamp
* All standard Date methods

**Math**

* `Math.floor()`, `Math.ceil()`, `Math.round()`
* `Math.random()`, `Math.max()`, `Math.min()`
* All standard Math methods

**RegExp**

* `new RegExp()` - Create regular expressions
* String regex methods: `match()`, `replace()`, `test()`

**Array & Object**

* All standard Array methods: `map()`, `filter()`, `reduce()`, etc.
* All standard Object methods: `keys()`, `values()`, `entries()`, etc.

**Example: Data Transformation**

```javascript theme={null}
const users = data.input.users;

// Filter active users
const activeUsers = users.filter((user) => user.status === "active");

// Transform to required format
const formatted = activeUsers.map((user) => ({
  id: user.id,
  name: `${user.firstName} ${user.lastName}`,
  email: user.email.toLowerCase(),
  joinedDate: new Date(user.createdAt).toISOString().split("T")[0],
}));

// Sort by join date
formatted.sort((a, b) => new Date(b.joinedDate) - new Date(a.joinedDate));

return {
  users: formatted,
  total: formatted.length,
};
```

## Best Practices

### Error Handling

Always wrap API calls in try-catch blocks and provide helpful error messages:

```javascript theme={null}
try {
  const response = await ld.request(options);
  return response.json;
} catch (error) {
  ld.log("Error details:", error.message, error.stack);
  return {
    error: `Failed to fetch data: ${error.message}`,
  };
}
```

### Input Validation

Validate user inputs before using them:

```javascript theme={null}
// Validate required fields
if (!data.input.email || !data.input.name) {
  return {
    error: "Missing required fields: email and name are required",
  };
}

// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(data.input.email)) {
  return {
    error: "Invalid email format",
  };
}

// Validate file type
const allowedTypes = ["application/pdf", "image/jpeg", "image/png"];
if (
  data.input.file &&
  !allowedTypes.includes(data.input.file.mimeType)
) {
  return {
    error: `Unsupported file type. Allowed: ${allowedTypes.join(", ")}`,
  };
}
```

### Performance Tips

**Minimize API Calls**

```javascript theme={null}
// Bad: Multiple sequential calls
const user = await ld.request({ url: "/users/123" });
const posts = await ld.request({ url: `/users/123/posts` });
const comments = await ld.request({ url: `/users/123/comments` });

// Good: Use batch endpoints when available
const response = await ld.request({
  url: "/users/123?include=posts,comments",
});
```

**Use Pagination**

```javascript theme={null}
const allResults = [];
let page = 1;
const pageSize = 100;

while (true) {
  const response = await ld.request({
    url: "https://api.example.com/data",
    params: {
      page,
      pageSize,
    },
  });

  allResults.push(...response.json.items);

  if (response.json.items.length < pageSize) {
    break; // No more pages
  }

  page++;
}

return allResults;
```

### Security Considerations

**Never Hardcode Secrets**

```javascript theme={null}
// Bad: Hardcoded API key
const apiKey = "sk_live_abc123";

// Good: Use authentication fields
const apiKey = data.auth.api_key;
```

**Sanitize User Input**

```javascript theme={null}
// Sanitize SQL-like input
const searchTerm = data.input.search.replace(/['"]/g, "");

// Validate URL parameters
const itemId = data.input.itemId.replace(/[^a-zA-Z0-9-_]/g, "");
```

**Handle Rate Limits**

```javascript theme={null}
try {
  const response = await ld.request(options);
  return response.json;
} catch (error) {
  if (error.message.includes("429") || error.message.includes("rate limit")) {
    return {
      error: "API rate limit exceeded. Please try again in a few minutes.",
    };
  }
  throw error;
}
```

### Common Pitfalls

**1. Not Handling Async/Await Properly**

```javascript theme={null}
// Bad: Not awaiting promises
const response = ld.request(options); // Returns Promise, not response!
return response.json; // undefined

// Good: Always await
const response = await ld.request(options);
return response.json;
```

**2. Accessing Nested Properties Without Checks**

```javascript theme={null}
// Bad: Can throw if user or address is undefined
const city = response.json.user.address.city;

// Good: Use optional chaining
const city = response.json?.user?.address?.city || "Unknown";
```

**3. Not Parsing JSON Strings**

```javascript theme={null}
// Bad: Assuming string is already an object
const properties = data.input.properties; // String: '{"name":"value"}'
properties.name; // undefined

// Good: Parse JSON strings
const properties = JSON.parse(data.input.properties);
properties.name; // "value"
```

**4. Modifying Frozen Objects**

```javascript theme={null}
// Bad: Sandbox freezes global objects
Array.prototype.customMethod = () => {}; // Error!

// Good: Create new objects
const customArray = [...originalArray];
```

## Next Steps

* [Creating Custom Integrations](/en/using-langdock/guides/integrations/create-integrations) - Build your first integration
* [File Support for Actions](/en/using-langdock/guides/integrations/file-support-for-actions) - Handle file inputs and outputs
* [Integration Agent](/en/using-langdock/guides/integrations/agent) - Get help writing integration code
* [Workflow Code Node](/en/using-langdock/workflows/nodes/code-node) - Write custom code in workflows
