Integrations vs. Actions vs. Triggers

Integrations are standardized connections between Langdock and third-party tools that handle authentication and API communication. Within each integration, you can build:
  • Actions: Functions that assistants and workflows can call to interact with APIs (e.g., “create ticket”, “send email”, “get data”)
  • Triggers: Event monitors that start workflows when specific events occur (e.g., “new email received”, “file uploaded”)

Setting up an Integration

In the integrations menu, click Build integration to get started. Next, specify an integration name and upload an icon (shown in chat when using actions and in the integrations overview). Add a description to help assistants know when to use this integration. Hit Save to create it.

Authentication

Start with authentication in the Build tab. Select your authentication type and configure it following the steps below:

API Key

After selecting API Key authentication, add custom input fields in step 2 (like API key or client ID). These inputs are collected when users set up connections and can be marked as “required.” Step 3 lets you set up a test API endpoint to validate authentication. Replace the URL parameter and add references to your input fields using data.auth.fieldId. Use the built-in ld.request and ld.log functions for requests and logging. Test your action and create your first connection.

OAuth 2.0

Custom integrations support OAuth 2.0 authentication. Step 2 allows custom input fields (collected during connection setup). Client ID and Client Secret are entered in step 4, so this covers additional parameters only. Create an OAuth client Set up an OAuth client/App/Project in your target application and enable the required APIs. This is application-specific, which is why our interface supports custom code in step 5. For Google Calendar, create a Google Service Account, generate a new key to get the client ID and secret, add them to Langdock in step 4, save the OAuth Redirect URL, and enable the Google Calendar API. Change Authorization URL Check the OAuth documentation for your service and extract the Authorization URL. Usually, changing the BASE_URL in our template is sufficient. For Google Calendar:
return `https://accounts.google.com/o/oauth2/v2/auth?client_id=${env.CLIENT_ID}&response_type=code&scope=${data.input.scope}&access_type=offline&redirect_uri=${encodeURIComponent(data.input.redirectUrl)}&state=${data.input.state}&prompt=consent`;
Define Scopes Define OAuth scopes required by your actions. List them comma or space-separated according to your API documentation. For Google Calendar (space-separated):
https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile
Provide Access Token & Refresh Token URL Check your API’s OAuth docs for the Access Token URL and Refresh Token URL. Usually, updating the tokenUrl in our template works. For Google Calendar: const tokenUrl ='https://oauth2.googleapis.com/token'; Test Authentication Setup Provide a test API endpoint (like /me) to verify authentication. The return value of that test request can be used inside the OAuth Client Label to influence the naming of the established connections. You can access the return value via: {{data.input}} For Google Calendar: Google Sheets - {{data.input.useremail.value}} Test by adding a connection and verifying the authorization flow works. For Google Calendar, we test with:
url: 'https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses'

Public APIs

Choose None for publicly available APIs without authentication.

Building Actions

Actions allow assistants to interact with your API endpoints. There are two types of actions:
  • Regular Actions: Standard API interactions (create, read, update, delete operations)
  • Native Actions: Special file search and download actions that integrate with Langdock’s file system

Regular Actions

Regular actions are the most common type and handle standard API operations.

When to Build Regular Actions

  • CRUD operations: Create, read, update, or delete data via API calls
  • Data processing: Send data to APIs for analysis, transformation, or validation
  • File operations: Upload files to services, process documents, send attachments
  • Notifications: Send emails, messages, or create tickets
  • Integrations: Connect multiple services or sync data between platforms

Setting Up Regular Actions

  1. Add Action: In your integration, click “Add Action”
  2. Configure Basic Info: Set name, description, and slug
  3. Add Input Fields: Define what data the action needs from users
  4. Write Action Code: Implement the API interaction logic
  5. Test: Validate your action works correctly

Input Field Types

TypePurposeNotes
TEXTShort text inputSingle line text
MULTI_LINE_TEXTLong text inputMultiple lines, good for descriptions
NUMBERNumeric inputIntegers or decimals
BOOLEANTrue/false toggleCheckbox input
SELECTDropdown optionsPre-defined choices
FILEFile uploadSingle or multiple files (see file support guide)
OBJECTComplex dataJSON objects with custom schema
PASSWORDSensitive textHidden input for secrets

Example: Create Ticket Action

// Validate required inputs
if (!data.input.title) {
  return { error: 'Title is required' };
}

// Build request
const options = {
  method: 'POST',
  url: 'https://api.ticketing-service.com/tickets',
  headers: {
    'Authorization': `Bearer ${data.auth.api_key}`,
    'Content-Type': 'application/json',
  },
  body: {
    title: data.input.title,
    description: data.input.description || '',
    priority: data.input.priority || 'medium',
    assignee: data.input.assignee
  }
};

try {
  const response = await ld.request(options);

  if (response.status === 201) {
    return {
      success: true,
      ticketId: response.json.id,
      url: response.json.url,
      message: `Created ticket #${response.json.id}: ${data.input.title}`
    };
  } else {
    throw new Error(`API returned status ${response.status}`);
  }
} catch (error) {
  ld.log('Error creating ticket:', error.message);
  return {
    success: false,
    error: `Failed to create ticket: ${error.message}`
  };
}

File Upload Example

// Handle file uploads (requires FILE input field)
const document = data.input.document; // FileData object

if (!document) {
  return { error: 'Please attach a document' };
}

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

const options = {
  method: 'POST',
  url: 'https://api.example.com/documents',
  headers: {
    'Authorization': `Bearer ${data.auth.api_key}`,
    'Content-Type': 'application/json'
  },
  body: {
    filename: document.fileName,
    content: document.base64,
    mimeType: document.mimeType
  }
};

const response = await ld.request(options);
return {
  success: true,
  documentId: response.json.id,
  message: `Uploaded ${document.fileName} successfully`
};

Returning Files from Actions

Actions can also generate and return files:
// Generate CSV export
const data = await fetchCustomerData();

const csvHeader = 'Name,Email,Created';
const csvRows = data.map(customer =>
  `"${customer.name}","${customer.email}","${customer.created}"`
);
const csvContent = [csvHeader, ...csvRows].join('\n');

return {
  files: {
    fileName: `customers-${new Date().toISOString().slice(0,10)}.csv`,
    mimeType: 'text/csv',
    text: csvContent  // Use 'text' for UTF-8 content, 'base64' for binary
  },
  success: true,
  exported: data.length
};

Building Triggers

Triggers monitor external systems for events and can start workflows automatically.

When to Build Triggers

  • Event monitoring: Detect new emails, files, records, or changes
  • Workflow automation: Start processes when specific events occur
  • Data synchronization: Keep systems in sync by detecting changes
  • Notifications: React to external events and notify users

Trigger Types

  • Polling Triggers: Periodically check APIs for new events
  • Webhook Triggers: Receive real-time notifications from external systems

Setting Up Polling Triggers

  1. Add Trigger: In your integration, click “Add Trigger”
  2. Choose Type: Select “Polling”
  3. Configure Settings: Set name, description, and polling interval
  4. Add Input Fields: Define configuration parameters (optional)
  5. Write Trigger Code: Implement the polling logic
  6. Test: Validate your trigger detects events correctly

Required Return Format

Triggers must return an array of events with this structure:
return [
  {
    id: "unique_event_id",        // Required: Unique identifier
    timestamp: "2024-01-15T...",  // Required: Event timestamp (ISO string)
    data: {
      // Your event data here
      eventType: "new_email",
      subject: "Important message",
      from: "sender@example.com",
      // ... other event properties
    }
  }
];

Example: New Email Trigger

// Fetch recent emails
const options = {
  method: 'GET',
  url: 'https://api.email-service.com/messages',
  headers: {
    'Authorization': `Bearer ${data.auth.access_token}`,
  },
  params: {
    since: new Date(Date.now() - 60 * 60 * 1000).toISOString(), // Last hour
    limit: 10
  }
};

try {
  const response = await ld.request(options);
  const emails = response.json.messages || [];

  // Transform to required format
  const results = emails.map(email => ({
    id: email.id,
    timestamp: email.receivedAt,
    data: {
      messageId: email.id,
      subject: email.subject,
      from: email.from,
      to: email.to,
      body: email.body,
      isRead: email.isRead
    }
  }));

  return results;
} catch (error) {
  ld.log('Error fetching emails:', error.message);
  return [];
}

Triggers with File Attachments

When triggers detect events with files, include them in the data object:
const results = [];

for (const email of emails) {
  const attachments = [];

  // Process email attachments
  if (email.attachments && email.attachments.length > 0) {
    for (const attachment of email.attachments) {
      const content = await downloadAttachment(attachment.id);

      attachments.push({
        fileName: attachment.filename,
        mimeType: attachment.mimeType,
        base64: content.toString('base64')
      });
    }
  }

  results.push({
    id: email.id,
    timestamp: email.receivedAt,
    data: {
      subject: email.subject,
      from: email.from,
      body: email.body,
      files: attachments  // Files go inside data object
    }
  });
}

return results;

Setting Up Webhook Triggers

  1. Add Trigger: Choose “Webhook” type
  2. Configure Endpoint: Note the provided webhook URL
  3. Set Up External System: Configure your service to send events to the webhook URL
  4. Write Processing Code: Transform incoming webhook data (optional)
  5. Test: Send test events to verify functionality

Webhook Processing Example

// Transform incoming webhook data
const webhookData = data.input.webhookPayload;

return [
  {
    id: webhookData.event_id || crypto.randomUUID(),
    timestamp: webhookData.timestamp || new Date().toISOString(),
    data: {
      eventType: webhookData.type,
      resourceId: webhookData.resource_id,
      action: webhookData.action,
      changes: webhookData.changes,
      // Transform webhook format to your preferred structure
    }
  }
];

Native Actions

Native actions allow you to natively search and download files that aren’t stored locally on a user’s device. We’ve already built native actions for SharePoint, OneDrive, Google Drive, and Confluence. You can access these via the Select files button to search and attach files directly to Chat or Assistant Knowledge. Native Actions Chat Attach files to the chat using native integrations. Native Actions Knowledge Attach files to the Assistant knowledge by using a native integration. Building native actions for other tools enables you to search and download files from those platforms in the same way.

Setting up a Native Action

To set up a native action, begin building your integration as usual. Add another action, and in Step 1 under Advanced, select either “Search files” or “Download file” as the action type. Afterwards, you build the action as any other action, but your function needs to return a specific object structure. This ensures compatibility and enables assistants to handle files and search results correctly.

Required Output Format

Depending on the action you select, your function must return a specific object structure. This ensures compatibility and enables assistants to handle files and search results correctly. Search files: When building a native search integration, your function must return an array of objects matching the following schema:
{
  url: string,
  documentId: string,
  title: string,
  author?: {
    id: string,
    name: string,
    imgUrl?: string,
  },
  mimeType: string,
  lastSeenByUser: Date,
  createdDate: Date,
  lastModifiedByAnyone: Date,
  lastModifiedByUserId?: {
    id?: string,
    name?: string,
    lastModifiedByUserIdDate: Date,
  },
  parent?: {
    id: string,
    title?: string,
    url?: string,
    type?: string,
    driveId?: string,
    siteId?: string,
    listId?: string,
    listItemId?: string,
  }
}
The title and mimeType will be displayed in the UI for all search results. Please also check out a detailed description for each parameter: Below you can find an example implementation for the native SharePoint Search files action. Download file
For native download actions, return an object in the following format:
{
  data: response.data,
  fileName: string,
  mimeType: string,
  buffer: response.buffer, // Conditional
  url: string,
  lastModified: Date,
  text: string // Conditional
}
The fileName and mimeType will be displayed in the UI for all search results. Please also check out a detailed description for each parameter: Below you can find an example implementation for the native SharePoint Download file action.

Accessing Input Fields

Use data.input.{inputFieldId} for input field values and data.auth.{authenticationFieldId} for authentication field values from the user’s current connection.

Built-in Functions for Custom Code Sections

Use our Integration Assistant to help set up your integration functions.

Basic Functions

The custom client library ld contains the core functionality needed for implementing authentication flows. ld.request() creates a web request using the format specified by the passed object and returns the response. The parameters must be valid JSON objects since JSON.stringify() is called internally. For complex JSON objects, you’ll need to apply JSON.parse() first. ld.log() outputs the passed values to the Logs field, which appears below the Test Action button after running an action. Example usage from the Google Calendar integration:
const options = {
  method: 'GET',
  url: `https://www.googleapis.com/calendar/v3/calendars/${data.input.calendarId}/events/${data.input.eventId}`,
  headers: {
    'Authorization': 'Bearer ' + data.auth.access_token,
    'Accept': 'application/json',
  },
};

const response = await ld.request(options);

return response.json;

JSON

Both JSON.stringify() (converting a JavaScript JSON object into a string) and JSON.parse() (converting a string into a JavaScript JSON object) are available in custom code blocks. The parameters passed to the ld.request() function must be valid JSON objects since JSON.stringify() is called internally. For complex JSON structures, you’ll need to apply JSON.parse() beforehand to ensure proper formatting. Example usage from the HubSpot integration:
const properties = data.input.properties
  ? JSON.parse(data.input.properties)
  : {};

const options = {
  method: "PATCH",
  url: `https://api.hubapi.com/crm/v3/objects/companies/${data.input.companyId}`,
  headers: {
    Authorization: "Bearer " + data.auth.access_token,
    "Content-Type": "application/json",
  },
  body: {
    properties: properties,
  },
};

Base 64

Since some OAuth clients require Base64-encoded client credentials for Basic authentication, we’ve built a helper function for this. btoa() encodes a string to Base64, while atob() decodes a Base64 string back to plain text. Example usage:
const auth = btoa(`${env.CLIENT_ID}:${env.CLIENT_SECRET}`);

Sandbox Library Restrictions

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

Best Practices

Action Design

  • Single responsibility: Each action should do one thing well
  • Clear naming: Use descriptive action names that explain the purpose
  • Input validation: Always validate required inputs and provide helpful error messages
  • Error handling: Catch and handle API errors gracefully
  • Logging: Use ld.log() to help with debugging

ID Handling

Most API calls require specific internal IDs. The challenge is that assistants can’t guess these IDs, which creates a poor user experience when calling actions like “get specific contact in HubSpot” or “add event to specific calendar in Google Calendar.” The solution: Create helper actions that retrieve and return these IDs to the assistant first. For example, our Get deal context function for HubSpot uses GET endpoints to gather internal IDs for available pipelines and stages. This enables assistants to use actions like Create deal or Update deal much more effectively since they now have the required context.

Performance

  • Minimize API calls: Batch operations when possible
  • Use pagination: Handle large datasets appropriately
  • Timeout handling: Set appropriate timeouts for external API calls

Security

  • Validate inputs: Never trust user input without validation
  • Sanitize data: Clean data before sending to external APIs
  • Handle secrets: Use authentication fields for sensitive data, never hardcode
  • Rate limiting: Respect API rate limits and implement backoff strategies