# nodes

node_type: n8n-nodes-omnara.omnara
package_name: n8n-nodes-omnara
display_name: Omnara
description: Send messages to users via Omnara - a web and mobile platform for human-AI communication
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: ksarangmath
author_github_url: https://github.com/omnara-ai/omnara.git
npm_package_name: n8n-nodes-omnara
npm_version: 0.1.7
community_fetched_at: 2026-04-16T09:43:36.311Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "message",
      "options": [
        {
          "name": "Message",
          "value": "message",
          "description": "Send messages to Omnara agents"
        },
        {
          "name": "Session",
          "value": "session",
          "description": "Manage agent sessions"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "send",
      "options": [
        {
          "name": "Send",
          "value": "send",
          "action": "Send message",
          "description": "Send a non-blocking message to the user (for status updates, progress reports, or sharing your thought process). Use this when you want to inform the user but do not need their response to continue"
        },
        {
          "name": "Send and Wait",
          "value": "sendAndWait",
          "action": "Send message and wait for response",
          "description": "Send a message and wait for the user's response (for questions, approvals, or conversations). Use this when you need the user's input to proceed with your task"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "message"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "end",
      "options": [
        {
          "name": "End",
          "value": "end",
          "action": "End agent session",
          "description": "End an agent session"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "session"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Send",
      "value": "send",
      "action": "Send message",
      "description": "Send a non-blocking message to the user (for status updates, progress reports, or sharing your thought process). Use this when you want to inform the user but do not need their response to continue",
      "resource": "message"
    },
    {
      "name": "Send and Wait",
      "value": "sendAndWait",
      "action": "Send message and wait for response",
      "description": "Send a message and wait for the user's response (for questions, approvals, or conversations). Use this when you need the user's input to proceed with your task",
      "resource": "message"
    },
    {
      "name": "End",
      "value": "end",
      "action": "End agent session",
      "description": "End an agent session",
      "resource": "session"
    }
  ]

npm_readme:
  # n8n-nodes-omnara

  The best human-in-the-loop node for n8n, built for seamless human-agent interaction. Perfect for approval workflows and real-time agent conversations.

  [Omnara](https://omnara.com) lets you monitor and guide AI agents without the noise. View your agent's full thought process through our web and mobile apps with smart notifications - only get alerted when your input is needed. Need urgent responses? Enable email and SMS notifications with zero setup. Stay in control while automation runs.

  ## Installation

  ### Community Node (Recommended)

  ```bash
  npm install n8n-nodes-omnara
  ```

  Or in n8n, go to **Settings** > **Community Nodes** and search for `n8n-nodes-omnara`.

  ### Manual Installation

  1. Clone or download this repository
  2. In the repository folder, run:
     ```bash
     npm install
     npm run build
     ```
  3. Copy the `dist` folder to your n8n custom nodes folder
  4. Restart n8n

  ### Development Installation

  For development and testing:

  ```bash
  # Install dependencies
  npm install

  # Build the node
  npm run build

  # For development with auto-rebuild
  npm run dev

  # Link to n8n for testing
  npm link
  # In your n8n installation:
  npm link n8n-nodes-omnara
  ```

  ## Getting Started

  ### 1. Get Your API Key

  1. Go to [Omnara Dashboard](https://omnara.com/dashboard)
  2. Click on your email in the bottom left corner
  3. Click "API Keys"
  4. Create a new API key and copy it

  ### 2. Add Omnara Credentials in n8n

  1. In n8n, go to Credentials → Add Credential → Omnara API
  2. Paste your API key
  3. Leave the API URL as default (unless you have a custom deployment)
  4. Save the credentials

  ### 3. Important: Agent Instance ID and Agent Type

  **Every Omnara node in your workflow needs:**
  - **Agent Instance ID**: A unique UUID for each workflow run (same across all nodes in one run)
  - **Agent Type**: The name of your agent on the Omnara dashboard (same across all nodes)

  ⚠️ **Critical**: Use the SAME agent instance ID and agent type across ALL Omnara nodes in a single workflow run!

  ## Workflow Setup

  ### Option A: Trigger with Webhook (Recommended)

  This lets you start workflows from the Omnara dashboard.

  **Step 1: Set up n8n Webhook**
  1. Add a **Webhook** node as your trigger
  2. Set it to **POST** method
  3. Copy the webhook URL (looks like: `https://your-n8n.com/webhook/xxx`)

  **Step 2: Configure Omnara Dashboard**
  1. Go to [Omnara Dashboard](https://omnara.com/dashboard)
  2. Click the **+ New Instance** button (top left)
  3. Paste your webhook URL
  4. Create your agent type
  5. Send a message to trigger your workflow

  **Step 3: Use Webhook Data in Omnara Nodes**
  The webhook provides these values - use them in ALL your Omnara nodes:
  - **Agent Instance ID**: `{{ $('Webhook').item.json.body.agent_instance_id }}`
  - **Agent Type**: `{{ $('Webhook').item.json.body.agent_type }}`

  The webhook also provides:
  - **Prompt**: `{{ $('Webhook').item.json.body.prompt }}` - This is the message you entered in Omnara dashboard. Use it as input for your workflow if needed.

  ### Option B: Trigger Without Webhook

  For workflows triggered by other events (cron, manual, other apps).

  **Requirements:**
  1. Generate a unique UUID for Agent Instance ID (use Expression: `{{ $uuid() }}`)
  2. Choose an agent type name (e.g., "customer_support", "code_reviewer")
  3. Store these in Set node variables
  4. Use the SAME values across all Omnara nodes in the workflow

  **Example Setup:**
  ```
  Trigger → Set Node (store UUID & agent type) → Your Workflow → Omnara Nodes
  ```

  In Set node:
  - `agent_instance_id`: `{{ $uuid() }}`
  - `agent_type`: "your_agent_name"

  Then reference in all Omnara nodes:
  - **Agent Instance ID**: `{{ $('Set').item.json.agent_instance_id }}`
  - **Agent Type**: `{{ $('Set').item.json.agent_type }}`

  ## Operations

  ### Message Resource

  #### Send Message (Non-blocking)
  Send status updates and progress reports to users without waiting for a response. Perfect for AI agents to share their thought process, progress updates, or informational messages while continuing to work.

  **Parameters:**
  - **Agent Instance ID**: The ID of the agent instance (creates new if doesn't exist)
  - **Agent Type**: Type of agent (e.g., 'claude_code', 'cursor')
  - **Message**: Status update or informational message (workflow continues immediately)
  - **Additional Options**:
    - Send Email/SMS/Push: Notification preferences

  #### Send and Wait (Blocking)
  Send a question or request and WAIT for the user's response. Essential for AI agents that need human input, approvals, or answers to continue their task. The workflow pauses until the user responds via the Omnara web/mobile app.

  **Parameters:**
  - **Agent Instance ID**: The ID of the agent instance
  - **Agent Type**: Type of agent (e.g., 'claude_code', 'cursor')
  - **Message**: Question or request that requires user response (workflow pauses until answered)
  - **Wait Options**:
    - Timeout: How long to wait (default 24 hours)
    - **Sync Mode (For AI Agents)**: Enable synchronous polling mode for AI Agent compatibility
      - Sync Timeout: Max wait time in sync mode (default 2 hours, max 48 hours)
      - Poll Interval: How often to check for responses (default 5 seconds)
  - **Additional Options**: Same as Send Message

  **How it works:**

  *Normal Mode (for regular workflows):*
  1. Sends message to Omnara with `requires_user_input: true`
  2. Generates a webhook URL and stores it with the message
  3. Pauses workflow execution
  4. When user responds in Omnara dashboard, workflow resumes with the response

  *Sync Mode (for AI Agent tools):*
  1. Sends message to Omnara
  2. Polls for responses every 5 seconds
  3. Returns immediately when response received
  4. Works within AI Agent execution context (avoids async/await issues)

  ### Session Resource

  #### End Session
  End an agent session and mark it as completed.

  **Parameters:**
  - **Agent Instance ID**: The ID of the agent instance session to end

  ## Usage Examples

  ### Basic Message Send
  ```javascript
  // Send a status update to your agent
  {
    "resource": "message",
    "operation": "send",
    "agentInstanceId": "agent-123",
    "content": "Build completed successfully"
  }
  ```

  ### Approval Workflow (Regular)
  ```javascript
  // Ask for approval and wait for response
  {
    "resource": "message",
    "operation": "sendAndWait",
    "agentInstanceId": "agent-456",
    "agentType": "claude_code",
    "message": "Deploy to production? Please approve or decline.",
    "options": {
      "sendEmail": true,
      "sendPush": true
    }
  }
  ```

  ### AI Agent Tool Usage (Sync Mode)
  ```javascript
  // Use sync mode when calling from AI Agent tools
  {
    "resource": "message",
    "operation": "sendAndWait",
    "agentInstanceId": "agent-789",
    "agentType": "claude_code",
    "message": "What changes should I make to the database schema?",
    "options": {
      "syncMode": true,
      "syncTimeout": 600,  // Wait up to 10 minutes
      "pollInterval": 5,   // Check every 5 seconds
      "sendEmail": true,
      "sendPush": true
    }
  }
  ```

  ### End Session
  ```javascript
  // Mark agent session as completed
  {
    "resource": "session",
    "operation": "end",
    "agentInstanceId": "agent-789"
  }
  ```

  ## Agent Tool Support

  This node is configured with `usableAsTool: true`, making it available as a tool for AI agents in n8n. 

  **Important**: When using Send and Wait as an AI Agent tool, you MUST enable **Sync Mode** in the options. This solves the async/await limitations of AI Agents in n8n.

  AI Agents can:
  - Send status updates
  - Ask for human input and wait for responses (using Sync Mode)
  - Manage agent sessions

  ### Why Sync Mode?
  Regular Send and Wait uses `putExecutionToWait()` which doesn't work properly in AI Agent context due to n8n's architecture. Sync Mode polls for responses synchronously, allowing AI Agents to wait for human responses without timing out or returning prematurely.

  ## Webhook Integration

  The Send and Wait operation uses n8n's built-in webhook functionality:
  - Webhook URLs are automatically generated by n8n
  - Omnara stores the webhook URL and triggers it when users respond
  - Workflows resume with the user's response data
  - Supports timeout configuration (max 7 days)

  ## Error Handling

  The node includes comprehensive error handling:
  - Authentication failures
  - Invalid agent instance IDs
  - Timeout handling for wait operations
  - Network errors and API issues

  All operations support the "Continue On Fail" option in n8n.

  ## Development

  ### Building
  ```bash
  npm run build
  ```

  ### Linting
  ```bash
  npm run lint
  npm run lint:fix
  ```

  ### Code Formatting
  ```bash
  npm run format
  ```

  ## Resources

  - [Omnara GitHub](https://github.com/omnara-ai/omnara)
  - [n8n Documentation](https://docs.n8n.io)
  - [n8n Community Nodes](https://docs.n8n.io/integrations/community-nodes/)

  ## License

  MIT

  ## Support & Feedback

  Our mission is to make Omnara the best platform for human-in-the-loop workflows and human-AI collaboration. We're actively improving and would love your input!

  - **Issues & feature requests**: [GitHub Issues](https://github.com/omnara-ai/omnara/issues) or contact@omnara.com
  - **Questions & support**: contact@omnara.com

  Share your ideas on how we can make human-AI interaction better!

ai_documentation_summary:
  {"purpose":"This n8n node enables human-in-the-loop workflows by allowing AI agents to send messages to users via the Omnara platform for approvals, feedback, or status updates.","capabilities":["Send non-blocking status updates and progress reports","Send blocking messages requiring user response with wait functionality","Support for email, SMS, and push notifications","Synchronous polling mode for AI Agent compatibility","Session management (start and end)","Webhook-triggered workflows from Omnara dashboard"],"authentication":"API Key required (configured via n8n Credentials)","commonUseCases":["Human approval workflows","AI agent feedback collection","Real-time human-agent interaction","Progress reporting to users","Customer support agent monitoring"],"limitations":["Requires consistent Agent Instance ID and Agent Type across all nodes in a single workflow run","Blocking mode pauses workflow execution until user responds","Requires manual UUID generation for non-webhook triggered workflows","Sync mode has configurable timeout limits (max 48 hours)"],"relatedNodes":["Webhook","Set","Credentials","Expression"]}


node_type: @2chat/n8n-nodes-twochat.twoChat
package_name: @2chat/n8n-nodes-twochat
display_name: 2Chat
description: Sends data to 2Chat
category: Community
development_style: declarative
version: 0.4.2
properties_schema: []
operations: []
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: 2chat_integrations
author_github_url: https://github.com/2ChatCo/2chat-n8n.git
npm_package_name: @2chat/n8n-nodes-twochat
npm_version: 0.4.2
community_fetched_at: 2026-04-16T09:43:36.312Z
ai_summary_generated_at: 2026-03-25 22:51:43
npm_readme:
  # n8n-nodes-twochat

  This is an n8n community node. It lets you use 2Chat WhatsApp API in your n8n workflows.

  2Chat is a conversational commerce automation platform that makes WhatsApp Messenger and WhatsApp Business programmable through a unified API.

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.

  [Installation](#installation)  
  [Operations](#operations)  
  [Credentials](#credentials)  
  [Compatibility](#compatibility)  
  [Resources](#resources)  

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ```
  npm install n8n-nodes-twochat
  ```

  ## Operations

  ### Message Resource
  - **Send Message**: Send WhatsApp messages to individual contacts
    - Support for text messages
    - Support for media attachments (images, documents, etc.)
    - Support for location pins

  - **Send Message to group**: Send WhatsApp messages to groups
  	- Support for text messages
    - Support for media attachments (images, documents, etc.)
    - Support for location pins

  ### Phone Number Resource
  - **Check Number**: Verify if a phone number is registered on WhatsApp
    - Get detailed information about the number (region, carrier, timezone)
    - Check if the number is a business account
    - Get business profile information when available

  ### WABA Resource
  - **Send Message**: Send WABA messages to individual contacts
  	- Support for text messages

  - **Send Template Message**: Send WABA template messages to individual contacts
    - Support for template messages

  ## Triggers

  - **WhatsApp Message Events:** Listen for message events
    - New Message: Triggers when a new message is either sent or received on 2Chat. Works for messages sent by agents or your users/customers.
    - Message Received: Triggers when a WhatsApp message is received on your 2Chat connected number.
    - Message Sent: Triggers when a WhatsApp message is sent from your 2Chat connected number.
    - New Conversation: Triggers when a new WhatsApp conversation is started within the specified time period.
    - Message Edited: Triggers when a message is edited. It will also trigger for group messages being edited.
    - Reaction Received: Triggers when a WhatsApp message is reacted to.

  - **WhatsApp Business API Events**: Listen for WhatsApp Business API events
  	- WABA Message Received: Triggers when a new WABA message is received
  	- WABA Message Sent: Triggers when a new WABA message is sent

  - **WhatsApp Calls**: Listen for WhatsApp calls events
  	- Call Made: Triggers when a new WhatsApp call is made
  	- Call Received: Triggers when a new WhatsApp call is received or missed on your connected number

  - **WhatsApp group**: Listen for WhatsApp group events
    - Message Received: Triggers when a new WhatsApp Group Message is received on the configured group
    - Participant Join: Triggers when someone joins a group
    - Participant Left: Triggers when someone leaves a group
    - Participant Removed: Triggers when someone is kicked/removed from a group
    - Message Reaction Received: Triggers when a group participant reacts to a message

  - **Phone Calls**: Listen for phone calls events
  	- Incoming completed: Triggers when an incoming phone call is completed (answered or missed)
  	- Oubound Completed: Triggers when an outbound phone call is completed (answered or failed)
  	- Status Update: Triggers when a phone call status changes (e.g., ringing, answered, ended)

  ## Credentials

  You need to create a 2Chat account and get an API key to use this node.

  1. Create a 2Chat account at [2Chat.co](https://2chat.co)
  2. Connect a WhatsApp channel in the channels section
  3. Get an API key from the developers section
  4. Use this API key in the n8n credentials for the 2Chat node

  ## Compatibility

  - Requires n8n version 1.0.0 or later

  ## Resources

  * [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
  * [2Chat API Documentation](https://developers.2chat.co/docs/intro)

ai_documentation_summary:
  {"purpose":"This n8n community node enables integration with the 2Chat WhatsApp API to send messages, manage phone numbers, handle WABA resources, and listen to various WhatsApp and call events within automated workflows.","capabilities":["Send text messages to individual contacts","Send media attachments (images, documents) to contacts","Send location pins","Send messages to WhatsApp groups","Check if a phone number is registered on WhatsApp","Get detailed phone number information (region, carrier, timezone)","Check if a number is a business account","Retrieve business profile information","Send WABA template messages","Listen for new message events (sent/received/edited)"],"authentication":"API key required (2Chat API key obtained from 2Chat.co developers section)","commonUseCases":["Automated customer support via WhatsApp","E-commerce order notifications and updates","Marketing campaigns and promotional messages","Lead verification and phone number validation","Automated group management and announcements"],"limitations":["Requires a paid 2Chat account and API key","Requires n8n version 1.0.0 or later","Community node status may imply limited official support compared to official nodes"],"relatedNodes":["n8n HTTP Request","n8n Webhook","n8n Function Item"]}


node_type: n8n-nodes-linked-api.linkedApi
package_name: n8n-nodes-linked-api
display_name: Linked API
description: Control your LinkedIn accounts and retrieve real-time data.
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: linked-api
author_github_url: https://github.com/Linked-API/
npm_package_name: n8n-nodes-linked-api
npm_version: 1.0.4
community_fetched_at: 2026-04-16T09:43:36.313Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "standard",
      "options": [
        {
          "name": "Standard Interface",
          "value": "standard"
        },
        {
          "name": "Sales Navigator",
          "value": "salesNavigator"
        },
        {
          "name": "Other",
          "value": "other"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "searchCompanies",
      "options": [
        {
          "name": "Check Connection Status",
          "value": "checkConnectionStatus",
          "action": "Check connection status",
          "description": "Check the connection status between your account and another person"
        },
        {
          "name": "Comment on Post",
          "value": "commentOnPost",
          "action": "Comment on post",
          "description": "Leave a comment on a LinkedIn post"
        },
        {
          "name": "Create Post",
          "value": "createPost",
          "action": "Create post",
          "description": "Create a new LinkedIn post"
        },
        {
          "name": "Fetch Company",
          "value": "fetchCompany",
          "action": "Fetch company",
          "description": "Fetch detailed information about a LinkedIn company"
        },
        {
          "name": "Fetch Person",
          "value": "fetchPerson",
          "action": "Fetch person",
          "description": "Fetch detailed information about a LinkedIn person"
        },
        {
          "name": "Fetch Post",
          "value": "fetchPost",
          "action": "Fetch post",
          "description": "Retrieve detailed information about a LinkedIn post"
        },
        {
          "name": "React to Post",
          "value": "reactToPost",
          "action": "React to post",
          "description": "React to a LinkedIn post with like, celebrate, support, love, insightful, or funny"
        },
        {
          "name": "Remove Connection",
          "value": "removeConnection",
          "action": "Remove connection",
          "description": "Remove a person from your connections"
        },
        {
          "name": "Retrieve Connections",
          "value": "retrieveConnections",
          "action": "Retrieve connections",
          "description": "Retrieve your connections with optional filtering"
        },
        {
          "name": "Retrieve Pending Requests",
          "value": "retrievePendingRequests",
          "action": "Retrieve pending requests",
          "description": "Retrieve pending connection requests sent from your account"
        },
        {
          "name": "Retrieve Performance",
          "value": "retrievePerformance",
          "action": "Retrieve performance",
          "description": "Retrieve performance analytics from your LinkedIn dashboard"
        },
        {
          "name": "Retrieve SSI",
          "value": "retrieveSSI",
          "action": "Retrieve SSI",
          "description": "Retrieve your current Social Selling Index (SSI) score"
        },
        {
          "name": "Search Companies",
          "value": "searchCompanies",
          "action": "Search companies",
          "description": "Search for companies on LinkedIn"
        },
        {
          "name": "Search People",
          "value": "searchPeople",
          "action": "Search people",
          "description": "Search for people on LinkedIn"
        },
        {
          "name": "Send Connection Request",
          "value": "sendConnectionRequest",
          "action": "Send connection request",
          "description": "Send a connection request to a person"
        },
        {
          "name": "Send Message",
          "value": "sendMessage",
          "action": "Send message",
          "description": "Send a message to a LinkedIn person"
        },
        {
          "name": "Sync Conversation",
          "value": "syncConversation",
          "action": "Sync conversation",
          "description": "Sync a conversation so you can start polling it for new messages"
        },
        {
          "name": "Withdraw Connection Request",
          "value": "withdrawConnectionRequest",
          "action": "Withdraw connection request",
          "description": "Withdraw a connection request sent to a person"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "standard"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "nvSearchCompanies",
      "options": [
        {
          "name": "Fetch Company in Sales Navigator",
          "value": "nvFetchCompany",
          "action": "Fetch company in Sales Navigator",
          "description": "Fetch detailed information about a company in Sales Navigator"
        },
        {
          "name": "Fetch Person in Sales Navigator",
          "value": "nvFetchPerson",
          "action": "Fetch person in Sales Navigator",
          "description": "Fetch detailed information about a person in Sales Navigator"
        },
        {
          "name": "Search Companies in Sales Navigator",
          "value": "nvSearchCompanies",
          "action": "Search companies in Sales Navigator",
          "description": "Search for companies in Sales Navigator"
        },
        {
          "name": "Search People in Sales Navigator",
          "value": "nvSearchPeople",
          "action": "Search people in Sales Navigator",
          "description": "Search for people in Sales Navigator"
        },
        {
          "name": "Send Message in Sales Navigator",
          "value": "nvSendMessage",
          "action": "Send message in Sales Navigator",
          "description": "Send a message to a person in Sales Navigator"
        },
        {
          "name": "Sync Conversation in Sales Navigator",
          "value": "nvSyncConversation",
          "action": "Sync conversation in Sales Navigator",
          "description": "Sync a conversation in Sales Navigator so you can start polling it"
        }
      ],
      "displayName": "Operations",
      "displayOptions": {
        "show": {
          "resource": [
            "salesNavigator"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "customWorkflow",
      "options": [
        {
          "name": "Custom Workflow",
          "value": "customWorkflow",
          "action": "Execute custom workflow",
          "description": "Execute a custom multi-step workflow using raw workflow definition"
        },
        {
          "name": "Get Workflow Result",
          "value": "getWorkflowResult",
          "action": "Get workflow result",
          "description": "Get the result of workflow execution"
        },
        {
          "name": "Cancel Workflow",
          "value": "cancelWorkflow",
          "action": "Cancel workflow",
          "description": "Cancel a running workflow execution"
        },
        {
          "name": "Poll Conversations",
          "value": "pollConversations",
          "action": "Poll conversations",
          "description": "Poll multiple conversations for new messages"
        },
        {
          "name": "Actions Usage Statistics",
          "value": "actionsUsageStatistics",
          "action": "Get actions statistics",
          "description": "Get actions usage statistics for a date range"
        }
      ],
      "displayName": "Operations",
      "displayOptions": {
        "show": {
          "resource": [
            "other"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Check Connection Status",
      "value": "checkConnectionStatus",
      "action": "Check connection status",
      "description": "Check the connection status between your account and another person",
      "resource": "standard"
    },
    {
      "name": "Comment on Post",
      "value": "commentOnPost",
      "action": "Comment on post",
      "description": "Leave a comment on a LinkedIn post",
      "resource": "standard"
    },
    {
      "name": "Create Post",
      "value": "createPost",
      "action": "Create post",
      "description": "Create a new LinkedIn post",
      "resource": "standard"
    },
    {
      "name": "Fetch Company",
      "value": "fetchCompany",
      "action": "Fetch company",
      "description": "Fetch detailed information about a LinkedIn company",
      "resource": "standard"
    },
    {
      "name": "Fetch Person",
      "value": "fetchPerson",
      "action": "Fetch person",
      "description": "Fetch detailed information about a LinkedIn person",
      "resource": "standard"
    },
    {
      "name": "Fetch Post",
      "value": "fetchPost",
      "action": "Fetch post",
      "description": "Retrieve detailed information about a LinkedIn post",
      "resource": "standard"
    },
    {
      "name": "React to Post",
      "value": "reactToPost",
      "action": "React to post",
      "description": "React to a LinkedIn post with like, celebrate, support, love, insightful, or funny",
      "resource": "standard"
    },
    {
      "name": "Remove Connection",
      "value": "removeConnection",
      "action": "Remove connection",
      "description": "Remove a person from your connections",
      "resource": "standard"
    },
    {
      "name": "Retrieve Connections",
      "value": "retrieveConnections",
      "action": "Retrieve connections",
      "description": "Retrieve your connections with optional filtering",
      "resource": "standard"
    },
    {
      "name": "Retrieve Pending Requests",
      "value": "retrievePendingRequests",
      "action": "Retrieve pending requests",
      "description": "Retrieve pending connection requests sent from your account",
      "resource": "standard"
    },
    {
      "name": "Retrieve Performance",
      "value": "retrievePerformance",
      "action": "Retrieve performance",
      "description": "Retrieve performance analytics from your LinkedIn dashboard",
      "resource": "standard"
    },
    {
      "name": "Retrieve SSI",
      "value": "retrieveSSI",
      "action": "Retrieve SSI",
      "description": "Retrieve your current Social Selling Index (SSI) score",
      "resource": "standard"
    },
    {
      "name": "Search Companies",
      "value": "searchCompanies",
      "action": "Search companies",
      "description": "Search for companies on LinkedIn",
      "resource": "standard"
    },
    {
      "name": "Search People",
      "value": "searchPeople",
      "action": "Search people",
      "description": "Search for people on LinkedIn",
      "resource": "standard"
    },
    {
      "name": "Send Connection Request",
      "value": "sendConnectionRequest",
      "action": "Send connection request",
      "description": "Send a connection request to a person",
      "resource": "standard"
    },
    {
      "name": "Send Message",
      "value": "sendMessage",
      "action": "Send message",
      "description": "Send a message to a LinkedIn person",
      "resource": "standard"
    },
    {
      "name": "Sync Conversation",
      "value": "syncConversation",
      "action": "Sync conversation",
      "description": "Sync a conversation so you can start polling it for new messages",
      "resource": "standard"
    },
    {
      "name": "Withdraw Connection Request",
      "value": "withdrawConnectionRequest",
      "action": "Withdraw connection request",
      "description": "Withdraw a connection request sent to a person",
      "resource": "standard"
    },
    {
      "name": "Fetch Company in Sales Navigator",
      "value": "nvFetchCompany",
      "action": "Fetch company in Sales Navigator",
      "description": "Fetch detailed information about a company in Sales Navigator",
      "resource": "salesNavigator"
    },
    {
      "name": "Fetch Person in Sales Navigator",
      "value": "nvFetchPerson",
      "action": "Fetch person in Sales Navigator",
      "description": "Fetch detailed information about a person in Sales Navigator",
      "resource": "salesNavigator"
    },
    {
      "name": "Search Companies in Sales Navigator",
      "value": "nvSearchCompanies",
      "action": "Search companies in Sales Navigator",
      "description": "Search for companies in Sales Navigator",
      "resource": "salesNavigator"
    },
    {
      "name": "Search People in Sales Navigator",
      "value": "nvSearchPeople",
      "action": "Search people in Sales Navigator",
      "description": "Search for people in Sales Navigator",
      "resource": "salesNavigator"
    },
    {
      "name": "Send Message in Sales Navigator",
      "value": "nvSendMessage",
      "action": "Send message in Sales Navigator",
      "description": "Send a message to a person in Sales Navigator",
      "resource": "salesNavigator"
    },
    {
      "name": "Sync Conversation in Sales Navigator",
      "value": "nvSyncConversation",
      "action": "Sync conversation in Sales Navigator",
      "description": "Sync a conversation in Sales Navigator so you can start polling it",
      "resource": "salesNavigator"
    },
    {
      "name": "Custom Workflow",
      "value": "customWorkflow",
      "action": "Execute custom workflow",
      "description": "Execute a custom multi-step workflow using raw workflow definition",
      "resource": "other"
    },
    {
      "name": "Get Workflow Result",
      "value": "getWorkflowResult",
      "action": "Get workflow result",
      "description": "Get the result of workflow execution",
      "resource": "other"
    },
    {
      "name": "Cancel Workflow",
      "value": "cancelWorkflow",
      "action": "Cancel workflow",
      "description": "Cancel a running workflow execution",
      "resource": "other"
    },
    {
      "name": "Poll Conversations",
      "value": "pollConversations",
      "action": "Poll conversations",
      "description": "Poll multiple conversations for new messages",
      "resource": "other"
    },
    {
      "name": "Actions Usage Statistics",
      "value": "actionsUsageStatistics",
      "action": "Get actions statistics",
      "description": "Get actions usage statistics for a date range",
      "resource": "other"
    }
  ]

npm_readme:
  Linked API for n8n connects your LinkedIn account to n8n's automation platform, so you can build visual workflows that search for prospects, send messages, manage connections, extract data, and more – all with [industry-leading safety](https://linkedapi.io/safety/).

  > [!IMPORTANT]
  > Our n8n node is a verified community node – just open the nodes panel, search for "Linked API", and install it with a click.
  > 
  > Alternatively, install it manually: go to Settings → Community Nodes, enter n8n-nodes-linked-api, and click Install.

  ## Use cases
  - **Lead generation pipeline**. Search for prospects matching your criteria, automatically send connection requests, wait for acceptance, then send personalized follow-up messages. Connect the results to your CRM to track everything in one place.
  - **CRM synchronization**. When a new lead enters your CRM, automatically find their LinkedIn profile, send a connection request, and update the CRM with their LinkedIn data. Keep your sales pipeline enriched with real-time LinkedIn information.
  - **Content engagement**. Monitor when your target accounts post on LinkedIn, automatically react or comment on their posts, then log the engagement in your tracking spreadsheet. Build relationships at scale without losing the personal touch.
  - **Recruitment workflows**. Search for candidates with specific skills, send personalized InMails through Sales Navigator, track responses in your ATS, and automatically follow up with interested candidates. Streamline your entire recruitment funnel.
  - **Multi-channel outreach**. Combine LinkedIn with email, Slack, and other channels. When someone accepts your connection request, trigger an email sequence, notify your team in Slack, and create a task in your project management tool.


  ## Get started
  To build LinkedIn automations with n8n, read these essential guides:

  1. [Creating credential](https://linkedapi.io/integrations/n8n/creating-credential/) – get your API tokens and connect to n8n.
  2. [Building workflows](https://linkedapi.io/integrations/n8n/building-workflows-0/) – learn how to use Linked API actions in your workflows.
  3. [Available actions](https://linkedapi.io/integrations/n8n/available-actions/) – explore all LinkedIn actions you can automate.

  ## License
  This project is licensed under the MIT – see the [LICENSE](https://github.com/Linked-API/n8n-nodes-linked-api/blob/main/LICENSE.md) file for details.

ai_documentation_summary:
  {"purpose":"This n8n community node connects to LinkedIn accounts to automate prospecting, messaging, connection management, and data extraction within visual workflows.","capabilities":["Search for prospects by criteria","Send connection requests","Send personalized messages (InMails)","Manage connections","Extract profile data","Monitor account posts","React to or comment on posts","Track responses and engagement","Sync data to CRM or spreadsheets"],"authentication":"API tokens (OAuth2-based credential setup required via Linked API integration)","commonUseCases":["Lead generation pipelines","CRM synchronization with LinkedIn data","Content engagement automation","Recruitment and candidate outreach","Multi-channel outreach workflows"],"limitations":["Community node (not official n8n node)","Requires manual installation from community panel","Dependent on Linked API's safety and rate limits"],"relatedNodes":["n8n-nodes-base.httpRequest","n8n-nodes-base.webhook","n8n-nodes-base.credentialManager"]}


node_type: n8n-nodes-justcall.justCallTrigger
package_name: n8n-nodes-justcall
display_name: JustCall Trigger
description: Starts the workflow when JustCall events occur
category: Community
development_style: declarative
version: 1
properties_schema: []
operations: []
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: devjustcall
author_github_url: https://github.com/saaslabsco/
npm_package_name: n8n-nodes-justcall
npm_version: 1.0.4
community_fetched_at: 2026-04-16T09:43:36.314Z
ai_summary_generated_at: 2026-03-25 22:51:43
npm_readme:
  # n8n-nodes-justcall

  [![npm version](https://img.shields.io/npm/v/n8n-nodes-justcall)](https://www.npmjs.com/package/n8n-nodes-justcall)
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

  An n8n community node package that provides seamless integration with the JustCall API, enabling you to automate calls, SMS, contact management, AI voice agents, Sales Dialer campaigns, and JustCall AI insights within your n8n workflows.

  ## Features

  This package includes two main nodes:

  ### 1. JustCall Node

  A comprehensive node for interacting with the JustCall API, supporting the following resources:

  #### 📞 **Call**
  - **Get** - Retrieve a specific call by ID
  - **Get Many** - List and filter calls with pagination support
  - **Update** - Update call notes, disposition, and rating
  - **Download Call Recording** - Download the recording of a completed call
  - **Get Call Journey** - Get journey details of a completed call (available after call has ended)
  - **Get Voice Agent Data** - Get voice agent insights and metadata for a completed call

  #### 💬 **SMS**
  - **Send** - Send SMS or MMS messages
  - **Get** - Retrieve a specific SMS by ID
  - **Get Many** - List and filter SMS messages

  #### 👤 **Contact**
  - **Create** - Create a new contact
  - **Get** - Retrieve a specific contact
  - **Get Many** - List and filter contacts
  - **Update** - Update an existing contact

  #### 📱 **Phone Number**
  - **Get** - Retrieve a specific phone number
  - **Get Many** - List all phone numbers

  #### 🤖 **AI Voice Agent**
  - **List Agents** - List all available AI voice agents
  - **Initiate Call** - Initiate a call with an AI voice agent

  #### 📋 **Sales Dialer**
  - **List All Calls** - List all Sales Dialer calls with filters (campaign, agent, date range, etc.)
  - **Get a Call** - Get a specific Sales Dialer call by ID
  - **List All Campaigns** - List all Sales Dialer campaigns
  - **Get a Campaign** - Get a specific Sales Dialer campaign by ID
  - **List Campaign Contacts** - List contacts in a Sales Dialer campaign
  - **Add Contact to Campaign** - Add a contact to a Sales Dialer campaign (by phone number or contact ID)
  - **List All Contacts** - List all Sales Dialer contacts
  - **Get a Contact** - Get a specific Sales Dialer contact by ID

  #### 🧠 **JustCall AI**
  - **List Calls AI Data** - List AI-generated analysis for JustCall and Sales Dialer calls (summary, transcription, insights, action items)
  - **Get Call AI Data** - Get AI-generated analysis for a specific call
  - **List Meetings AI Data** - List AI-generated analysis for meeting instances
  - **Get Meeting AI Data** - Get AI-generated analysis for a specific meeting instance

  ### 2. JustCall Trigger Node

  A webhook trigger node that automatically starts workflows when JustCall events occur:

  - **Call Answered** - Triggered when a call is answered
  - **Call Completed** - Triggered when a call is completed
  - **Call Initiated** - Triggered when a call is initiated
  - **Call Missed** - Triggered when a call is missed
  - **Call Updated** - Triggered when a call is updated
  - **Incoming Call** - Triggered when an incoming call is received
  - **SMS Received** - Triggered when an SMS is received
  - **SMS Sent** - Triggered when an SMS is sent
  - **Voicemail Received** - Triggered when a voicemail is received

  ## Installation

  ### For n8n Cloud Users

  This node is available in the n8n community nodes catalog. You can install it directly from the n8n interface:

  1. Go to **Settings** → **Community Nodes**
  2. Search for `n8n-nodes-justcall`
  3. Click **Install**

  ### For Self-Hosted n8n

  If you're running a self-hosted instance of n8n, install this package in your n8n installation directory:

  ```bash
  npm install n8n-nodes-justcall
  ```

  Or if you're using n8n via Docker, add this to your `docker-compose.yml`:

  ```yaml
  services:
    n8n:
      environment:
        - N8N_USER_FOLDER=/home/node/.n8n
      volumes:
        - n8n_data:/home/node/.n8n
  ```

  Then install the package in the container:

  ```bash
  docker exec -it <container-name> npm install n8n-nodes-justcall
  ```

  ## Configuration

  ### API Credentials

  Before using the JustCall nodes, you need to configure your JustCall API credentials:

  1. In n8n, go to **Credentials** → **Add Credential**
  2. Search for **JustCall API**
  3. Enter your JustCall API key
     - You can find your API key in your JustCall dashboard under **Profile** → **APIs and Webhooks**

  ## Usage Examples

  ### Example 1: Send SMS on Contact Creation

  Create a workflow that automatically sends a welcome SMS when a new contact is created:

  1. Add a **JustCall** node
  2. Select **Contact** → **Create**
  3. Configure the contact details
  4. Add another **JustCall** node
  5. Select **SMS** → **Send**
  6. Use the contact's phone number from the previous node

  ### Example 2: Trigger Workflow on Call Completion

  Set up a workflow that processes call data when a call is completed:

  1. Add a **JustCall Trigger** node
  2. Select **Call Completed** event
  3. Add subsequent nodes to process the call data (e.g., save to database, send notification)

  ### Example 3: AI Voice Agent Outbound Call

  Initiate an outbound call using an AI voice agent:

  1. Add a **JustCall** node
  2. Select **AI Voice Agent** → **Initiate Call**
  3. Select the agent and provide the phone number
  4. Configure call parameters

  ### Example 4: Sales Dialer – Add Contact to Campaign

  Add a contact to a Sales Dialer campaign for power dialing:

  1. Add a **JustCall** node
  2. Select **Sales Dialer** → **Add Contact to Campaign**
  3. Enter the campaign ID and either phone number or contact ID
  4. Optionally add contact details (name, email, custom fields)

  ### Example 5: JustCall AI – Get Call Insights

  Retrieve AI-generated summary and insights for a call:

  1. Add a **JustCall** node
  2. Select **JustCall AI** → **Get Call AI Data**
  3. Enter the call ID (JustCall or Sales Dialer)
  4. Optionally enable summary, transcription, smart chapters, or action items

  ## Development

  ### Prerequisites

  - Node.js (v18 or higher)
  - npm or pnpm
  - TypeScript

  ### Setup

  1. Clone the repository:
  ```bash
  git clone https://github.com/saaslabsco/n8n-nodes-justcall.git
  cd n8n-nodes-justcall
  ```

  2. Install dependencies:
  ```bash
  npm install
  ```

  3. Build the project:
  ```bash
  npm run build
  ```

  ### Available Scripts

  - `npm run build` - Compile TypeScript to JavaScript
  - `npm run dev` - Watch mode for development
  - `npm run lint` - Run ESLint
  - `npm run lintfix` - Fix ESLint errors automatically
  - `npm run format` - Format code with Prettier
  - `npm run changeset` - Create a new changeset for versioning
  - `npm run version` - Bump versions based on changesets
  - `npm run release` - Publish the package

  ### Project Structure

  ```
  n8n-nodes-justcall/
  ├── nodes/
  │   └── JustCall/
  │       ├── descriptions/     # Node property descriptions
  │       ├── handlers/          # Operation handlers
  │       ├── utils/             # Utility functions
  │       ├── JustCall.node.ts   # Main JustCall node
  │       └── JustCallTrigger.node.ts  # Trigger node
  ├── credentials/               # Credential definitions
  └── dist/                     # Compiled output
  ```

  ## Contributing

  Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (`git checkout -b feature/amazing-feature`)
  3. Make your changes
  4. Run tests and linting (`npm run lint`)
  5. Commit your changes (`git commit -m 'Add some amazing feature'`)
  6. Create a changeset (`npm run changeset`)
  7. Push to the branch (`git push origin feature/amazing-feature`)
  8. Open a Pull Request

  ### Versioning

  This project uses [Changesets](https://github.com/changesets/changesets) for version management. When making changes:

  1. Run `npm run changeset` to document your changes
  2. Commit the changeset file along with your code changes
  3. When ready to release, maintainers will run `npm run version` to update versions and changelog

  ## Changelog

  See [CHANGELOG.md](./CHANGELOG.md) for a list of changes and version history.

  ## License

  This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.

  ## Support

  For issues, questions, or contributions, please visit:
  - **GitHub Issues**: [https://github.com/saaslabsco/n8n-nodes-justcall/issues](https://github.com/saaslabsco/n8n-nodes-justcall/issues)
  - **JustCall API Documentation**: [https://developer.justcall.io/reference/initiate_outbound_call_v21](https://developer.justcall.io/reference/initiate_outbound_call_v21)

  ## Author

  **JustCall Dev**
  - Email: dev@justcall.io
  - GitHub: [@saaslabsco](https://github.com/saaslabsco)

  ---

  Made with ❤️ by the JustCall team

ai_documentation_summary:
  {"purpose":"The JustCall Trigger node automatically starts n8n workflows when specific JustCall events occur, such as calls being answered, completed, or SMS messages being sent or received.","capabilities":["Trigger on Call Answered","Trigger on Call Completed","Trigger on Call Initiated","Trigger on Call Missed","Trigger on Call Updated","Trigger on Incoming Call","Trigger on SMS Received","Trigger on SMS Sent","Trigger on Voicemail Received"],"authentication":"API key required (JustCall API key configured in n8n Credentials)","commonUseCases":["Automate follow-up actions when a call is completed","Process incoming SMS messages in real-time","Start workflows based on call events (answered, missed, etc.)","Integrate JustCall events with external databases or notification systems"],"limitations":["Trigger events only occur after the event has happened (not predictive or scheduled)","Requires valid JustCall API credentials configured in n8n"],"relatedNodes":["JustCall Node","n8n-nodes-justcall.justCallNode"]}


node_type: @copicake/n8n-nodes-copicake.copicake
package_name: @copicake/n8n-nodes-copicake
display_name: Copicake
description: Generate and retrieve images using Copicake API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: eragonj
author_github_url: https://github.com/Copicake/n8n-nodes-copicake.git
npm_package_name: @copicake/n8n-nodes-copicake
npm_version: 1.1.0
community_fetched_at: 2026-04-16T09:43:36.315Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "image",
      "options": [
        {
          "name": "Image",
          "value": "image"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "create",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create an image",
          "description": "Create a new image from a template"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get an image",
          "description": "Get an existing image by rendering ID"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "image"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Create",
      "value": "create",
      "action": "Create an image",
      "description": "Create a new image from a template",
      "resource": "image"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get an image",
      "description": "Get an existing image by rendering ID",
      "resource": "image"
    }
  ]

npm_readme:
  ![Banner image](https://user-images.githubusercontent.com/10284570/173569848-c624317f-42b1-45a6-ab09-f0ea3c247648.png)

  # n8n-nodes-copicake

  This is an n8n community node that lets you use [Copicake](https://copicake.com/) in your n8n workflows.

  Copicake is a data-driven image generation service that allows you to generate social media materials with just one click using templates and API calls.

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ### Community Nodes (Recommended)

  1. Go to **Settings > Community Nodes**.
  2. Select **Install**.
  3. Enter `n8n-nodes-copicake` in **Enter npm package name**.
  4. Agree to the [risks](https://docs.n8n.io/integrations/community-nodes/risks/) of using community nodes: select **I understand the risks of installing unverified code from a public source**.
  5. Select **Install**.

  After installing the node, you can use it like any other node in your workflows.

  ### Manual Installation

  To get started install the package in your n8n root directory:

  ```bash
  npm install n8n-nodes-copicake
  ```

  For Docker-based deployments, add the following line before the font installation command in your [n8n Dockerfile](https://github.com/n8n-io/n8n/blob/master/docker/images/n8n/Dockerfile):

  ```dockerfile
  RUN cd /usr/local/lib/node_modules/n8n && npm install n8n-nodes-copicake
  ```

  ## Credentials

  You need to set up credentials to use this node:

  1. In n8n, go to **Credentials** and create new **Copicake API** credentials.
  2. Enter your Copicake API key.
     - You can get your API key from the [Copicake dashboard](https://copicake.com/).

  ## Operations

  This node supports the following operations:

  ### Image

  #### Create
  Create a new image from a template with custom changes.

  **Parameters:**
  - **Template ID** (required): The ID of the template to use for image generation
  - **Changes**: Array of changes to apply to the template elements
    - **Element Name**: The name of the element to modify
    - **Change Type**: Type of change (Text, Image, QR Code, Shape)
    - **Properties**: Specific properties based on change type:
      - **Text**: text, fill color, stroke color, text background color
      - **Image**: image URL, stroke color
      - **QR Code**: content, fill color, stroke color
      - **Shape**: fill color, stroke color
  - **Additional Options**:
    - **Format**: Output format (PNG or JPG)
    - **Max Wait Time (Seconds)**: Maximum time to wait for rendering completion (default: 60)
    - **Polling Interval (Seconds)**: How often to check rendering status (default: 2)
    - **Wait for Completion**: Whether to poll until rendering is complete (default: true)
    - **Webhook URL**: URL to receive webhook when image is ready

  **Asynchronous Processing:**
  By default, the node will automatically wait for the image rendering to complete before returning the final result. This includes polling the Copicake API every 2 seconds until the status becomes either `success` or `failed`, with a maximum wait time of 60 seconds. You can disable this behavior by setting "Wait for Completion" to false, which will return the initial response with `processing` status immediately.

  **Example:**
  ```json
  {
    "template_id": "jfwrFJdR3z1eF8BcEhSnAFyhxgOq",
    "changes": [
      {
        "name": "text-9so09m",
        "text": "hello world",
        "fill": "#ff0000"
      },
      {
        "name": "image-yeavh7",
        "src": "https://your_website.com/test.png"
      }
    ],
    "options": {
      "format": "png",
      "webhook_url": "https://your_website.com/webhook_url"
    }
  }
  ```

  #### Get
  Retrieve an existing image by its rendering ID.

  **Parameters:**
  - **Rendering ID** (required): The rendering ID of the image to retrieve

  **Example:**
  ```json
  {
    "rendering_id": "ki9ImK39lPRwb1oOpKbROkNWumFz"
  }
  ```

  ## Response Format

  ### Create Image Response
  ```json
  {
    "rendering_id": "xc17NtfyMaxPDCtz0ZjEZ4guBmiC",
    "status": "processing",
    "created_at": "2023-01-01T00:00:00Z"
  }
  ```

  ### Get Image Response
  ```json
  {
    "rendering_id": "xc17NtfyMaxPDCtz0ZjEZ4guBmiC",
    "status": "success",
    "permanent_url": "https://copicake.s3.ap-northeast-1.amazonaws.com/renderings/xc17NtfyMaxPDCtz0ZjEZ4guBmiC.png",
    "created_at": "2023-01-01T00:00:00Z",
    "completed_at": "2023-01-01T00:00:05Z"
  }
  ```

  ## Example Workflows

  ### Basic Image Generation
  1. **Manual Trigger** → **Copicake (Create Image)** → **HTTP Request** (to download image)

  ### Automated Social Media Posts
  1. **Schedule Trigger** → **Copicake (Create Image)** → **Twitter** (post with generated image)

  ### Dynamic Content Generation
  1. **Webhook** → **Set** (prepare data) → **Copicake (Create Image)** → **Email** (send with generated image)

  ## Resources

  - [Copicake API Documentation](https://docs.copicake.com/)
  - [Copicake Website](https://copicake.com/)
  - [n8n Community Nodes Documentation](https://docs.n8n.io/integrations/community-nodes/)

  ## Version History

  ### 0.1.0
  - Initial release
  - Support for Create Image and Get Image operations
  - Full support for all Copicake API features including text, image, QR code, and shape modifications

  ## License

  [MIT](https://github.com/copicake/n8n-nodes-copicake/blob/main/LICENSE.md)

ai_documentation_summary:
  {"purpose":"Generates and retrieves images using the Copicake API, allowing users to create social media materials with templates and custom modifications.","capabilities":["Create images from templates with custom text, images, QR codes, and shapes","Retrieve existing images by rendering ID","Support for PNG and JPG output formats","Asynchronous image generation with configurable polling intervals and wait times","Webhook support for receiving images when ready","Customization of text, images, QR codes, and shapes within templates"],"authentication":"API key required","commonUseCases":["Automated social media post generation","Dynamic content creation for marketing materials","Email campaigns with personalized images","Social media management workflows"],"limitations":["Community node (unverified code from public source)","Requires Copicake API key from external dashboard","Maximum wait time for image generation is 60 seconds","Limited to Copicake's available templates and features"],"relatedNodes":["HTTP Request","Schedule Trigger","Twitter","Email","Webhook"]}


node_type: @solution25/n8n-nodes-shopware.shopware
package_name: @solution25/n8n-nodes-shopware
display_name: Shopware
description: Read, update, write and delete data from Shopware
category: Community
development_style: declarative
version: 2.0.0
properties_schema: []
operations: []
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: fatjon-gashi1
author_github_url: https://github.com/solution25com/n8n-nodes-shopware.git
npm_package_name: @solution25/n8n-nodes-shopware
npm_version: 2.0.0
community_fetched_at: 2026-04-16T09:43:36.316Z
ai_summary_generated_at: 2026-03-25 22:51:43
npm_readme:
  # @solution25/n8n-nodes-shopware

  This is an n8n community node. It lets you use Shopware 6 in your n8n workflows.

  Shopware 6 is a modern, open-source, headless e-commerce platform designed for flexibility and scalability. With this node, you can connect your Shopware store to other services and automate tasks such as product management, order processing, and customer handling.

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.

  [Installation](#installation)
  [Operations](#operations)
  [Credentials](#credentials)
  [Compatibility](#compatibility)
  [Usage](#usage)
  [Resources](#resources)
  [Version history](#version-history)

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ## Operations

  This node currently supports the following resources and operations:

  | Resource | Get | Get Many | Create | Update | Delete |
  |----------|-----|----------|--------|--------|--------|
  | Product  |  ✓  |     ✓    |    ✓   |    ✓   |    ✓   |
  | Customer |  ✓  |     ✓    |    ✓   |    ✓   |    ✓   |
  | Order    |  ✓  |     ✓    |    ✓   |    ✓   |    ✓   |

  ## Credentials

  Authentication is handled via a [Shopware Integration](https://docs.shopware.com/en/shopware-6-en/settings/system/integrationen), which you can create in the Shopware admin panel.
  Required fields:

  * Domain (your Shopware 6 store URL)

  * Client ID

  * Client Secret

  Once configured, the node uses these credentials to authenticate against the Shopware Admin API.

  ## Compatibility

  n8n version: tested with the latest n8n release (keep n8n updated for best compatibility).

  Shopware version: compatible with Shopware 6.6.x and 6.7.x

  ## Usage

  Here are a few examples of what you can do with this node:

  * Automatically create a new customer in Shopware when a signup happens in another system.

  * Sync products between Shopware and an external inventory tool.

  * Fetch new orders and send notifications to Slack, email, or other channels.

  For general n8n usage, see the [Try it out guide](https://docs.n8n.io/try-it-out/).

  ## Resources

  * [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  * [Shopware 6 Admin API reference](https://shopware.stoplight.io/docs/admin-api/twpxvnspkg3yu-quick-start-guide)
  * [Shopware documentation](https://docs.shopware.com)
  * [Shopware official site](https://www.shopware.com)

  ## Version history

  | Version         | Description                                                              |
  | --------------- | ------------------------------------------------------------------------ |
  | **0.1.0-alpha** | Pre-release with basic product, customer, and order support.             |
  | **1.0.0**       | Initial release with full product resource support.                      |
  | **1.1.0**       | Added full support for customer resource.                                |
  | **1.2.0**       | Added full support for order resource.                                   |
  | **1.2.1**       | Fixed order creation using node-created customers.                       |
  | **1.3.0**       | Added advanced filtering for all resources.                              |
  | **1.3.1**       | Added base unit testing for all resources.                               |
  | **1.4.0**       | Added full support for categories resource.                              |
  | **1.4.1**       | Removed deprecated transport method, options and types.                  |
  | **1.4.2**       | Fixed missing credentials in package definition and resolved icon paths. |
  | **1.4.3**       | Added credentials explicitly to the base versioned node.                 |
  | **1.4.4**       | Added line-items, currency, deliveries and transactions data to order. Changed node icon format to SVG.|
  | **1.4.5**       | Removed debug statements from code.                                        |
  | **2.0.0**       | Large refactor, validation fixes, correct tax calculations and rounding, significant optimizations, media upload for products, more associations for resources, better error handling and surfacing, line item pricing fix, order delivery versioning fix, complete fields for addresses, filters fix.                                        |

ai_documentation_summary:
  {"purpose":"This n8n community node enables full CRUD operations (Create, Read, Update, Delete) on Shopware 6 resources including Products, Customers, Orders, and Categories.","capabilities":["Create new products","Update existing products","Delete products","Retrieve single or multiple products","Create new customers","Update existing customers","Delete customers","Retrieve single or multiple customers","Create new orders","Update existing orders"],"authentication":"Shopware Integration credentials (Domain, Client ID, Client Secret)","commonUseCases":["Automatically create customers in Shopware when signups occur in other systems","Sync products between Shopware and external inventory tools","Fetch new orders and trigger notifications to Slack, email, or other channels","Automate order processing workflows"],"limitations":["Requires a Shopware 6.6.x or 6.7.x installation","Requires manual setup of Shopware Integration credentials in the Shopware admin panel","Community node (not official n8n node)"],"relatedNodes":["Shopware API","Shopware Integration","Shopware Admin API"]}


node_type: n8n-nodes-octavehq.octave
package_name: n8n-nodes-octavehq
display_name: Octave
description: Interact with the Octave V2 API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: jtempels
author_github_url: https://github.com/octavehq/octave-n8n.git
npm_package_name: n8n-nodes-octavehq
npm_version: 1.5.0
community_fetched_at: 2026-04-16T09:43:36.317Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "agent",
      "options": [
        {
          "name": "Agent",
          "value": "agent"
        },
        {
          "name": "Async",
          "value": "async"
        },
        {
          "name": "Competitor",
          "value": "competitor"
        },
        {
          "name": "Experiment",
          "value": "experiment"
        },
        {
          "name": "Persona",
          "value": "persona"
        },
        {
          "name": "Playbook",
          "value": "playbook"
        },
        {
          "name": "Product",
          "value": "product"
        },
        {
          "name": "Proof Point",
          "value": "proofPoint"
        },
        {
          "name": "Reference",
          "value": "reference"
        },
        {
          "name": "Segment",
          "value": "segment"
        },
        {
          "name": "Use Case",
          "value": "useCase"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Build Workspace",
          "value": "buildWorkspace",
          "action": "Build workspace",
          "description": "Generate and build a Workspace"
        },
        {
          "name": "Call Prep",
          "value": "callPrep",
          "action": "Generate call prep",
          "description": "Generate call prep content including discovery questions, call scripts, and objection handling"
        },
        {
          "name": "Enrich Company",
          "value": "enrichCompany",
          "action": "Enrich a company",
          "description": "Enrich a company"
        },
        {
          "name": "Enrich Person",
          "value": "enrichPerson",
          "action": "Enrich a person",
          "description": "Enrich a Person"
        },
        {
          "name": "Generate Content",
          "value": "generateContent",
          "action": "Generate content",
          "description": "Generate content for a person"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List agents",
          "description": "List all agents"
        },
        {
          "name": "Qualify Company",
          "value": "qualifyCompany",
          "action": "Qualify a company",
          "description": "Determine if a company is qualified for a given Product and/or Segment"
        },
        {
          "name": "Qualify Person",
          "value": "qualifyPerson",
          "action": "Qualify a person",
          "description": "Determine if a person is qualified for a given Product, Persona, and/or Segment"
        },
        {
          "name": "Run Prospector",
          "value": "runProspector",
          "action": "Run a prospector agent",
          "description": "Find relevant people at a company or lookalike companies"
        },
        {
          "name": "Run Sequence",
          "value": "runSequence",
          "action": "Run a sequence agent",
          "description": "Generate Emails for a person"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "agent"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "List Playbooks",
          "value": "list",
          "action": "List playbooks"
        },
        {
          "name": "Get Playbook",
          "value": "get",
          "action": "Get a playbook by OID"
        },
        {
          "name": "Create Playbook",
          "value": "create",
          "action": "Create a new playbook"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "playbook"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a product",
          "description": "Create a new product"
        },
        {
          "name": "Generate",
          "value": "generate",
          "action": "Generate products",
          "description": "Generate products from source materials using AI"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a product",
          "description": "Get a specific product by OId"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List products",
          "description": "List products in the workspace"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a product",
          "description": "Update an existing product"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "product"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a persona",
          "description": "Create a new persona"
        },
        {
          "name": "Generate",
          "value": "generate",
          "action": "Generate personas",
          "description": "Generate personas from source materials using AI"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a persona",
          "description": "Get a specific persona by OId"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List personas",
          "description": "List personas in the workspace"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a persona",
          "description": "Update an existing persona"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "persona"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a proof point",
          "description": "Create a new proof point"
        },
        {
          "name": "Generate",
          "value": "generate",
          "action": "Generate proof points",
          "description": "Generate proof points from source materials using AI"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a proof point",
          "description": "Get a proof point by OId"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List proof points",
          "description": "List proof points with optional filtering and pagination"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a proof point",
          "description": "Update an existing proof point"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "proofPoint"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a reference",
          "description": "Create a new reference"
        },
        {
          "name": "Generate",
          "value": "generate",
          "action": "Generate references",
          "description": "Generate references from source materials using AI"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a reference",
          "description": "Get a specific reference by OId"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List references",
          "description": "List references in the workspace"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a reference",
          "description": "Update an existing reference"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "reference"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a use case",
          "description": "Create a new use case"
        },
        {
          "name": "Generate",
          "value": "generate",
          "action": "Generate use cases",
          "description": "Generate use cases from source materials using AI"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a use case",
          "description": "Get a specific use case by OId"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List use cases",
          "description": "List use cases in the workspace"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a use case",
          "description": "Update an existing use case"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "useCase"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "runAgent",
      "options": [
        {
          "name": "Run Agent",
          "value": "runAgent",
          "action": "Run an agent asynchronously",
          "description": "Triggers an agent to run in the background"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "async"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a competitor",
          "description": "Create a new competitor"
        },
        {
          "name": "Generate",
          "value": "generate",
          "action": "Generate competitors",
          "description": "Generate competitors from source materials using AI"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a competitor",
          "description": "Get a competitor by OId"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List competitors",
          "description": "List competitors with optional filtering"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a competitor",
          "description": "Update an existing competitor"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "competitor"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a segment",
          "description": "Create a new segment"
        },
        {
          "name": "Generate",
          "value": "generate",
          "action": "Generate segments",
          "description": "Generate segments from source materials using AI"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a segment",
          "description": "Get a segment by OId"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List segments",
          "description": "List segments with optional filtering"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a segment",
          "description": "Update an existing segment"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "segment"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "create",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create an experiment",
          "description": "Create a new agent experiment"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "experiment"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Build Workspace",
      "value": "buildWorkspace",
      "action": "Build workspace",
      "description": "Generate and build a Workspace",
      "resource": "agent"
    },
    {
      "name": "Call Prep",
      "value": "callPrep",
      "action": "Generate call prep",
      "description": "Generate call prep content including discovery questions, call scripts, and objection handling",
      "resource": "agent"
    },
    {
      "name": "Enrich Company",
      "value": "enrichCompany",
      "action": "Enrich a company",
      "description": "Enrich a company",
      "resource": "agent"
    },
    {
      "name": "Enrich Person",
      "value": "enrichPerson",
      "action": "Enrich a person",
      "description": "Enrich a Person",
      "resource": "agent"
    },
    {
      "name": "Generate Content",
      "value": "generateContent",
      "action": "Generate content",
      "description": "Generate content for a person",
      "resource": "agent"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List agents",
      "description": "List all agents",
      "resource": "agent"
    },
    {
      "name": "Qualify Company",
      "value": "qualifyCompany",
      "action": "Qualify a company",
      "description": "Determine if a company is qualified for a given Product and/or Segment",
      "resource": "agent"
    },
    {
      "name": "Qualify Person",
      "value": "qualifyPerson",
      "action": "Qualify a person",
      "description": "Determine if a person is qualified for a given Product, Persona, and/or Segment",
      "resource": "agent"
    },
    {
      "name": "Run Prospector",
      "value": "runProspector",
      "action": "Run a prospector agent",
      "description": "Find relevant people at a company or lookalike companies",
      "resource": "agent"
    },
    {
      "name": "Run Sequence",
      "value": "runSequence",
      "action": "Run a sequence agent",
      "description": "Generate Emails for a person",
      "resource": "agent"
    },
    {
      "name": "List Playbooks",
      "value": "list",
      "action": "List playbooks",
      "resource": "playbook"
    },
    {
      "name": "Get Playbook",
      "value": "get",
      "action": "Get a playbook by OID",
      "resource": "playbook"
    },
    {
      "name": "Create Playbook",
      "value": "create",
      "action": "Create a new playbook",
      "resource": "playbook"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a product",
      "description": "Create a new product",
      "resource": "product"
    },
    {
      "name": "Generate",
      "value": "generate",
      "action": "Generate products",
      "description": "Generate products from source materials using AI",
      "resource": "product"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a product",
      "description": "Get a specific product by OId",
      "resource": "product"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List products",
      "description": "List products in the workspace",
      "resource": "product"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a product",
      "description": "Update an existing product",
      "resource": "product"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a persona",
      "description": "Create a new persona",
      "resource": "persona"
    },
    {
      "name": "Generate",
      "value": "generate",
      "action": "Generate personas",
      "description": "Generate personas from source materials using AI",
      "resource": "persona"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a persona",
      "description": "Get a specific persona by OId",
      "resource": "persona"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List personas",
      "description": "List personas in the workspace",
      "resource": "persona"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a persona",
      "description": "Update an existing persona",
      "resource": "persona"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a proof point",
      "description": "Create a new proof point",
      "resource": "proofPoint"
    },
    {
      "name": "Generate",
      "value": "generate",
      "action": "Generate proof points",
      "description": "Generate proof points from source materials using AI",
      "resource": "proofPoint"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a proof point",
      "description": "Get a proof point by OId",
      "resource": "proofPoint"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List proof points",
      "description": "List proof points with optional filtering and pagination",
      "resource": "proofPoint"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a proof point",
      "description": "Update an existing proof point",
      "resource": "proofPoint"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a reference",
      "description": "Create a new reference",
      "resource": "reference"
    },
    {
      "name": "Generate",
      "value": "generate",
      "action": "Generate references",
      "description": "Generate references from source materials using AI",
      "resource": "reference"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a reference",
      "description": "Get a specific reference by OId",
      "resource": "reference"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List references",
      "description": "List references in the workspace",
      "resource": "reference"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a reference",
      "description": "Update an existing reference",
      "resource": "reference"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a use case",
      "description": "Create a new use case",
      "resource": "useCase"
    },
    {
      "name": "Generate",
      "value": "generate",
      "action": "Generate use cases",
      "description": "Generate use cases from source materials using AI",
      "resource": "useCase"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a use case",
      "description": "Get a specific use case by OId",
      "resource": "useCase"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List use cases",
      "description": "List use cases in the workspace",
      "resource": "useCase"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a use case",
      "description": "Update an existing use case",
      "resource": "useCase"
    },
    {
      "name": "Run Agent",
      "value": "runAgent",
      "action": "Run an agent asynchronously",
      "description": "Triggers an agent to run in the background",
      "resource": "async"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a competitor",
      "description": "Create a new competitor",
      "resource": "competitor"
    },
    {
      "name": "Generate",
      "value": "generate",
      "action": "Generate competitors",
      "description": "Generate competitors from source materials using AI",
      "resource": "competitor"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a competitor",
      "description": "Get a competitor by OId",
      "resource": "competitor"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List competitors",
      "description": "List competitors with optional filtering",
      "resource": "competitor"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a competitor",
      "description": "Update an existing competitor",
      "resource": "competitor"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a segment",
      "description": "Create a new segment",
      "resource": "segment"
    },
    {
      "name": "Generate",
      "value": "generate",
      "action": "Generate segments",
      "description": "Generate segments from source materials using AI",
      "resource": "segment"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a segment",
      "description": "Get a segment by OId",
      "resource": "segment"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List segments",
      "description": "List segments with optional filtering",
      "resource": "segment"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a segment",
      "description": "Update an existing segment",
      "resource": "segment"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create an experiment",
      "description": "Create a new agent experiment",
      "resource": "experiment"
    }
  ]

npm_readme:
  # n8n-nodes-octavehq

  This is an n8n community node. It lets you use Octave in your n8n workflows.

  Octave is the AI-powered messaging brain for B2B go-to-market teams, helping you articulate your product's value, synthesize customer interaction data, and deploy consistent, effective messaging across all your execution channels. In a world of rapid product innovation, Octave ensures your go-to-market strategy is always on message and impactful.

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.

  [Installation](#installation)
  [Operations](#operations)
  [Resources](#resources)
  [Version history](#version-history)

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ## Operations

  The Octave node supports the following operations, categorized by resource:

  ### Response Format

  All agent operations now return responses that include both the data and API metadata:

  ```json
  {
    "data": {
      // Actual response data from the operation
    },
    "_metadata": {
      "requestId": "unique-request-identifier",
      "timestamp": "2021-01-01T00:00:00.000Z",
      "usage": 0,
      "message": "Additional information about the request"
    }
  }
  ```

  The `_metadata` field provides valuable information for tracking, debugging, and monitoring API usage.

  **Resource: Agent**
  *   **List Agents**: List all agents
      *   Corresponds to `GET /api/v2/agents/list`
  *   **Build Workspace**: Generate and build a Workspace
      *   Corresponds to `POST /api/v2/agents/workspace/build`
  *   **Call Prep**: Generate call prep content including discovery questions, call scripts, and objection handling
      *   Corresponds to `POST /api/v2/agents/call-prep/run`
  *   **Enrich Company**: Enrich a company
      *   Corresponds to `POST /api/v2/agents/enrich-company/run`
  *   **Enrich Person**: Enrich a Person
      *   Corresponds to `POST /api/v2/agents/enrich-person/run`
  *   **Generate Content**: Generate content for a person
      *   Corresponds to `POST /api/v2/agents/generate-content/run`
  *   **Run Sequence**: Generate Emails for a person
      *   Corresponds to `POST /api/v2/agents/sequence/run`
  *   **Run Prospector**: Find relevant people at a company or lookalike companies
      *   Corresponds to `POST /api/v2/agents/prospector/run`
  *   **Qualify Company**: Determine if a company is qualified for a given Product and/or Segment
      *   Corresponds to `POST /api/v2/agents/qualify-company/run`
  *   **Qualify Person**: Determine if a person is qualified for a given Product, Persona, and/or Segment
      *   Corresponds to `POST /api/v2/agents/qualify-person/run`

  **Resource: Async**
  *   **Run Agent Asynchronously**: Triggers an agent to run in the background.
      *   Corresponds to `POST /api/v2/async/agent/run`

  **Resource: Competitor**
  *   **List Competitors**: Retrieves a list of competitors with optional filtering.
      *   Corresponds to `GET /api/v2/competitor/list`
  *   **Get Competitor**: Retrieves a specific competitor by its OId.
      *   Corresponds to `GET /api/v2/competitor/get`
  *   **Create Competitor**: Creates a new competitor.
      *   Corresponds to `POST /api/v2/competitor/create`
  *   **Update Competitor**: Updates an existing competitor.
      *   Corresponds to `PUT /api/v2/competitor/update`
  *   **Generate Competitors**: Generate competitors from source materials using AI.
      *   Corresponds to `POST /api/v2/competitor/generate`

  **Resource: Experiment**
  *   **Create Experiment**: Creates a new agent experiment for A/B testing different agents.
      *   Corresponds to `POST /api/v2/agents/experiment/create`

  **Resource: Persona**
  *   **List Personas**: Retrieves a list of personas.
      *   Corresponds to `GET /api/v2/persona/list`
  *   **Get Persona**: Retrieves a specific persona by its OId.
      *   Corresponds to `GET /api/v2/persona/get`
  *   **Create Persona**: Creates a new persona.
      *   Corresponds to `POST /api/v2/persona/create`
  *   **Update Persona**: Updates an existing persona.
      *   Corresponds to `PUT /api/v2/persona/update`
  *   **Generate Personas**: Generate personas from source materials using AI.
      *   Corresponds to `POST /api/v2/persona/generate`

  **Resource: Playbook**
  *   **List Playbooks**: Retrieves a list of playbooks.
      *   Corresponds to `GET /api/v2/playbook/list`
  *   **Get Playbook**: Retrieves a specific playbook by its OId.
      *   Corresponds to `GET /api/v2/playbook/get`
  *   **Create Playbook**: Creates a new playbook.
      *   Corresponds to `POST /api/v2/playbook/create`
  *   **Update Playbook**: Updates an existing playbook.
      *   Corresponds to `PUT /api/v2/playbook/update`

  **Resource: Product**
  *   **List Products**: Retrieves a list of products.
      *   Corresponds to `GET /api/v2/product/list`
  *   **Get Product**: Retrieves a specific product by its OId.
      *   Corresponds to `GET /api/v2/product/get`
  *   **Create Product**: Creates a new product.
      *   Corresponds to `POST /api/v2/product/create`
  *   **Update Product**: Updates an existing product.
      *   Corresponds to `PUT /api/v2/product/update`
  *   **Generate Products**: Generate products from source materials using AI.
      *   Corresponds to `POST /api/v2/product/generate`

  **Resource: Proof Point**
  *   **List Proof Points**: Retrieves a list of proof points with optional filtering.
      *   Corresponds to `GET /api/v2/proof-point/list`
  *   **Get Proof Point**: Retrieves a specific proof point by its OId.
      *   Corresponds to `GET /api/v2/proof-point/get`
  *   **Create Proof Point**: Creates a new proof point.
      *   Corresponds to `POST /api/v2/proof-point/create`
  *   **Update Proof Point**: Updates an existing proof point.
      *   Corresponds to `PUT /api/v2/proof-point/update`
  *   **Generate Proof Points**: Generate proof points from source materials using AI.
      *   Corresponds to `POST /api/v2/proof-point/generate`

  **Resource: Reference**
  *   **List References**: Retrieves a list of references.
      *   Corresponds to `GET /api/v2/reference/list`
  *   **Get Reference**: Retrieves a specific reference by its OId.
      *   Corresponds to `GET /api/v2/reference/get`
  *   **Create Reference**: Creates a new reference.
      *   Corresponds to `POST /api/v2/reference/create`
  *   **Update Reference**: Updates an existing reference.
      *   Corresponds to `PUT /api/v2/reference/update`
  *   **Generate References**: Generate references from source materials using AI.
      *   Corresponds to `POST /api/v2/reference/generate`

  **Resource: Segment**
  *   **List Segments**: Retrieves a list of segments with optional filtering.
      *   Corresponds to `GET /api/v2/segment/list`
  *   **Get Segment**: Retrieves a specific segment by its OId.
      *   Corresponds to `GET /api/v2/segment/get`
  *   **Create Segment**: Creates a new segment.
      *   Corresponds to `POST /api/v2/segment/create`
  *   **Update Segment**: Updates an existing segment.
      *   Corresponds to `PUT /api/v2/segment/update`
  *   **Generate Segments**: Generate segments from source materials using AI.
      *   Corresponds to `POST /api/v2/segment/generate`

  **Resource: Use Case**
  *   **List Use Cases**: Retrieves a list of use cases.
      *   Corresponds to `GET /api/v2/use-case/list`
  *   **Get Use Case**: Retrieves a specific use case by its OId.
      *   Corresponds to `GET /api/v2/use-case/get`
  *   **Create Use Case**: Creates a new use case.
      *   Corresponds to `POST /api/v2/use-case/create`
  *   **Update Use Case**: Updates an existing use case.
      *   Corresponds to `PUT /api/v2/use-case/update`
  *   **Generate Use Cases**: Generate use cases from source materials using AI.
      *   Corresponds to `POST /api/v2/use-case/generate`

  ## Credentials

  Get your API key from the Octave settings page. We recommend naming the credentials with the Workspace that the API key is associated with.

  ## Resources

  * [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  * [Library Overview](https://youtu.be/UyU1tDuRE58)
  * [Playbooks Overview](https://youtu.be/SINDvLIfRa8)
  * [Agents Overview](https://www.youtube.com/watch?v=oExCRibGjAU)
  * [Octave + Clay Overview](https://www.youtube.com/watch?v=JjYLBddEn7A)


  ## Version history
  * 0.1.1 - Octave <> N8N. Let the games begin!
  * 1.0.1 - Add `x-request-source` header to indicate request came from n8n
  * 1.0.2 - Add Credentials test + make Octave node usable by AI
  * 1.1.0 - Added Competitor, Segment, and Experiment resources; Added Call Prep agent; Removed Personalize Template agent; Improved modular architecture
  * 1.1.1 - Uses IHttpRequestOptions instead of deprecated IRequestOptions
  * 1.1.2 - Uses httpRequestWithAuthentication instead of httpRequest to be more secure and maintainable
  * 1.2.0 - Removed List API Keys endpoint
  * 1.2.1 - Rebuild to remove deprecated API files from build
  * 1.3.0 - Major expansion: Added full CRUD support (create, update, generate) for all resources; Added new Proof Point resource; Enhanced UX with native collection inputs for generate operations; Improved agent field ordering with LinkedIn Profile prioritization; Enhanced runtime context handling for sequences
  * 1.3.1 - Fixed UI issue in generate operations where "Add Source" button was not displaying Type and Value fields properly
  * 1.3.2 - Update Playbook types
  * 1.4.0 - Enhanced response format: All agent operations now include API metadata (`_metadata`) alongside data, providing request IDs, timestamps, usage information, and additional context for better tracking and debugging
  * 1.5.0 - Added `generate` route to references. Updated documentation.
  * 1.5.1 - Fixed runtime context handling: All agent operations (except Run Sequence) now correctly accept runtime context as a string instead of JSON. Run Sequence continues to use an object structure with step-specific keys.

ai_documentation_summary:
  {"purpose":"This n8n community node enables interaction with the Octave V2 API to automate B2B go-to-market messaging, including agent management, content generation, and prospecting.","capabilities":["List and manage agents","Build and manage workspaces","Generate call prep content","Enrich company and person data","Generate marketing content","Run email sequences","Prospect and find relevant leads","Qualify companies and individuals","Manage competitors (list, get, create, update, generate)","Manage personas (list, get, create, update, generate)"],"authentication":"API key required","commonUseCases":["Automating B2B sales outreach and prospecting","Generating personalized call scripts and objection handling","Enriching lead data for sales teams","Creating and managing marketing playbooks","A/B testing different messaging agents"],"limitations":["Community node (may have slower updates or support compared to official nodes)","Requires Octave V2 API access and valid API credentials","Response includes metadata wrapper that may require parsing"],"relatedNodes":["n8n-nodes-octavehq.octave","HTTP Request","JSON Response"]}


node_type: n8n-nodes-solapi.solapi
package_name: n8n-nodes-solapi
display_name: Solapi
description: Send messages with Solapi
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: nurigo
author_github_url: https://github.com/solapi/
npm_package_name: n8n-nodes-solapi
npm_version: 0.2.8
community_fetched_at: 2026-04-16T09:43:36.318Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "message",
      "options": [
        {
          "name": "Message",
          "value": "message"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "sendText",
      "options": [
        {
          "name": "Send Kakao AlimTalk",
          "value": "sendKakaoATA",
          "action": "Send kakao alim talk a message",
          "description": "Send Kakao AlimTalk (template-based)"
        },
        {
          "name": "Send Kakao FriendTalk",
          "value": "sendKakaoCTA",
          "action": "Send kakao friend talk a message"
        },
        {
          "name": "Send Text Message",
          "value": "sendText",
          "action": "Send text message a message",
          "description": "Send SMS/LMS/MMS via Solapi"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "message"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Send Kakao AlimTalk",
      "value": "sendKakaoATA",
      "action": "Send kakao alim talk a message",
      "description": "Send Kakao AlimTalk (template-based)",
      "resource": "message"
    },
    {
      "name": "Send Kakao FriendTalk",
      "value": "sendKakaoCTA",
      "action": "Send kakao friend talk a message",
      "resource": "message"
    },
    {
      "name": "Send Text Message",
      "value": "sendText",
      "action": "Send text message a message",
      "description": "Send SMS/LMS/MMS via Solapi",
      "resource": "message"
    }
  ]

npm_readme:
  # Solapi for n8n (Community Node)

  솔라피(Solapi) 메시지/카카오/커머스 웹훅을 n8n에서 쉽게 사용하기 위한 커뮤니티 노드입니다.

  ## 설치

  - n8n Community Nodes에서 설치
    - n8n UI → Settings → Community Nodes → Install → `n8n-nodes-solapi` 입력
  - 또는 수동 설치
    - 프로젝트에 패키지 설치 후 n8n 재기동

  ## 인증 설정 (OAuth2)
  1) n8n → Credentials → New → `Solapi OAuth2 API` 선택
  2) Solapi 콘솔에서 발급받은 Client ID/Secret 입력
  3) Scope 기본값 유지(권장):
  ```
  message:write message:read senderid:read storage:write storage:read webhook:read webhook:write kakao:write kakao:read users:read contacts:read contacts:write commerce:read commerce:write
  ```
  4) Save & Connect로 로그인 → 연결 성공 확인

  ## 인증 설정 (API Key)
  1) n8n → Credentials → New → `Solapi API Key` 선택
  2) Solapi 콘솔에서 발급받은 API Key / API Secret 입력
  3) 노드의 `Authentication`에서 `API Key (HMAC-SHA256)` 선택
  4) 사용 시 각 요청에는 문서에 따라 HMAC-SHA256 시그니처가 자동으로 포함됩니다. 상세: [Solapi API Key 인증 방식](https://developers.solapi.com/references/authentication/api-key)

  ## 사용 가능한 노드/오퍼레이션
  리소스 `Message`에서 아래 오퍼레이션을 제공합니다.

  - Send Text Message
    - SMS/LMS/MMS 발송
    - 필수: To, From, Text
    - 선택: Subject(LMS), Image ID(MMS), Country Code(기본 82)
    - 동적 옵션: From(발신번호), Image ID(MMS 스토리지)

  - Send Kakao AlimTalk (ATA)
    - 템플릿 기반 알림톡 발송
    - 필수: To, Kakao Channel, Kakao Template
    - 선택: Template Variables(JSON), From(문자 대체발신), Country Code
    - 동적 옵션: Kakao Channel, Kakao Template

  - Send Kakao FriendTalk (CTA)
    - 카카오 친구톡 발송(채널 친구 대상)
    - 필수: To, Kakao Channel, Text
    - 선택: AD Flag, CTA Image ID, Buttons(JSON Array), From(문자 대체발신), Country Code
    - 동적 옵션: Kakao Channel, Kakao Image

  - On Message Report (Single)
    - 단건 메시지 결과 웹훅 트리거(SINGLE-REPORT)
    - 워크플로우 활성화 시 Solapi Outgoing Webhook 자동 등록/해지

  - On Group Report
    - 메시지 그룹 결과 웹훅 트리거(GROUP-REPORT)
    - 워크플로우 활성화 시 Solapi Outgoing Webhook 자동 등록/해지

  - On Commerce Action
    - 커머스 액션 트리거(결제완료 등)
    - 필수: Commerce Hook (Solapi 콘솔에서 미리 생성)
    - 워크플로우 활성화 시 선택한 Hook에 웹훅 연결/해지

  ## 필드 설명 요약
  - To: 콤마/줄바꿈으로 여러 번호 입력 가능. 국가코드는 Country Code로 설정(기본 82)
  - From: Solapi에 등록된 발신번호만 사용 가능(동적 옵션)
  - Image ID: Solapi Storage에 업로드된 파일의 fileId(동적 옵션)
  - Kakao Channel: Solapi에 등록된 PFID(동적 옵션)
  - Kakao Template: 선택한 채널에서 발송 가능한 템플릿(동적 옵션)
  - Template Variables(JSON): 템플릿 변수 맵. 예) `{ "name": "홍길동" }`
  - Buttons(JSON Array): 친구톡 버튼 배열. 예)
  ```
  [
    { "buttonName": "홈", "buttonType": "WL", "linkMo": "https://example.com" }
  ]
  ```

  ## 권한(Scopes)
  - 기본 제공 스코프로 대부분의 기능 동작
  - 최소 필요 스코프 예시
    - 메시지 발송: `message:write`
    - 발신번호 조회: `senderid:read`
    - 스토리지 조회: `storage:read`
    - 카카오: `kakao:read kakao:write`
    - 웹훅: `webhook:read webhook:write`
    - 커머스: `commerce:read commerce:write`

  ## 트러블슈팅
  - 401/403 인증 오류
    - 크리덴셜 연결 상태 확인 및 재인증
    - Solapi 앱의 Redirect URI에 n8n 크리덴셜 리다이렉트 URL 등록 여부 확인
  - 템플릿/채널 목록 비어있음
    - Solapi 콘솔에서 채널/PFID 및 템플릿 등록 상태 확인
    - 해당 채널에 발송 가능한 템플릿인지 확인
  - 문자 대체발신 미동작
    - `From(문자 대체발신)`을 비워두면 대체발송 비활성화됨. 등록된 발신번호 선택 필요
  - 웹훅 미수신
    - 워크플로우 활성화 상태 확인
    - 방화벽/외부 접근 가능한 n8n 호스트인지 확인
    - Solapi 콘솔의 Outgoing Webhook 목록에 등록 여부 확인

  ## 라이선스
  MIT

ai_documentation_summary:
  {"purpose":"A community node that enables n8n to send messages via Solapi, supporting SMS/LMS/MMS, Kakao AlimTalk, Kakao FriendTalk, and commerce actions.","capabilities":["Send text messages (SMS/LMS/MMS)","Send Kakao AlimTalk (ATA) notifications","Send Kakao FriendTalk (CTA) messages","Trigger on single message reports via webhooks","Trigger on group message reports via webhooks","Trigger on commerce actions (e.g., payment completion) via webhooks","Dynamic options for sender number, image IDs, and Kakao channels/templates"],"authentication":"OAuth2 or API Key (HMAC-SHA256)","commonUseCases":["Automated customer notifications via Kakao templates","Transactional SMS/MMS delivery","Commerce order status updates","Message delivery monitoring and reporting"],"limitations":["Sender numbers must be pre-registered in Solapi","Image IDs must be uploaded to Solapi Storage first","Kakao channels and templates must be configured in Solapi console","Webhook triggers require workflow activation and proper firewall configuration"],"relatedNodes":["n8n-nodes-core.httpRequest","n8n-nodes-core.webhook","n8n-nodes-core.credentialManager"]}


node_type: n8n-nodes-autobound.autobound
package_name: n8n-nodes-autobound
display_name: Autobound
description: Generate AI-powered personalized sales content and retrieve insights using Autobound
category: Community
development_style: declarative
version: 3.1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: autobound
author_github_url: https://github.com/jasonhowie/
npm_package_name: n8n-nodes-autobound
npm_version: 2.3.3
community_fetched_at: 2026-04-16T09:43:36.318Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "content",
      "options": [
        {
          "name": "Content",
          "value": "content",
          "description": "Generate personalized sales content"
        },
        {
          "name": "Insight",
          "value": "insights",
          "description": "Retrieve insights for contacts and companies"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "generateEmail",
      "options": [
        {
          "name": "Generate Email",
          "value": "generateEmail",
          "action": "Generate an email",
          "description": "Generate personalized email content"
        },
        {
          "name": "Generate LinkedIn Message",
          "value": "generateLinkedIn",
          "action": "Generate a linkedin message",
          "description": "Generate personalized LinkedIn message"
        },
        {
          "name": "Generate Cold Call Script",
          "value": "generateColdCall",
          "action": "Generate a cold call script",
          "description": "Generate a cold call script"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "content"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getInsights",
      "options": [
        {
          "name": "Get Insights",
          "value": "getInsights",
          "action": "Get insights",
          "description": "Retrieve insights for a contact or company"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "insights"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Generate Email",
      "value": "generateEmail",
      "action": "Generate an email",
      "description": "Generate personalized email content",
      "resource": "content"
    },
    {
      "name": "Generate LinkedIn Message",
      "value": "generateLinkedIn",
      "action": "Generate a linkedin message",
      "description": "Generate personalized LinkedIn message",
      "resource": "content"
    },
    {
      "name": "Generate Cold Call Script",
      "value": "generateColdCall",
      "action": "Generate a cold call script",
      "description": "Generate a cold call script",
      "resource": "content"
    },
    {
      "name": "Get Insights",
      "value": "getInsights",
      "action": "Get insights",
      "description": "Retrieve insights for a contact or company",
      "resource": "insights"
    }
  ]

npm_readme:
  # n8n-nodes-autobound

  <img src="https://github.com/jasonhowie/n8n-nodes-autobound/raw/main/nodes/Autobound/autobound-logo.svg" alt="Autobound Logo" width="300" />

  An n8n community node for Autobound's AI-powered sales content generation and insights API.

  [![npm version](https://badge.fury.io/js/n8n-nodes-autobound.svg)](https://www.npmjs.com/package/n8n-nodes-autobound)
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

  ## 🚀 Features

  - **AI-Powered Content Generation**: Create personalized emails, LinkedIn messages, and cold call scripts
  - **Insights Retrieval**: Access detailed prospect and company insights
  - **Multi-Language Support**: Generate content in 10+ languages
  - **Advanced Personalization**: Leverage 350+ insight types for hyper-targeted content
  - **Email Sequences**: Generate multi-step email campaigns
  - **Content Rewriting**: Improve existing content with AI

  ## 📦 Installation

  ### Prerequisites

  - An [Autobound](https://autobound.ai) API key

  ### Self-hosted n8n

  In a self-hosted n8n instance, you can install community nodes directly:

  1. Navigate to **Settings** → **Community Nodes**
  2. Click **Install**
  3. Enter `n8n-nodes-autobound` and click **Install**
  4. The node will be available immediately

  Alternatively, you can install via npm:

  ```bash
  # Install globally for global n8n installations
  npm install -g n8n-nodes-autobound

  # Or install locally in your n8n project
  npm install n8n-nodes-autobound
  ```

  After npm installation, restart your n8n instance to load the new node.

  ### n8n Cloud

  For n8n cloud users:

  1. Go to **Settings** → **Community Nodes**
  2. Click **Install**
  3. Enter `n8n-nodes-autobound` and click **Install**
  4. The node will be installed automatically

  > **Note**: Community nodes are available on n8n cloud for Pro and Enterprise plans.

  ## 🔧 Configuration

  1. **Add Credentials**:
     - In n8n, go to **Settings → Credentials**
     - Click **Add Credential**
     - Search for "Autobound API"
     - Enter your Autobound API key

  2. **Get Your API Key**:
     - Sign up at [Autobound](https://autobound.ai)
     - Navigate to Settings → API Keys
     - Generate a new API key

  ## 🎯 Usage

  ### Content Generation

  The Autobound node supports three types of content generation:

  #### 1. Email Generation

  ```json
  {
  	"resource": "content",
  	"operation": "generateEmail",
  	"contactEmail": "prospect@company.com",
  	"userEmail": "you@company.com",
  	"additionalContext": "Prospect showed interest in cybersecurity solutions",
  	"tone": "professional",
  	"writingStyle": "consultative",
  	"wordCount": 150
  }
  ```

  #### 2. LinkedIn Messages

  ```json
  {
  	"resource": "content",
  	"operation": "generateLinkedIn",
  	"contactEmail": "prospect@company.com",
  	"userEmail": "you@company.com",
  	"messageType": "connection_request",
  	"tone": "friendly",
  	"wordCount": 100
  }
  ```

  #### 3. Cold Call Scripts

  ```json
  {
  	"resource": "content",
  	"operation": "generateColdCall",
  	"contactEmail": "prospect@company.com",
  	"userEmail": "you@company.com",
  	"callDuration": "60_seconds",
  	"tone": "professional"
  }
  ```

  ### Insights Retrieval (v1.4)

  Get detailed insights about prospects and companies:

  ```json
  {
  	"resource": "insights",
  	"contactEmail": "prospect@company.com",
  	"insightSubtype": "companyMarketTrends, financialEarningsCall"
  }
  ```

  ## 🔥 Advanced Features

  ### Email Sequences

  Generate multi-step email campaigns:

  ```json
  {
  	"resource": "content",
  	"operation": "generateEmail",
  	"contactEmail": "prospect@company.com",
  	"userEmail": "you@company.com",
  	"sequenceNumberOfEmails": 3,
  	"writingStyle": "challenger_sale"
  }
  ```

  ### Content Rewriting

  Improve existing content:

  ```json
  {
  	"resource": "content",
  	"operation": "generateEmail",
  	"contactEmail": "prospect@company.com",
  	"userEmail": "you@company.com",
  	"contentToRewrite": "Your existing email content here..."
  }
  ```

  ### Multiple Variations

  Generate multiple content variations:

  ```json
  {
  	"resource": "content",
  	"operation": "generateEmail",
  	"contactEmail": "prospect@company.com",
  	"userEmail": "you@company.com",
  	"n": 3
  }
  ```

  ### Insight Control

  Fine-tune which insights to include or exclude:

  ```json
  {
  	"resource": "content",
  	"operation": "generateEmail",
  	"contactEmail": "prospect@company.com",
  	"userEmail": "you@company.com",
  	"enabledInsights": "companyMarketTrends, newsEvents, jobOpenings",
  	"disabledInsights": "personalInterests, schoolMascot"
  }
  ```

  ## 📋 Available Parameters

  ### Required Fields

  - `contactEmail` or `contactLinkedinUrl`: Prospect identifier
  - `userEmail` or `userLinkedinUrl`: Seller identifier (for content generation)

  ### Optional Fields

  - `additionalContext`: Custom context or intent signals
  - `tone`: Content tone (professional, casual, friendly, etc.)
  - `language`: Content language (10+ supported)
  - `wordCount`: Target word count
  - `n`: Number of variations to generate
  - `enabledInsights`: Comma-separated insight types to include
  - `disabledInsights`: Comma-separated insight types to exclude
  - `valueProposition`: Your company's value proposition
  - `salesAsset`: Sales collateral to reference

  ## 🎨 Writing Styles (Email)

  - **Challenger Sale**: Challenge prospect's thinking with insights
  - **Consultative**: Advisory approach with expert positioning
  - **CXO Pitch**: Direct, strategic communication for executives
  - **Data-Driven**: Focus on metrics and ROI
  - **Problem-Solver**: Identify and solve specific challenges
  - **Storyteller**: Use narratives and case studies
  - **Value-Based**: Emphasize business value and outcomes

  ## 🌍 Supported Languages

  - English, Spanish, French, German, Italian
  - Portuguese, Dutch, Russian, Chinese, Japanese

  ## 💡 Example Workflows

  ### Lead Qualification + Content Generation

  1. **HTTP Request** → Get prospect data from CRM
  2. **Autobound** → Generate personalized email
  3. **Send Email** → Deliver via your email service
  4. **CRM Update** → Log the outreach

  ### Multi-Channel Outreach

  1. **Autobound** → Generate email content
  2. **Autobound** → Generate LinkedIn message
  3. **Autobound** → Generate call script
  4. **Parallel execution** → Send via multiple channels

  ### Content A/B Testing

  1. **Autobound** → Generate 3 email variations (`n: 3`)
  2. **Function** → Randomly select version
  3. **Send Email** → Deliver selected variation
  4. **Track** → Monitor response rates

  ## 🔧 Troubleshooting

  ### Common Issues

  1. **Node not appearing**: Restart n8n after installation
  2. **Credential errors**: Verify API key in Autobound dashboard
  3. **Rate limits**: Implement delays between requests
  4. **Large responses**: Consider using pagination for insights

  ### Debug Mode

  Enable debug logging to see request/response details:

  ```json
  {
  	"debug": true
  }
  ```

  ## 📊 API Limits

  - **Rate Limits**: Respect Autobound's API rate limits
  - **Content Length**: Max 500 words for generated content
  - **Variations**: Max 5 variations per request
  - **Sequences**: Max 10 emails per sequence

  ## 🤝 Contributing

  This is a community node. Contributions are welcome!

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. Submit a pull request

  ## 📄 License

  MIT License - see [LICENSE](LICENSE) file for details.

  ## 🔗 Links

  - [Autobound Website](https://autobound.ai)
  - [Autobound API Documentation](https://autobound-api.readme.io/)
  - [n8n Documentation](https://docs.n8n.io/)
  - [Community Nodes Guide](https://docs.n8n.io/integrations/community-nodes/)

  ## 📞 Support

  - **Issues**: [GitHub Issues](https://github.com/jasonhowie/n8n-nodes-autobound/issues)
  - **Autobound Help Center**: [https://help.autobound.ai/en](https://help.autobound.ai/en)
  - **n8n Community**: [n8n Community Forum](https://community.n8n.io/)

  ## 📋 Changelog

  ### v2.3.2 (Latest)

  - **Fixed**: Email sequences now properly parse and return individual emails as separate items
    - When generating an email sequence, each email is now returned as its own item with proper structure
    - Each item includes the email subject, body, and metadata (insights used, value props, etc.)
    - Items are numbered (1 of 3, 2 of 3, etc.) for easy identification

  ### v2.3.1

  - Fixed credential test configuration
  - Updated Insights API from v1.2 to v1.4
  - Updated installation documentation for cloud users

  ### v2.2.1

  - Fixed npm package logo display

  ### v2.0.0

  - Renamed package from `n8n-node-autobound` to `n8n-nodes-autobound`
  - Added Generate Insights API support (v1.2)
  - Multi-resource node structure
  - 350+ insight types support

  ---

  Made with ❤️ by the n8n community and [Autobound](https://autobound.ai)

ai_documentation_summary:
  {"purpose":"Generates AI-powered personalized sales content (emails, LinkedIn messages, cold call scripts) and retrieves prospect/company insights using Autobound's API.","capabilities":["Generate personalized email campaigns","Generate LinkedIn connection requests and messages","Generate cold call scripts","Retrieve prospect and company insights","Generate multi-step email sequences","Rewrite existing content","Generate multiple content variations","Filter insights by inclusion/exclusion rules","Support 10+ languages","Apply 7+ writing styles"],"authentication":"API key required (Autobound API key)","commonUseCases":["Automated cold outreach campaigns","Personalized email sequences for sales teams","LinkedIn prospecting automation","Lead qualification with insight retrieval","Content optimization and rewriting"],"limitations":["Community node (requires n8n Pro/Enterprise plan for n8n Cloud)","Requires valid Autobound API key","Self-hosted n8n requires manual installation and restart"],"relatedNodes":["HTTP Request","Wait","Switch","Set","Split In Batches"]}


node_type: n8n-nodes-abyssale.abyssale
package_name: n8n-nodes-abyssale
display_name: Abyssale
description: Consume Abyssale API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: remyabyssale
author_github_url: https://github.com/getabyssale/
npm_package_name: n8n-nodes-abyssale
npm_version: 1.0.3
community_fetched_at: 2026-04-16T09:43:36.319Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "banner",
      "options": [
        {
          "name": "Banner",
          "value": "banner"
        },
        {
          "name": "Design",
          "value": "design"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "generateSingle",
      "options": [
        {
          "name": "Export Banners",
          "value": "exportBanners",
          "action": "Export banners",
          "description": "Export one or multiple previously generated visuals in .zip format"
        },
        {
          "name": "Generate HTML5 Banner Ads",
          "value": "generateHTML5BannerAds",
          "action": "Generate html5 banner ads",
          "description": "Generate HTML5 banner ads from an animated design"
        },
        {
          "name": "Generate Multi-Format Animated GIFs",
          "value": "generateMultiFormatGIFs",
          "action": "Generate multi format animated gifs",
          "description": "Generate multiple animated GIFs at once from a design created in Abyssale"
        },
        {
          "name": "Generate Multi-Format Images",
          "value": "generateMultiFormatImages",
          "action": "Generate multiple images",
          "description": "Generate a multiple images (JPEG, PNG, PDF,..) from a design"
        },
        {
          "name": "Generate Multi-Format PDFs for Printing",
          "value": "generateMultiFormatPDFs",
          "action": "Generate multi format pdfs for printing",
          "description": "Generate multiple printable PDFs at once from a design created in Abyssale"
        },
        {
          "name": "Generate Multi-Format Videos",
          "value": "generateMultiFormatVideos",
          "action": "Generate multi format videos",
          "description": "Generate multiple videos at once from an animated design created in Abyssale"
        },
        {
          "name": "Generate Single Image",
          "value": "generateSingle",
          "action": "Generate a single image",
          "description": "Generate a single image (JPEG, PNG, PDF) from a design"
        },
        {
          "name": "Get Banner",
          "value": "getBanner",
          "action": "Get banner",
          "description": "Retrieves the details about a previously generated banner"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "banner"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getDesign",
      "options": [
        {
          "name": "Get a Design",
          "value": "getDesign",
          "action": "Get a design",
          "description": "Retrieves the details about a design you created on Abyssale"
        },
        {
          "name": "Get a Design Format",
          "value": "getDesignFormat",
          "action": "Get a design format",
          "description": "Retrieves the details about a format added to a design you created on Abyssale"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "design"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Export Banners",
      "value": "exportBanners",
      "action": "Export banners",
      "description": "Export one or multiple previously generated visuals in .zip format",
      "resource": "banner"
    },
    {
      "name": "Generate HTML5 Banner Ads",
      "value": "generateHTML5BannerAds",
      "action": "Generate html5 banner ads",
      "description": "Generate HTML5 banner ads from an animated design",
      "resource": "banner"
    },
    {
      "name": "Generate Multi-Format Animated GIFs",
      "value": "generateMultiFormatGIFs",
      "action": "Generate multi format animated gifs",
      "description": "Generate multiple animated GIFs at once from a design created in Abyssale",
      "resource": "banner"
    },
    {
      "name": "Generate Multi-Format Images",
      "value": "generateMultiFormatImages",
      "action": "Generate multiple images",
      "description": "Generate a multiple images (JPEG, PNG, PDF,..) from a design",
      "resource": "banner"
    },
    {
      "name": "Generate Multi-Format PDFs for Printing",
      "value": "generateMultiFormatPDFs",
      "action": "Generate multi format pdfs for printing",
      "description": "Generate multiple printable PDFs at once from a design created in Abyssale",
      "resource": "banner"
    },
    {
      "name": "Generate Multi-Format Videos",
      "value": "generateMultiFormatVideos",
      "action": "Generate multi format videos",
      "description": "Generate multiple videos at once from an animated design created in Abyssale",
      "resource": "banner"
    },
    {
      "name": "Generate Single Image",
      "value": "generateSingle",
      "action": "Generate a single image",
      "description": "Generate a single image (JPEG, PNG, PDF) from a design",
      "resource": "banner"
    },
    {
      "name": "Get Banner",
      "value": "getBanner",
      "action": "Get banner",
      "description": "Retrieves the details about a previously generated banner",
      "resource": "banner"
    },
    {
      "name": "Get a Design",
      "value": "getDesign",
      "action": "Get a design",
      "description": "Retrieves the details about a design you created on Abyssale",
      "resource": "design"
    },
    {
      "name": "Get a Design Format",
      "value": "getDesignFormat",
      "action": "Get a design format",
      "description": "Retrieves the details about a format added to a design you created on Abyssale",
      "resource": "design"
    }
  ]

npm_readme:
  # n8n-nodes-abyssale

  This is an n8n community node. It lets you use Abyssale API in your n8n workflows.

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/sustainable-use-license/) workflow automation platform.

  [Abyssale](https://www.abyssale.com/) helps you generate creative content at scale.

  [Installation](#installation)
  [Operations](#operations)
  [Credentials](#credentials)
  [Compatibility](#compatibility)
  [Usage](#usage)
  [Resources](#resources)

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ## Operations

  - Banner
    - Export banners
    - Generate html5 banner ads
    - Generate multi format animated gifs
    - Generate multiple images
    - Generate multiple format pds for printing
    - Generate multiple format videos
    - Generate a single image
    - Get banner
    - Watch banner creation
    - Watch banner batch creation
    - Watch banners export
  - Design
    - Get a design
    - Get a design format
    - Watch design status changes

  ## Credentials

  You can use abyssale access token to use this node.

  ### Access token

  1. Follow this [tutorial](https://help.abyssale.com/en/articles/329008-how-to-create-your-abyssale-api-key) to generate your api key.
  2. Copy the token.

  ## Compatibility

  Compatible with n8n@1.117.3 or later

  ## Usage

  ### Generate marketing visuals with n8n
  Transform your n8n workflows with automated visual generation. 
  Pull data from any source and turn it into engaging visuals for every channel - from social posts to email campaigns.

  #### Basic image generation
  1. Add an **Abyssale** node to your workflow.
  2. Select the **Generate a single image** operation from the node's dropdown menu.
  3. Add your Abyssale API credential.
  4. Choose a static design. The design must be created in the Abyssale app beforehand and will be used as the base for your generated images.
  5. Select your desired format you want to use.
  6. Configure the design elements you want to customize. Map input data to specific elements like text, images, or colors to personalize each generated visual.
  7. Execute the node by clicking **Execute step**.
  8. Abyssale will automatically generate an image with your specified element modifications applied.

  ## Resources

  * [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  * [Abyssale API docs](https://developers.abyssale.com/)

ai_documentation_summary:
  {"purpose":"This n8n community node enables the consumption of the Abyssale API to generate creative marketing visuals, including banners, images, videos, and animated GIFs.","capabilities":["Generate HTML5 banner ads","Generate multi-format animated GIFs","Generate multiple images","Generate multiple format PDFs for printing","Generate multiple format videos","Generate a single image","Export banners","Get banner details","Watch banner creation status","Watch banner batch creation status"],"authentication":"API key (Abyssale access token)","commonUseCases":["Automated social media post generation","Email campaign visual creation","Dynamic marketing material personalization","Multi-channel content scaling"],"limitations":["Static designs must be created in the Abyssale app beforehand","Requires n8n version 1.117.3 or later","Community node (not official n8n product)"],"relatedNodes":["n8n-nodes-base.httpRequest","n8n-nodes-base.code"]}


node_type: n8n-nodes-htmlcsstoimage.htmlCssToImage
package_name: n8n-nodes-htmlcsstoimage
display_name: HTML to Image
description: Convert HTML/CSS or a URL to an Image
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: htmlcsstoimage
author_github_url: https://github.com/HtmlCssToImg/
npm_package_name: n8n-nodes-htmlcsstoimage
npm_version: 0.3.1
community_fetched_at: 2026-04-16T09:43:36.320Z
ai_summary_generated_at: 2026-03-25 22:47:20
properties_schema:
  [
    {
      "name": "operation",
      "type": "options",
      "default": "htmlToImage",
      "options": [
        {
          "name": "HTML to Image",
          "value": "htmlToImage",
          "action": "Convert html to an image",
          "description": "Convert HTML and CSS to an image"
        },
        {
          "name": "URL to Image",
          "value": "urlToImage",
          "action": "Capture screenshot of a website",
          "description": "Capture a screenshot of a website"
        }
      ],
      "displayName": "Operation",
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "HTML to Image",
      "value": "htmlToImage",
      "action": "Convert html to an image",
      "description": "Convert HTML and CSS to an image"
    },
    {
      "name": "URL to Image",
      "value": "urlToImage",
      "action": "Capture screenshot of a website",
      "description": "Capture a screenshot of a website"
    }
  ]

npm_readme:
  # n8n-nodes-htmlcsstoimage

  This is an n8n community node. It lets you use htmlcsstoimg.com in your n8n workflows.

  htmlcsstoimg.com is a service that allows you to convert HTML and CSS into images and screenshots of websites.

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.

  [Installation](#installation)  
  [Operations](#operations)  
  [Credentials](#credentials)
  [Compatibility](#compatibility)
  [Resources](#resources)

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ## Operations

  *   **HTML to Image**: Convert HTML and CSS to an image.
  *   **Capture Website Screenshot**: Capture a screenshot of a website from a URL.

  ## Credentials

  You will need to sign up for an account at [htmlcsstoimg.com](https://htmlcsstoimg.com/) to get an API key.

  ## Compatibility

  This node is tested against n8n version 1.0.0 and above.

  ## Resources

  * [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  * [htmlcsstoimg.com documentation](https://htmlcsstoimg.com/docs)

ai_documentation_summary:
  {"purpose":"Converts HTML/CSS code or website URLs into image files using the htmlcsstoimg.com service.","capabilities":["Convert HTML and CSS to an image","Capture a screenshot of a website from a URL","Customize rendering via HTML/CSS input","Generate images from live web pages"],"authentication":"API key required (obtained from htmlcsstoimg.com account)","commonUseCases":["Generating product mockups from HTML designs","Creating website screenshots for documentation","Converting code snippets to visual previews","Automating social media image generation from web content"],"limitations":["Dependent on external service (htmlcsstoimg.com) availability and rate limits","Requires external account setup and API key management","May have rendering constraints based on the third-party service's capabilities"],"relatedNodes":["HTTP Request","Code","Image Output"]}


node_type: n8n-nodes-nele-ai.neleAi
package_name: n8n-nodes-nele-ai
display_name: nele.ai
description: Search for results in your nele.ai knowledge database
category: Miscellaneous
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: gal.digital.npm
author_github_url: https://github.com/gal-digital-gmbh/
npm_package_name: n8n-nodes-nele-ai
npm_version: 1.0.4
community_fetched_at: 2026-04-16T09:43:36.321Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Chat",
          "value": "chat",
          "description": "Generate text responses via a conversational model"
        },
        {
          "name": "Knowledge Database",
          "value": "documentCollection",
          "description": "Manage and search your knowledge databases (aka document collections)"
        },
        {
          "name": "Image",
          "value": "image",
          "description": "Generate and retrieve images based on text prompts"
        },
        {
          "name": "Audio",
          "value": "audio",
          "description": "Transcribe and retrieve audio files as text"
        },
        {
          "name": "Model",
          "value": "model",
          "description": "Retrieve available models"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "completionSync",
      "options": [
        {
          "name": "Completion",
          "value": "completionSync",
          "action": "Generate a text response",
          "description": "Creates a response for a given chat message history"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "chat"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "search",
      "options": [
        {
          "name": "Search",
          "value": "search",
          "action": "Search knowledge database",
          "description": "Returns relevant results for a given search query"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "documentCollection"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "generate",
      "options": [
        {
          "name": "Generate",
          "value": "generate",
          "action": "Generate an image",
          "description": "Creates an image from a text prompt"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "image"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "transcribe",
      "options": [
        {
          "name": "Transcribe",
          "value": "transcribe",
          "action": "Transcribe an audio file",
          "description": "Creates a text transcription from an audio file"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "audio"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "load",
      "options": [
        {
          "name": "List Models",
          "value": "load",
          "action": "List AI models",
          "description": "Lists all available AI models"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "model"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Completion",
      "value": "completionSync",
      "action": "Generate a text response",
      "description": "Creates a response for a given chat message history",
      "resource": "chat"
    },
    {
      "name": "Search",
      "value": "search",
      "action": "Search knowledge database",
      "description": "Returns relevant results for a given search query",
      "resource": "documentCollection"
    },
    {
      "name": "Generate",
      "value": "generate",
      "action": "Generate an image",
      "description": "Creates an image from a text prompt",
      "resource": "image"
    },
    {
      "name": "Transcribe",
      "value": "transcribe",
      "action": "Transcribe an audio file",
      "description": "Creates a text transcription from an audio file",
      "resource": "audio"
    },
    {
      "name": "List Models",
      "value": "load",
      "action": "List AI models",
      "description": "Lists all available AI models",
      "resource": "model"
    }
  ]

npm_readme:
  # nele.ai n8n community node

  This is an n8n community node that lets you use [nele.ai](https://www.nele.ai) AI tools, chat completions, image generators, and knowledge database operations in your n8n workflows.

  [nele.ai](https://www.nele.ai) provides a unified API for a variety of AI models and tools, including chat, image generation, audio transcription, and document collection search.

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ## Operations

  This node supports the following operations:

  - **Chat**: Generate text responses via conversational AI models.
  - **Image**: Generate and retrieve images based on text prompts.
  - **Audio**: Transcribe audio files to text.
  - **Knowledge Database**: Search and manage your knowledge databases (document collections).
  - **Model**: List available AI models.

  ## Credentials

  You need a nele.ai API key use this node.

  1. Sign up at [nele.ai](https://www.nele.ai) and obtain your API key.
  2. In n8n, add new credentials for "nele.ai API" and enter your API key.

  For more details, see the [nele.ai manual](https://www.nele.ai/en/functions/information-for-users/program-settings/api-key).

  ## Compatibility

  - Requires n8n version 1.109.1 or higher.
  - Requires node.js version 22.19.0 or higher.

  ## Usage

  After installing the node and configuring your credentials, you can add the "nele.ai" node to your workflows and select the desired resource and operation.

  - Use **Chat** for conversational AI.
  - Use **Image** to generate images from prompts.
  - Use **Audio** to transcribe audio files.
  - Use **Knowledge Database** to search your document collections.

  Refer to the in-app parameter descriptions for guidance on each operation.

  ## Resources

  - [nele.ai homepage](https://www.nele.ai)
  - [nele.ai manual](https://www.nele.ai/en/functions)
  - [nele.ai API documentation](https://api.aieva.io/docs)
  - [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)

ai_documentation_summary:
  {"purpose":"This n8n community node enables integration with nele.ai to perform AI tasks including chat, image generation, audio transcription, and knowledge database operations.","capabilities":["Generate text responses via conversational AI models","Generate and retrieve images from text prompts","Transcribe audio files to text","Search and manage knowledge databases (document collections)","List available AI models"],"authentication":"API key required","commonUseCases":["Conversational AI chatbots","Image generation workflows","Audio file transcription","Document retrieval from custom knowledge bases"],"limitations":["Requires n8n version 1.109.1 or higher","Requires node.js version 22.19.0 or higher"],"relatedNodes":["n8n-nodes-core-ai","n8n-nodes-core-http-request","n8n-nodes-core-code"]}


node_type: n8n-nodes-channel-talk.channelTalkTrigger
package_name: n8n-nodes-channel-talk
display_name: Channel Talk Trigger
description: Starts the workflow when Channel Talk events occur
category: Community
development_style: declarative
version: 1
properties_schema: []
operations: []
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: zoyi
author_github_url: https://github.com/channel-io/
npm_package_name: n8n-nodes-channel-talk
npm_version: 0.2.2
community_fetched_at: 2026-04-16T09:43:36.322Z
ai_summary_generated_at: 2026-03-25 22:51:43
npm_readme:
  # n8n-nodes-channel-talk

  This is an n8n community node. It lets you work with Channel Talk in your n8n workflows: send messages and retrieve channel info.

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/sustainable-use-license/) workflow automation platform.

  [Installation](#installation)
  [Operations](#operations)
  [Credentials](#credentials)
  [Compatibility](#compatibility)
  [Resources](#resources)

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ## Operations

  - Bot
    - List (GET /open/v5/bots)
    - Create or Update (POST /open/v5/bots)
    - Delete (DELETE /open/v5/bots/{botId})
  - Channel
    - Get: Retrieve the channel connected with your API credentials (GET /open/v5/channel)
  - Manager
    - List (GET /open/v5/managers)
    - Get (GET /open/v5/managers/{managerId})
  - Team Chat
    - List (GET /open/v5/groups)
    - Get (GET /open/v5/groups/{groupId})
    - Get by Name (GET /open/v5/groups/@{groupName})
    - Update (PATCH /open/v5/groups/{groupId})
    - Get Sessions (GET /open/v5/groups/{groupId}/sessions)
    - Get Messages (GET /open/v5/groups/{groupId}/messages)
    - Get Messages by Name (GET /open/v5/groups/@{groupName}/messages)
    - Get Messages File (GET /open/v5/groups/{groupId}/messages/file)
    - Get Messages File by Name (GET /open/v5/groups/@{groupName}/messages/file)
    - Create Message - Plain Text (POST /open/v5/groups/{groupId}/messages)
    - Create Message - Plain Text by Name (POST /open/v5/groups/@{groupName}/messages)
    - Create Message - Blocks (POST /open/v5/groups/{groupId}/messages)
    - Create Message - Blocks by Name (POST /open/v5/groups/@{groupName}/messages)
  - User
    - Get by User ID (GET /open/v5/users/{userId})
    - Get by Member ID (GET /open/v5/users/@{memberId})
    - Create (POST /open/v5/users)
    - Create or Update (PUT /open/v5/users/@{memberId})
    - Update (PATCH /open/v5/users/{userId})
    - Delete by User ID (DELETE /open/v5/users/{userId})
    - Delete by Member ID (DELETE /open/v5/users/@{memberId})
    - Block (POST /open/v5/users/{userId}/block)
    - Unblock (DELETE /open/v5/users/{userId}/block)
    - Issue User Token by User ID (PUT /open/v5/users/{userId}/user-token/issue)
    - Issue User Token by Member ID (PUT /open/v5/users/@{memberId}/user-token/issue)
    - Issue Session JWT (PUT /open/v5/users/@{memberId}/session-jwt/issue)
    - Touch (POST /open/v5/users/{userId}/touch)
  - User Chat
    - List (GET /open/v5/user-chats)
    - Get (GET /open/v5/user-chats/{userChatId})
    - Update (PATCH /open/v5/user-chats/{userChatId})
    - Delete (DELETE /open/v5/user-chats/{userChatId})
    - Open (PUT /open/v5/user-chats/{userChatId}/open)
    - Close (PATCH /open/v5/user-chats/{userChatId}/close)
    - Snooze (PUT /open/v5/user-chats/{userChatId}/snooze)
    - Remove (DELETE /open/v5/user-chats/{userChatId}/remove)
    - Assign to Manager (PATCH /open/v5/user-chats/{userChatId}/assign-to/managers/{managerId})
    - Invite (PATCH /open/v5/user-chats/{userChatId}/invite)
    - Get Cases (GET /open/v5/user-chats/cases)
    - Get Sessions (GET /open/v5/user-chats/{userChatId}/sessions)
    - Get Messages (GET /open/v5/user-chats/{userChatId}/messages)
    - Get Messages File (GET /open/v5/user-chats/{userChatId}/messages/file)
    - Create Message (POST /open/v5/user-chats/{userChatId}/messages)
    - Get Meets Messages (GET /open/v5/user-chats/{userChatId}/meets/{messageId}/messages)
    - Get Meets Recording (GET /open/v5/user-chats/{userChatId}/meets/{messageId}/recording)
  - Webhook
    - List (GET /open/v5/webhooks)
    - Get (GET /open/v5/webhooks/{webhookId})
    - Create (POST /open/v5/webhooks)
    - Update (PATCH /open/v5/webhooks/{webhookId})
    - Delete (DELETE /open/v5/webhooks/{webhookId})

  ## Credentials

  Create Open API credentials in Channel Talk and set the following fields:

  - Access Key
  - Access Secret

  These are sent as `x-access-key` and `x-access-secret` headers in each request.

  Refer to the Channel developers documentation for details:
  https://developers.channel.io/docs/authentication-2

  ## Compatibility

  Compatible with n8n@1.60.0 or later

  ## Resources

  - [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  - [Channel Open API docs](https://developers.channel.io/docs)

ai_documentation_summary:
  {"purpose":"This n8n community node enables interaction with Channel Talk's Open API, allowing users to manage bots, channels, teams, users, and messages within automated workflows.","capabilities":["List, create, update, and delete bots","Retrieve connected channel information","List, get, update, and delete team chats (groups)","Retrieve and create messages (plain text and blocks) in team chats","Manage user data including creation, updates, deletion, blocking, and token issuance","Handle user chats including opening, closing, snoozing, and assigning to managers","Retrieve and create messages in user chats","List, create, update, and delete webhooks","Issue session JWTs and user tokens"],"authentication":"API Key (Access Key and Access Secret sent as x-access-key and x-access-secret headers)","commonUseCases":["Automated customer support workflows using team chat messages","User management automation (onboarding, blocking, token generation)","Bot lifecycle management in automated scripts","Webhook management for custom integrations"],"limitations":["Community node (not official n8n node)","Requires specific Channel Talk API credentials","Compatible only with n8n version 1.60.0 or later"],"relatedNodes":["n8n-nodes-core.webhook","n8n-nodes-core.httpRequest","n8n-nodes-core.manualTrigger"]}


node_type: @picsart/n8n-nodes-picsart-creative-apis.picsartImage
package_name: @picsart/n8n-nodes-picsart-creative-apis
display_name: Picsart Image AI
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: romiksar
author_github_url: https://github.com/PicsArt/n8n-plugin.git
npm_package_name: @picsart/n8n-nodes-picsart-creative-apis
npm_version: 5.3.2
community_fetched_at: 2026-04-16T09:43:36.323Z
ai_summary_generated_at: 2026-03-25 22:51:43
description:
  Image processing with Picsart API: convert formats, crop, resize, remove backgrounds, enhance, upscale, restore faces, watermark, and analyze images

properties_schema:
  [
    {
      "name": "operation",
      "type": "options",
      "default": "Remove Background",
      "options": [
        {
          "name": "Car Classify",
          "value": "Car Classify",
          "action": "Classify car image",
          "description": "Classify a car image into categories (exterior, interior, engine, undercarriage, other)"
        },
        {
          "name": "Convert PNG to JPG",
          "value": "Convert PNG to JPG",
          "action": "Convert PNG image to JPG format",
          "description": "Convert PNG images to JPG format"
        },
        {
          "name": "Convert PNG to WEBP",
          "value": "Convert PNG to WEBP",
          "action": "Convert PNG image to WEBP format",
          "description": "Convert PNG images to WEBP format"
        },
        {
          "name": "Crop Image",
          "value": "Crop Image",
          "action": "Crop image to specific dimensions",
          "description": "Crop image to specified width and height"
        },
        {
          "name": "Describe Image",
          "value": "Describe Image",
          "action": "Generate text description from image",
          "description": "Generate a detailed text description for the provided image (image-to-text)"
        },
        {
          "name": "Enhance",
          "value": "Enhance",
          "action": "Enhance and upscale an image",
          "description": "Enhance and upscale an image"
        },
        {
          "name": "Face Enhance",
          "value": "Face Enhance",
          "action": "Enhance faces in photos",
          "description": "Turn old, blurry photos into clear portraits with AI face restoration"
        },
        {
          "name": "Hashtag",
          "value": "Hashtag",
          "action": "Generate hashtags from image",
          "description": "Analyze image and suggest relevant hashtags for the content"
        },
        {
          "name": "Remove Background",
          "value": "Remove Background",
          "action": "Remove background from an image",
          "description": "Remove background from an image"
        },
        {
          "name": "Resize Image",
          "value": "Resize Image",
          "action": "Resize image to specific dimensions",
          "description": "Resize image to specified width and height"
        },
        {
          "name": "Ultra Enhance",
          "value": "Ultra Enhance",
          "action": "Ultra enhance with generative model",
          "description": "Generative upscaling with high frequency detail (2x-16x, up to 64Mpx)"
        },
        {
          "name": "Ultra Upscale",
          "value": "Ultra Upscale",
          "action": "Ultra upscale an image up to 16x",
          "description": "Ultra upscale an image with advanced AI (2x-16x)"
        },
        {
          "name": "Watermark",
          "value": "Watermark",
          "action": "Add watermark to image",
          "description": "Add a watermark or logo to protect images from unauthorized usage"
        }
      ],
      "displayName": "Operation",
      "noDataExpression": true
    },
    {
      "name": "resource",
      "type": "options",
      "default": "Image URL",
      "options": [
        {
          "name": "DATA",
          "value": "DATA"
        },
        {
          "name": "Image URL",
          "value": "Image URL"
        }
      ],
      "displayName": "Resource",
      "displayOptions": {
        "show": {
          "operation": [
            "Remove Background",
            "Enhance",
            "Car Classify",
            "Describe Image",
            "Watermark",
            "Hashtag",
            "Convert PNG to JPG",
            "Convert PNG to WEBP",
            "Crop Image",
            "Resize Image"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "resource",
      "type": "options",
      "default": "Image URL",
      "options": [
        {
          "name": "Image URL",
          "value": "Image URL"
        },
        {
          "name": "Binary Data",
          "value": "DATA"
        }
      ],
      "description": "Choose whether to provide an image URL or binary image data",
      "displayName": "Resource",
      "displayOptions": {
        "show": {
          "operation": [
            "Ultra Upscale"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "resource",
      "type": "options",
      "default": "Image URL",
      "options": [
        {
          "name": "Image URL",
          "value": "Image URL"
        },
        {
          "name": "Binary Data",
          "value": "DATA"
        }
      ],
      "description": "Choose whether to provide an image URL or binary image data",
      "displayName": "Resource",
      "displayOptions": {
        "show": {
          "operation": [
            "Ultra Enhance"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "resource",
      "type": "options",
      "default": "Image URL",
      "options": [
        {
          "name": "Image URL",
          "value": "Image URL"
        },
        {
          "name": "Binary Data",
          "value": "DATA"
        }
      ],
      "description": "Choose whether to provide an image URL or binary image data",
      "displayName": "Resource",
      "displayOptions": {
        "show": {
          "operation": [
            "Face Enhance"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Car Classify",
      "value": "Car Classify",
      "action": "Classify car image",
      "description": "Classify a car image into categories (exterior, interior, engine, undercarriage, other)"
    },
    {
      "name": "Convert PNG to JPG",
      "value": "Convert PNG to JPG",
      "action": "Convert PNG image to JPG format",
      "description": "Convert PNG images to JPG format"
    },
    {
      "name": "Convert PNG to WEBP",
      "value": "Convert PNG to WEBP",
      "action": "Convert PNG image to WEBP format",
      "description": "Convert PNG images to WEBP format"
    },
    {
      "name": "Crop Image",
      "value": "Crop Image",
      "action": "Crop image to specific dimensions",
      "description": "Crop image to specified width and height"
    },
    {
      "name": "Describe Image",
      "value": "Describe Image",
      "action": "Generate text description from image",
      "description": "Generate a detailed text description for the provided image (image-to-text)"
    },
    {
      "name": "Enhance",
      "value": "Enhance",
      "action": "Enhance and upscale an image",
      "description": "Enhance and upscale an image"
    },
    {
      "name": "Face Enhance",
      "value": "Face Enhance",
      "action": "Enhance faces in photos",
      "description": "Turn old, blurry photos into clear portraits with AI face restoration"
    },
    {
      "name": "Hashtag",
      "value": "Hashtag",
      "action": "Generate hashtags from image",
      "description": "Analyze image and suggest relevant hashtags for the content"
    },
    {
      "name": "Remove Background",
      "value": "Remove Background",
      "action": "Remove background from an image",
      "description": "Remove background from an image"
    },
    {
      "name": "Resize Image",
      "value": "Resize Image",
      "action": "Resize image to specific dimensions",
      "description": "Resize image to specified width and height"
    },
    {
      "name": "Ultra Enhance",
      "value": "Ultra Enhance",
      "action": "Ultra enhance with generative model",
      "description": "Generative upscaling with high frequency detail (2x-16x, up to 64Mpx)"
    },
    {
      "name": "Ultra Upscale",
      "value": "Ultra Upscale",
      "action": "Ultra upscale an image up to 16x",
      "description": "Ultra upscale an image with advanced AI (2x-16x)"
    },
    {
      "name": "Watermark",
      "value": "Watermark",
      "action": "Add watermark to image",
      "description": "Add a watermark or logo to protect images from unauthorized usage"
    }
  ]

npm_readme:
  # @picsart/n8n-nodes-picsart-creative-apis

  [![npm version](https://img.shields.io/npm/v/@picsart/n8n-nodes-picsart-creative-apis.svg)](https://www.npmjs.com/package/@picsart/n8n-nodes-picsart-creative-apis)
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
  npx @n8n/scan-community-package n8n-nodes-PACKAGE

  n8n community nodes for image processing and generation using Picsart APIs.
  ## 💻 Development

  ### Setup
  ```bash
  git clone <repository-url>
  cd n8n-nodes-picsart-creative-apis
  pnpm install
  pnpm run build
  ```

  ### Available Scripts
  ```bash
  pnpm run dev        # Watch mode
  pnpm run build      # Build
  pnpm run lint       # Lint code
  pnpm run lintfix    # Fix linting issues
  ```

  ### Docker Development
  ```bash
  ./rebuild.sh
  # Open http://localhost:5678
  ```

  ## 🚀 Release

  Use the automated release script:

  ```bash
  ./release.sh patch    # Bug fixes (0.1.0 → 0.1.1)
  ./release.sh minor    # New features (0.1.0 → 0.2.0)
  ./release.sh major    # Breaking changes (0.1.0 → 1.0.0)
  ```

  Then push:
  ```bash
  git push && git push --tags
  ```

  CI/CD will automatically publish to npm.

  ## 📖 Resources

  - [Picsart API Documentation](https://docs.picsart.io/)
  - [n8n Documentation](https://docs.n8n.io/)
  - [Creating n8n Nodes](https://docs.n8n.io/integrations/creating-nodes/)
    
  ## Installation

  **For n8n Cloud:**
  1. Settings > Community Nodes > Install
  2. Enter: `@picsart/n8n-nodes-picsart-creative-apis`

  **For Self-hosted:**

  npm install @picsart/n8n-nodes-picsart-creative-apis

  Then restart n8n.

  **Setup Credentials:**
  1. Get API key from [Picsart Console](https://console.picsart.io/dashboard)
  2. In n8n: Settings → Credentials → Add "Picsart API"
  3. Paste your API key


  ## 📚 Nodes

  ### Picsart Text2Image
  Generate images from text prompts using Picsart GenAI API.

  **Key Parameters:**
  - Prompt (required): Text description of the image to generate
  - Width: Image width in pixels (default: 1024, max: 1024)
  - Height: Image height in pixels (default: 1024, max: 1024)

  **Output:** Binary image + JSON with prompt, dimensions, transaction ID, and image URL

  **Documentation:** [Picsart Text2Image API](https://docs.picsart.io/reference/genai-text2image-1)

  ### Picsart Enhance
  Upscale and enhance images with AI.

  **Key Parameters:**
  - Image URL (required, supports JPG/PNG/WEBP)
  - Upscale Factor: 2x, 4x, 6x, 8x, 16x
  - Format: JPG | PNG | WEBP

  **Output:** Binary image + JSON with URL and credits

  ### Picsart Remove Background

  **Key Parameters:**
  - Image URL (required)
  - Output Type: cutout | mask
  - Background: color, image URL, or transparent
  - Effects: blur, stroke, shadow
  - Format: JPG | PNG | WEBP

  **Output:** Binary image + JSON with URL and credits

ai_documentation_summary:
  {"purpose":"This n8n community node enables image processing and generation using the Picsart API, supporting tasks like format conversion, cropping, resizing, background removal, enhancement, upscaling, face restoration, watermarking, and image analysis.","capabilities":["Text-to-image generation from text prompts","Image upscaling and enhancement","Background removal with customizable outputs","Format conversion (JPG/PNG/WEBP)","Image cropping and resizing","Face restoration","Watermarking","Image analysis"],"authentication":"API key required","commonUseCases":["Generating images from text descriptions for content creation","Upscaling low-resolution images for high-quality output","Removing backgrounds from product photos for e-commerce","Enhancing image quality for social media posts"],"limitations":["Text-to-image generation limited to 1024x1024 maximum dimensions","Upscale factors limited to specific multipliers (2x, 4x, 6x, 8x, 16x)","Background removal output types restricted to cutout or mask formats"],"relatedNodes":["Picsart Text2Image","Picsart Enhance","Picsart Remove Background"]}


node_type: @starhunter/n8n-nodes-graphql.starhunter
package_name: @starhunter/n8n-nodes-graphql
display_name: Starhunter
description: Interact with Starhunter
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: andreas-ullrich-elunic
author_github_url: https://github.com/starhunter-gmbh/n8n-nodes-graphql.git
npm_package_name: @starhunter/n8n-nodes-graphql
npm_version: 0.2.2
community_fetched_at: 2026-04-16T09:43:36.323Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "person",
      "options": [
        {
          "name": "Candidate",
          "value": "candidate"
        },
        {
          "name": "Email",
          "value": "email"
        },
        {
          "name": "Employee",
          "value": "employee"
        },
        {
          "name": "Person",
          "value": "person"
        },
        {
          "name": "Project",
          "value": "project"
        },
        {
          "name": "Project Candidate",
          "value": "projectCandidate"
        },
        {
          "name": "Task",
          "value": "task"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getBirthdays",
      "options": [
        {
          "name": "Get Birthdays",
          "value": "getBirthdays",
          "action": "Get persons with birthdays on a date",
          "description": "Get all persons with birthdays on a specific date"
        },
        {
          "name": "Get by ID",
          "value": "getById",
          "action": "Get a person by ID",
          "description": "Retrieve a single person by their ID"
        },
        {
          "name": "Search",
          "value": "search",
          "action": "Search persons",
          "description": "Search for persons by name"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "person"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "search",
      "options": [
        {
          "name": "Search",
          "value": "search",
          "action": "Search candidates",
          "description": "Search for candidates by ID, name, or birth date"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "candidate"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "search",
      "options": [
        {
          "name": "Get Current User",
          "value": "getCurrent",
          "action": "Get current authenticated user",
          "description": "Get the employee record for the authenticated user"
        },
        {
          "name": "Search",
          "value": "search",
          "action": "Search employees",
          "description": "Search for employees by ID or name"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "employee"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "log",
      "options": [
        {
          "name": "Log Email",
          "value": "log",
          "action": "Log an email activity",
          "description": "Log an email activity in Starhunter"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "email"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "add",
      "options": [
        {
          "name": "Add to Project",
          "value": "add",
          "action": "Add candidate to project",
          "description": "Add a candidate to a project with optional status"
        },
        {
          "name": "Get By Status Change Date",
          "value": "getByStatusChangeDate",
          "action": "Get candidates by status change date",
          "description": "Get project candidates whose status changed X days ago"
        },
        {
          "name": "Update Status",
          "value": "updateStatus",
          "action": "Update presentation status",
          "description": "Update the status of a presentation with optional comment"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "projectCandidate"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "create",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a task",
          "description": "Create a new task in Starhunter"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "task"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "search",
      "options": [
        {
          "name": "Search",
          "value": "search",
          "action": "Search projects",
          "description": "Search for projects by status"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "project"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Get Birthdays",
      "value": "getBirthdays",
      "action": "Get persons with birthdays on a date",
      "description": "Get all persons with birthdays on a specific date",
      "resource": "person"
    },
    {
      "name": "Get by ID",
      "value": "getById",
      "action": "Get a person by ID",
      "description": "Retrieve a single person by their ID",
      "resource": "person"
    },
    {
      "name": "Search",
      "value": "search",
      "action": "Search persons",
      "description": "Search for persons by name",
      "resource": "person"
    },
    {
      "name": "Search",
      "value": "search",
      "action": "Search candidates",
      "description": "Search for candidates by ID, name, or birth date",
      "resource": "candidate"
    },
    {
      "name": "Get Current User",
      "value": "getCurrent",
      "action": "Get current authenticated user",
      "description": "Get the employee record for the authenticated user",
      "resource": "employee"
    },
    {
      "name": "Search",
      "value": "search",
      "action": "Search employees",
      "description": "Search for employees by ID or name",
      "resource": "employee"
    },
    {
      "name": "Log Email",
      "value": "log",
      "action": "Log an email activity",
      "description": "Log an email activity in Starhunter",
      "resource": "email"
    },
    {
      "name": "Add to Project",
      "value": "add",
      "action": "Add candidate to project",
      "description": "Add a candidate to a project with optional status",
      "resource": "projectCandidate"
    },
    {
      "name": "Get By Status Change Date",
      "value": "getByStatusChangeDate",
      "action": "Get candidates by status change date",
      "description": "Get project candidates whose status changed X days ago",
      "resource": "projectCandidate"
    },
    {
      "name": "Update Status",
      "value": "updateStatus",
      "action": "Update presentation status",
      "description": "Update the status of a presentation with optional comment",
      "resource": "projectCandidate"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a task",
      "description": "Create a new task in Starhunter",
      "resource": "task"
    },
    {
      "name": "Search",
      "value": "search",
      "action": "Search projects",
      "description": "Search for projects by status",
      "resource": "project"
    }
  ]

npm_readme:
  # @starhunter/n8n-nodes-graphql

  This is an n8n community node that integrates with [Starhunter](https://starhunter.com) in your n8n workflows.

  Starhunter is a customer relationship management (CRM) system designed for recruiting and talent management. This node allows you to automate interactions with persons, candidates, employees, emails, project candidates, and tasks in Starhunter.

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/sustainable-use-license/) workflow automation platform.

  [Installation](#installation)
  [Operations](#operations)
  [Credentials](#credentials)
  [Compatibility](#compatibility)
  [Usage](#usage)
  [Resources](#resources)
  [Version history](#version-history)

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ## Operations

  ### Person

  - **Get Birthdays**: Retrieve persons whose birthday falls on a specific date (today or a custom date in MM-DD format)
  - **Get by ID**: Retrieve a single person by their ID
  - **Search**: Search for persons by name (partial match supported)

  ### Candidate

  - **Search**: Search for candidates by ID, name, or birth date

  ### Employee

  - **Get Current**: Get the employee record for the currently authenticated user
  - **Search**: Search for employees by ID or name

  ### Email

  - **Log**: Log an email activity in Starhunter (records sender, recipient, subject, and body)

  ### Project Candidate

  - **Get by Status Change Date**: Retrieve project candidates whose status changed a specified number of days ago

  ### Task

  - **Create**: Create a new task with optional deadline, assignee, and target entity

  ## Credentials

  To use this node, you need to authenticate with your Starhunter instance using an API access token.

  ### Prerequisites

  1. Access to a Starhunter instance
  2. A valid API access token from your Starhunter account settings

  ### Setting up credentials in n8n

  1. In n8n, go to **Credentials** and click **Add Credential**
  2. Search for **Starhunter API**
  3. Enter the following:
     - **Base URL**: The base URL of your Starhunter instance (e.g., `https://your-company.starhunter.software`).
     - **Access Token**: Your Starhunter API access token
  4. Click **Save** to test the connection

  For API documentation, refer to your instance's built-in docs at `https://<your-instance>.starhunter.software/Api/docs`.

  ## Compatibility

  - **Minimum n8n version**: 1.0.0
  - **n8n Nodes API version**: 1
  - **AI Tool compatible**: Yes (can be used as a tool in n8n AI workflows)

  Tested with n8n version 1.x.

  ## Usage

  ### Example: Birthday notifications

  Create a workflow that runs daily to fetch persons with birthdays today and send notification emails or Slack messages to your team.

  1. Add a **Schedule Trigger** node set to run daily
  2. Add the **Starhunter** node with:
     - Resource: Person
     - Operation: Get Birthdays
     - Use Today: enabled
  3. Connect to an **Email** or **Slack** node to send notifications

  ### Example: Log emails to CRM

  Automatically log emails sent through your workflow back to Starhunter for tracking purposes.

  1. After sending an email in your workflow, add the **Starhunter** node
  2. Configure with:
     - Resource: Email
     - Operation: Log
     - Map the from, to, subject, and body fields from your email node

  ### Example: Track candidate status changes

  Monitor candidates who changed status a specific number of days ago for follow-up actions.

  1. Add a **Schedule Trigger** node
  2. Add the **Starhunter** node with:
     - Resource: Project Candidate
     - Operation: Get by Status Change Date
     - Status: Your target status (e.g., "Ident")
     - Days Ago: Number of days to look back

  ## Resources

  - [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  - [Starhunter](https://starhunter.software)

  ## Version history

  ### 0.1.1

  - Initial public release
  - Added publishConfig for npm public access

  ### 0.1.0

  - Initial release with support for:
    - Person operations (Get Birthdays, Get by ID, Search)
    - Candidate operations (Search)
    - Employee operations (Get Current, Search)
    - Email operations (Log)
    - Project Candidate operations (Get by Status Change Date)
    - Task operations (Create)

ai_documentation_summary:
  {"purpose":"This n8n community node enables automation of interactions within the Starhunter CRM system, covering recruiting and talent management tasks.","capabilities":["Retrieve persons by ID, search by name, or get birthdays for a specific date","Search for candidates by ID, name, or birth date","Retrieve current authenticated employee record","Search for employees by ID or name","Log email activities (sender, recipient, subject, body) into Starhunter","Retrieve project candidates based on status change date","Create new tasks with deadline, assignee, and target entity"],"authentication":"API key required (Starhunter API access token)","commonUseCases":["Automated birthday notifications for employees","Tracking candidate status changes for follow-up actions","Logging workflow-generated emails into the CRM","Retrieving current user employee profile"],"limitations":["Requires access to a Starhunter instance","Requires a valid API access token from Starhunter account settings"],"relatedNodes":["n8n Schedule Trigger","n8n Email","n8n Slack"]}


node_type: n8n-nodes-streamline.streamline
package_name: n8n-nodes-streamline
display_name: Streamline for Shopify
description: Call Streamline APIs
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: streamline-connector
author_github_url: https://github.com/streamline-connector/
npm_package_name: n8n-nodes-streamline
npm_version: 0.5.0
community_fetched_at: 2026-04-16T09:43:36.324Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "productActions",
      "options": [
        {
          "name": "Order",
          "value": "orderActions"
        },
        {
          "name": "Product",
          "value": "productActions"
        },
        {
          "name": "Additional",
          "value": "additionalActions"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getOne",
      "options": [
        {
          "name": "Get an Order",
          "value": "getOne",
          "action": "Get an order",
          "routing": {
            "request": {
              "url": "/api/order-data",
              "method": "GET"
            }
          },
          "description": "Get an order using email or order number"
        },
        {
          "name": "Get Many Orders",
          "value": "getMany",
          "action": "Get many orders",
          "routing": {
            "request": {
              "url": "/api/order-data",
              "method": "GET"
            }
          },
          "description": "Get paginated list of all orders"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "orderActions"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getRecommendations",
      "options": [
        {
          "name": "Get AI Product Recommendations",
          "value": "getRecommendations",
          "action": "Get AI product recommendations",
          "routing": {
            "request": {
              "url": "/products/recommendations",
              "method": "GET"
            }
          },
          "description": "Get AI-based product suggestions"
        },
        {
          "name": "Get Inventory Locations",
          "value": "getLocations",
          "action": "Get inventory locations",
          "routing": {
            "request": {
              "url": "/products/locations",
              "method": "GET"
            }
          },
          "description": "Retrieve store locations"
        },
        {
          "name": "Create a Discount",
          "value": "createDiscount",
          "action": "Create a discount",
          "routing": {
            "request": {
              "url": "/api/create-discount-code",
              "method": "POST"
            }
          },
          "description": "Create a new discount code"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "additionalActions"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getOne",
      "options": [
        {
          "name": "Create a Product",
          "value": "createProduct",
          "action": "Create a product",
          "routing": {
            "request": {
              "url": "/products/create-product",
              "method": "POST"
            }
          },
          "description": "Create a new product in your store"
        },
        {
          "name": "Create a Variant",
          "value": "createVariant",
          "action": "Create a variant",
          "routing": {
            "request": {
              "url": "/products/create-variant",
              "method": "POST"
            }
          },
          "description": "Add new variants to an existing product"
        },
        {
          "name": "Delete a Product",
          "value": "deleteProduct",
          "action": "Delete a product",
          "routing": {
            "request": {
              "url": "/products/delete-product",
              "method": "POST"
            }
          },
          "description": "Permanently delete a product"
        },
        {
          "name": "Delete a Variant",
          "value": "deleteVariant",
          "action": "Delete a variant",
          "routing": {
            "request": {
              "url": "/products/delete-variant",
              "method": "POST"
            }
          },
          "description": "Delete a variant from a product"
        },
        {
          "name": "Get a Product",
          "value": "getOne",
          "action": "Get a product",
          "routing": {
            "request": {
              "url": "/products/inventory",
              "method": "GET"
            }
          },
          "description": "Get a product by ID or title"
        },
        {
          "name": "Get Many Products",
          "value": "getMany",
          "action": "Get many products",
          "routing": {
            "request": {
              "url": "/products/inventory",
              "method": "GET"
            }
          },
          "description": "Get inventory for all products (by shop)"
        },
        {
          "name": "Update a Product",
          "value": "updateProduct",
          "action": "Update a product",
          "routing": {
            "request": {
              "url": "/products/update-product",
              "method": "POST"
            }
          },
          "description": "Update product title, vendor, tags, status, etc"
        },
        {
          "name": "Update a Variant",
          "value": "updateVariant",
          "action": "Update a variant",
          "routing": {
            "request": {
              "url": "/products/update-variant",
              "method": "POST"
            }
          },
          "description": "Update variant price, SKU, options, etc"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "productActions"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Get an Order",
      "value": "getOne",
      "action": "Get an order",
      "routing": {
        "request": {
          "url": "/api/order-data",
          "method": "GET"
        }
      },
      "description": "Get an order using email or order number",
      "resource": "orderActions"
    },
    {
      "name": "Get Many Orders",
      "value": "getMany",
      "action": "Get many orders",
      "routing": {
        "request": {
          "url": "/api/order-data",
          "method": "GET"
        }
      },
      "description": "Get paginated list of all orders",
      "resource": "orderActions"
    },
    {
      "name": "Get AI Product Recommendations",
      "value": "getRecommendations",
      "action": "Get AI product recommendations",
      "routing": {
        "request": {
          "url": "/products/recommendations",
          "method": "GET"
        }
      },
      "description": "Get AI-based product suggestions",
      "resource": "additionalActions"
    },
    {
      "name": "Get Inventory Locations",
      "value": "getLocations",
      "action": "Get inventory locations",
      "routing": {
        "request": {
          "url": "/products/locations",
          "method": "GET"
        }
      },
      "description": "Retrieve store locations",
      "resource": "additionalActions"
    },
    {
      "name": "Create a Discount",
      "value": "createDiscount",
      "action": "Create a discount",
      "routing": {
        "request": {
          "url": "/api/create-discount-code",
          "method": "POST"
        }
      },
      "description": "Create a new discount code",
      "resource": "additionalActions"
    },
    {
      "name": "Create a Product",
      "value": "createProduct",
      "action": "Create a product",
      "routing": {
        "request": {
          "url": "/products/create-product",
          "method": "POST"
        }
      },
      "description": "Create a new product in your store",
      "resource": "productActions"
    },
    {
      "name": "Create a Variant",
      "value": "createVariant",
      "action": "Create a variant",
      "routing": {
        "request": {
          "url": "/products/create-variant",
          "method": "POST"
        }
      },
      "description": "Add new variants to an existing product",
      "resource": "productActions"
    },
    {
      "name": "Delete a Product",
      "value": "deleteProduct",
      "action": "Delete a product",
      "routing": {
        "request": {
          "url": "/products/delete-product",
          "method": "POST"
        }
      },
      "description": "Permanently delete a product",
      "resource": "productActions"
    },
    {
      "name": "Delete a Variant",
      "value": "deleteVariant",
      "action": "Delete a variant",
      "routing": {
        "request": {
          "url": "/products/delete-variant",
          "method": "POST"
        }
      },
      "description": "Delete a variant from a product",
      "resource": "productActions"
    },
    {
      "name": "Get a Product",
      "value": "getOne",
      "action": "Get a product",
      "routing": {
        "request": {
          "url": "/products/inventory",
          "method": "GET"
        }
      },
      "description": "Get a product by ID or title",
      "resource": "productActions"
    },
    {
      "name": "Get Many Products",
      "value": "getMany",
      "action": "Get many products",
      "routing": {
        "request": {
          "url": "/products/inventory",
          "method": "GET"
        }
      },
      "description": "Get inventory for all products (by shop)",
      "resource": "productActions"
    },
    {
      "name": "Update a Product",
      "value": "updateProduct",
      "action": "Update a product",
      "routing": {
        "request": {
          "url": "/products/update-product",
          "method": "POST"
        }
      },
      "description": "Update product title, vendor, tags, status, etc",
      "resource": "productActions"
    },
    {
      "name": "Update a Variant",
      "value": "updateVariant",
      "action": "Update a variant",
      "routing": {
        "request": {
          "url": "/products/update-variant",
          "method": "POST"
        }
      },
      "description": "Update variant price, SKU, options, etc",
      "resource": "productActions"
    }
  ]

npm_readme:
  # n8n-nodes-streamline

  ![n8n.io - Workflow Automation](https://raw.githubusercontent.com/n8n-io/n8n/master/assets/n8n-logo.png)

  This is an n8n community node that connects your **Shopify eCommerce data** to [n8n](https://n8n.io) through the **Streamline Connector API**. It enables you to retrieve products, orders, and inventory data from Shopify to automate workflows and integrate with other services.

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.

  [Installation](#installation)
  [Operations](#operations)
  [Credentials](#credentials)
  [Compatibility](#compatibility)
  [Usage](#usage)
  [Resources](#resources)
  [Version History](#version-history)

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ### Community Nodes (Recommended)

  For users on n8n v0.187.0+:

  1. Go to **Settings > Community Nodes**
  2. Select **Install**
  3. Enter `n8n-nodes-streamline` in **Enter npm package name**
  4. Agree to the [risks](https://docs.n8n.io/integrations/community-nodes/risks/) of using community nodes
  5. Select **Install**

  After installation, the Streamline node will be available in your nodes panel.

  ### Manual Installation

  For self-hosted n8n instances:

  ```bash
  cd ~/.n8n/custom
  npm install n8n-nodes-streamline
  ```

  Restart n8n and the **Streamline** node will appear in your node list.

  ### Docker

  To use this node with Docker:

  ```bash
  docker run -it --rm \
    -p 5678:5678 \
    -v ~/.n8n:/home/node/.n8n \
    -v ~/.n8n/custom:/home/node/custom \
    -e N8N_CUSTOM_EXTENSIONS="/home/node/custom/node_modules/n8n-nodes-streamline" \
    n8nio/n8n
  ```

  ## Operations

  The Streamline node supports the following operations:

  | Resource                          | Operation               | Description                                                       |
  | --------------------------------- | ----------------------- | ----------------------------------------------------------------- |
  | **Get an order**                  | Get an order            | Get an order using email or order number (`/api/order-data`)      |
  | **Get many orders**               | Get many orders         | Get paginated list of all orders (`/api/order-data`, page & size) |
  | **Get AI Product Recommendation** | Get Recommendations     | Get AI-based product suggestions (`/products/recommendations`)    |
  | **Get a product**                 | Get a product           | Get a product by title – inventory/stock (`/products/inventory`)  |
  | **Get Inventory Locations**       | Get Inventory Locations | Retrieve store locations (`/products/locations`)                  |
  | **Create a Discount**             | Create a discount       | Create a new discount code (`/api/create-discount-code`)          |

  ## Credentials

  To use the Streamline node, you need to set up Streamline API credentials:

  1. In n8n, go to **Credentials → New**
  2. Search for and select **Streamline API**
  3. Enter your credentials:
     - **Streamline Shop ID** – Your Streamline access key (found in the app dashboard > Any endpoint page)
  4. Click **Save** and test the connection

  Once configured, select these credentials when using any Streamline node in your workflows.

  ### Getting API Credentials

  To obtain your Streamline API credentials:

  1. Visit [Streamline Connector](https://streamlineconnector.com)
  2. Sign up or log in to your account
  3. Navigate to your API settings
  4. Generate or copy your API key

  ## Compatibility

  - **Minimum n8n version:** 0.187.0
  - **Tested with n8n version:** 1.0.0+
  - **Node.js version:** 22+

  ## Usage

  ### Example Workflows

  Here are some common use cases for the Streamline node:

  **1. Sync Orders to Google Sheets**

  - Trigger: Schedule (every hour)
  - Streamline Node: Get Orders
  - Google Sheets: Append data
  - Use case: Create automated sales reports

  **2. Low Stock Alerts**

  - Trigger: Schedule (daily)
  - Streamline Node: Get Inventory
  - IF Node: Check if stock < threshold
  - Slack/Email Node: Send alert
  - Use case: Monitor inventory levels

  **3. Product Catalog Integration**

  - Trigger: Manual or Schedule
  - Streamline Node: Get Products
  - Your Service: Update product database
  - Use case: Maintain product information across platforms

  ### Tips

  - Use the **Schedule Trigger** node to automate regular data syncs
  - Combine with **IF** nodes to create conditional workflows
  - Use **Set** nodes to transform data before sending to other services
  - Enable **Error Workflows** to handle API errors gracefully

  ## Resources

  - [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
  - [Streamline Connector](https://streamlineconnector.com)
  - [n8n workflow examples](https://n8n.io/workflows)

  ### Support

  For issues and questions:

  - **Node issues:** [GitHub Issues](https://github.com/streamline-connector/n8n-nodes-streamline/issues)
  - **n8n help:** [n8n Community Forum](https://community.n8n.io/)
  - **Streamline API:** Contact devs@streamlineconnector.com

  ## Development

  ### Prerequisites

  - Node.js v22 or higher
  - npm
  - n8n (included as dev dependency)

  ### Setup

  ```bash
  # Clone the repository
  git clone https://github.com/streamline-connector/n8n-nodes-streamline.git
  cd n8n-nodes-streamline

  # Install dependencies
  npm install

  # Build the node
  npm run build
  ```

  ### Local Development

  Start n8n with your node loaded:

  ```bash
  npm run dev
  ```

  This command:

  - Builds your node with watch mode enabled
  - Starts n8n with your node available
  - Automatically rebuilds when you make changes
  - Opens n8n in your browser at http://localhost:5678

  ### Testing

  Run linting:

  ```bash
  npm run lint
  ```

  Auto-fix linting issues:

  ```bash
  npm run lint:fix
  ```

  ### Building for Production

  ```bash
  npm run build
  ```

  This compiles TypeScript code to the `dist/` folder.

  ## Version History

  See [CHANGELOG.md](CHANGELOG.md) for detailed version history.

  ## License

  [MIT](LICENSE.md)

  ## Contributing

  Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
  3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
  4. Push to the branch (`git push origin feature/AmazingFeature`)
  5. Open a Pull Request

  ---

  Made with ❤️ by [Streamline Connector](https://streamlineconnector.com)

ai_documentation_summary:
  {"purpose":"Connects Shopify eCommerce data to n8n via the Streamline Connector API to automate workflows for retrieving products, orders, inventory, and generating AI recommendations.","capabilities":["Retrieve single order by email or order number","Retrieve paginated list of all orders","Get AI-based product recommendations","Retrieve product details including inventory/stock levels","Retrieve store inventory locations","Create new discount codes"],"authentication":"API key required (Streamline Shop ID/Access Key)","commonUseCases":["Automated sales reporting by syncing orders to Google Sheets","Low stock alerts by monitoring inventory levels and sending notifications","Product catalog synchronization across multiple platforms"],"limitations":["Community node (requires approval and manual installation for self-hosted)","Requires n8n version 0.187.0 or higher","Requires Node.js version 22 or higher"],"relatedNodes":["Schedule Trigger","IF Node","Set Node","Google Sheets","Slack"]}


node_type: n8n-nodes-sellerassistant.sellerAssistant
package_name: n8n-nodes-sellerassistant
display_name: Seller Assistant
description: Interact with Seller Assistant API for Amazon product data
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: sellerassistant
author_github_url: https://github.com/sellerassistant/n8n-nodes-seller-assistant.git
npm_package_name: n8n-nodes-sellerassistant
npm_version: 0.1.3
community_fetched_at: 2026-04-16T09:43:36.326Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "product",
      "options": [
        {
          "name": "Converter",
          "value": "converter"
        },
        {
          "name": "FBA Inbound Shipment",
          "value": "fba"
        },
        {
          "name": "Keepa",
          "value": "keepa"
        },
        {
          "name": "Listings Item",
          "value": "listing"
        },
        {
          "name": "Product",
          "value": "product"
        },
        {
          "name": "Restriction",
          "value": "restriction"
        },
        {
          "name": "Sales Estimation",
          "value": "sales"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "identifierToAsins",
      "options": [
        {
          "name": "ASIN to Identifiers",
          "value": "asinToIdentifiers",
          "action": "Get product identifiers by ASIN",
          "routing": {
            "request": {
              "url": "=/products/{{$parameter.asin}}/identifiers",
              "method": "GET"
            }
          },
          "description": "Get array of product identifiers (UPC, EAN, etc.) based on ASIN"
        },
        {
          "name": "Identifier to ASINs",
          "value": "identifierToAsins",
          "action": "Convert upc or ean to asi ns",
          "routing": {
            "request": {
              "url": "=/converters/identifier-to-asins",
              "method": "GET"
            }
          },
          "description": "Get array of product ASINs based on UPC or EAN identifier"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "converter"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "listInboundPlans",
      "options": [
        {
          "name": "Get Inbound Plan",
          "value": "getInboundPlan",
          "action": "Get inbound plan",
          "routing": {
            "request": {
              "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}",
              "method": "GET"
            }
          },
          "description": "Get detailed information about a specific FBA inbound plan"
        },
        {
          "name": "Get Shipment",
          "value": "getShipment",
          "action": "Get shipment",
          "routing": {
            "request": {
              "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/shipments/{{$parameter.shipmentId}}",
              "method": "GET"
            }
          },
          "description": "Get detailed information about a specific shipment"
        },
        {
          "name": "Get Shipment Fees",
          "value": "getShipmentFees",
          "action": "Get shipment fees",
          "routing": {
            "request": {
              "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/shipments/{{$parameter.shipmentId}}/fees",
              "method": "GET"
            }
          },
          "description": "Get transportation and placement fees for a specific shipment"
        },
        {
          "name": "List Inbound Plans",
          "value": "listInboundPlans",
          "action": "List inbound plans",
          "routing": {
            "request": {
              "url": "=/fba/inboundPlans",
              "method": "GET"
            }
          },
          "description": "Get a paginated list of FBA inbound plans"
        },
        {
          "name": "List Plan Boxes",
          "value": "listPlanBoxes",
          "action": "List plan boxes",
          "routing": {
            "request": {
              "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/boxes",
              "method": "GET"
            }
          },
          "description": "Get all boxes across all shipments for a specific FBA inbound plan"
        },
        {
          "name": "List Plan Shipments",
          "value": "listPlanShipments",
          "action": "List plan shipments",
          "routing": {
            "request": {
              "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/shipments",
              "method": "GET"
            }
          },
          "description": "Get all shipments for a specific FBA inbound plan"
        },
        {
          "name": "List Shipment Boxes",
          "value": "listShipmentBoxes",
          "action": "List shipment boxes",
          "routing": {
            "request": {
              "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/shipments/{{$parameter.shipmentId}}/boxes",
              "method": "GET"
            }
          },
          "description": "Get all boxes for a specific shipment"
        },
        {
          "name": "List Shipment Items",
          "value": "listShipmentItems",
          "action": "List shipment items",
          "routing": {
            "request": {
              "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/shipments/{{$parameter.shipmentId}}/items",
              "method": "GET"
            }
          },
          "description": "Get all items in a specific shipment"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "fba"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getProduct",
      "options": [
        {
          "name": "Get Product",
          "value": "getProduct",
          "action": "Get keepa product data",
          "routing": {
            "request": {
              "url": "=/keepa/product",
              "method": "GET"
            }
          },
          "description": "Get detailed product info from Keepa including price history and statistics"
        },
        {
          "name": "Product Finder",
          "value": "productFinder",
          "action": "Find products with filters",
          "routing": {
            "send": {
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "=/keepa/query",
              "method": "POST"
            }
          },
          "description": "Search for products matching specified criteria with aggregated KPIs and insights"
        },
        {
          "name": "Search Products",
          "value": "searchProducts",
          "action": "Search keepa products",
          "routing": {
            "request": {
              "url": "=/keepa/search",
              "method": "GET"
            }
          },
          "description": "Search for Amazon products using keywords, returning up to 100 results per search term"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "keepa"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getItem",
      "options": [
        {
          "name": "Create Or Update Listing From ASIN",
          "value": "createOrUpdateItem",
          "action": "Create or update listing from ASIN",
          "routing": {
            "send": {
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "=/listings/items/{{encodeURIComponent($parameter.sku)}}",
              "method": "PUT"
            }
          },
          "description": "Create or update a listing by copying product data from an existing ASIN"
        },
        {
          "name": "Delete Listings Item",
          "value": "deleteItem",
          "action": "Delete listings item",
          "routing": {
            "request": {
              "url": "=/listings/items/{{encodeURIComponent($parameter.sku)}}",
              "method": "DELETE"
            }
          },
          "description": "Delete a listings item by seller SKU"
        },
        {
          "name": "Get Listings Item",
          "value": "getItem",
          "action": "Get listings item",
          "routing": {
            "request": {
              "url": "=/listings/items/{{encodeURIComponent($parameter.sku)}}",
              "method": "GET"
            }
          },
          "description": "Get a listings item by seller SKU"
        },
        {
          "name": "Search Listings Items",
          "value": "searchItems",
          "action": "Search listings items",
          "routing": {
            "send": {
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "=/listings/items",
              "method": "GET"
            }
          },
          "description": "Search for listings items by identifiers"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "listing"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Calculate Product Profit",
          "value": "calculateProfit",
          "action": "Calculate product profit by ASIN",
          "routing": {
            "request": {
              "url": "=/products/{{$parameter.asin}}/calculate",
              "method": "POST"
            }
          },
          "description": "Calculate product profit for FBA and FBM including ROI, margin, and break-even analysis"
        },
        {
          "name": "Get Product Info",
          "value": "get",
          "action": "Get product info by ASIN",
          "routing": {
            "request": {
              "url": "=/products/{{$parameter.asin}}",
              "method": "GET"
            }
          },
          "description": "Retrieve product information from Amazon by ASIN"
        },
        {
          "name": "Get Product Statistics",
          "value": "getStats",
          "action": "Get product statistics by ASIN",
          "routing": {
            "request": {
              "url": "=/products/{{$parameter.asin}}/stats",
              "method": "GET"
            }
          },
          "description": "Fetch detailed statistics for a product including monthly sales, prices, BSR, offers, reviews, and BSR drops count"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "product"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "check",
      "options": [
        {
          "name": "Check ASIN Restrictions",
          "value": "check",
          "action": "Check ASIN restrictions",
          "routing": {
            "request": {
              "url": "=/products/{{$parameter.asin}}/restriction-status",
              "method": "GET"
            }
          },
          "description": "Check if you are allowed to sell a product. Returns ALLOWED_TO_SELL, NOT_ELIGIBLE, or APPROVAL_REQUIRED."
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "restriction"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "estimateByAsin",
      "options": [
        {
          "name": "Estimate by ASIN",
          "value": "estimateByAsin",
          "action": "Estimate sales by ASIN",
          "routing": {
            "request": {
              "url": "=/products/{{$parameter.asin}}/sales",
              "method": "GET"
            }
          },
          "description": "Estimate approximate monthly sales number for a product by ASIN"
        },
        {
          "name": "Estimate by BSR",
          "value": "estimateByBsr",
          "action": "Estimate sales by BSR and category",
          "routing": {
            "request": {
              "url": "=/estimate-sales",
              "method": "POST"
            }
          },
          "description": "Estimate approximate monthly sales number based on BSR and category"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "sales"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "ASIN to Identifiers",
      "value": "asinToIdentifiers",
      "action": "Get product identifiers by ASIN",
      "routing": {
        "request": {
          "url": "=/products/{{$parameter.asin}}/identifiers",
          "method": "GET"
        }
      },
      "description": "Get array of product identifiers (UPC, EAN, etc.) based on ASIN",
      "resource": "converter"
    },
    {
      "name": "Identifier to ASINs",
      "value": "identifierToAsins",
      "action": "Convert upc or ean to asi ns",
      "routing": {
        "request": {
          "url": "=/converters/identifier-to-asins",
          "method": "GET"
        }
      },
      "description": "Get array of product ASINs based on UPC or EAN identifier",
      "resource": "converter"
    },
    {
      "name": "Get Inbound Plan",
      "value": "getInboundPlan",
      "action": "Get inbound plan",
      "routing": {
        "request": {
          "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}",
          "method": "GET"
        }
      },
      "description": "Get detailed information about a specific FBA inbound plan",
      "resource": "fba"
    },
    {
      "name": "Get Shipment",
      "value": "getShipment",
      "action": "Get shipment",
      "routing": {
        "request": {
          "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/shipments/{{$parameter.shipmentId}}",
          "method": "GET"
        }
      },
      "description": "Get detailed information about a specific shipment",
      "resource": "fba"
    },
    {
      "name": "Get Shipment Fees",
      "value": "getShipmentFees",
      "action": "Get shipment fees",
      "routing": {
        "request": {
          "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/shipments/{{$parameter.shipmentId}}/fees",
          "method": "GET"
        }
      },
      "description": "Get transportation and placement fees for a specific shipment",
      "resource": "fba"
    },
    {
      "name": "List Inbound Plans",
      "value": "listInboundPlans",
      "action": "List inbound plans",
      "routing": {
        "request": {
          "url": "=/fba/inboundPlans",
          "method": "GET"
        }
      },
      "description": "Get a paginated list of FBA inbound plans",
      "resource": "fba"
    },
    {
      "name": "List Plan Boxes",
      "value": "listPlanBoxes",
      "action": "List plan boxes",
      "routing": {
        "request": {
          "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/boxes",
          "method": "GET"
        }
      },
      "description": "Get all boxes across all shipments for a specific FBA inbound plan",
      "resource": "fba"
    },
    {
      "name": "List Plan Shipments",
      "value": "listPlanShipments",
      "action": "List plan shipments",
      "routing": {
        "request": {
          "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/shipments",
          "method": "GET"
        }
      },
      "description": "Get all shipments for a specific FBA inbound plan",
      "resource": "fba"
    },
    {
      "name": "List Shipment Boxes",
      "value": "listShipmentBoxes",
      "action": "List shipment boxes",
      "routing": {
        "request": {
          "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/shipments/{{$parameter.shipmentId}}/boxes",
          "method": "GET"
        }
      },
      "description": "Get all boxes for a specific shipment",
      "resource": "fba"
    },
    {
      "name": "List Shipment Items",
      "value": "listShipmentItems",
      "action": "List shipment items",
      "routing": {
        "request": {
          "url": "=/fba/inboundPlans/{{$parameter.inboundPlanId}}/shipments/{{$parameter.shipmentId}}/items",
          "method": "GET"
        }
      },
      "description": "Get all items in a specific shipment",
      "resource": "fba"
    },
    {
      "name": "Get Product",
      "value": "getProduct",
      "action": "Get keepa product data",
      "routing": {
        "request": {
          "url": "=/keepa/product",
          "method": "GET"
        }
      },
      "description": "Get detailed product info from Keepa including price history and statistics",
      "resource": "keepa"
    },
    {
      "name": "Product Finder",
      "value": "productFinder",
      "action": "Find products with filters",
      "routing": {
        "send": {
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "=/keepa/query",
          "method": "POST"
        }
      },
      "description": "Search for products matching specified criteria with aggregated KPIs and insights",
      "resource": "keepa"
    },
    {
      "name": "Search Products",
      "value": "searchProducts",
      "action": "Search keepa products",
      "routing": {
        "request": {
          "url": "=/keepa/search",
          "method": "GET"
        }
      },
      "description": "Search for Amazon products using keywords, returning up to 100 results per search term",
      "resource": "keepa"
    },
    {
      "name": "Create Or Update Listing From ASIN",
      "value": "createOrUpdateItem",
      "action": "Create or update listing from ASIN",
      "routing": {
        "send": {
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "=/listings/items/{{encodeURIComponent($parameter.sku)}}",
          "method": "PUT"
        }
      },
      "description": "Create or update a listing by copying product data from an existing ASIN",
      "resource": "listing"
    },
    {
      "name": "Delete Listings Item",
      "value": "deleteItem",
      "action": "Delete listings item",
      "routing": {
        "request": {
          "url": "=/listings/items/{{encodeURIComponent($parameter.sku)}}",
          "method": "DELETE"
        }
      },
      "description": "Delete a listings item by seller SKU",
      "resource": "listing"
    },
    {
      "name": "Get Listings Item",
      "value": "getItem",
      "action": "Get listings item",
      "routing": {
        "request": {
          "url": "=/listings/items/{{encodeURIComponent($parameter.sku)}}",
          "method": "GET"
        }
      },
      "description": "Get a listings item by seller SKU",
      "resource": "listing"
    },
    {
      "name": "Search Listings Items",
      "value": "searchItems",
      "action": "Search listings items",
      "routing": {
        "send": {
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "=/listings/items",
          "method": "GET"
        }
      },
      "description": "Search for listings items by identifiers",
      "resource": "listing"
    },
    {
      "name": "Calculate Product Profit",
      "value": "calculateProfit",
      "action": "Calculate product profit by ASIN",
      "routing": {
        "request": {
          "url": "=/products/{{$parameter.asin}}/calculate",
          "method": "POST"
        }
      },
      "description": "Calculate product profit for FBA and FBM including ROI, margin, and break-even analysis",
      "resource": "product"
    },
    {
      "name": "Get Product Info",
      "value": "get",
      "action": "Get product info by ASIN",
      "routing": {
        "request": {
          "url": "=/products/{{$parameter.asin}}",
          "method": "GET"
        }
      },
      "description": "Retrieve product information from Amazon by ASIN",
      "resource": "product"
    },
    {
      "name": "Get Product Statistics",
      "value": "getStats",
      "action": "Get product statistics by ASIN",
      "routing": {
        "request": {
          "url": "=/products/{{$parameter.asin}}/stats",
          "method": "GET"
        }
      },
      "description": "Fetch detailed statistics for a product including monthly sales, prices, BSR, offers, reviews, and BSR drops count",
      "resource": "product"
    },
    {
      "name": "Check ASIN Restrictions",
      "value": "check",
      "action": "Check ASIN restrictions",
      "routing": {
        "request": {
          "url": "=/products/{{$parameter.asin}}/restriction-status",
          "method": "GET"
        }
      },
      "description": "Check if you are allowed to sell a product. Returns ALLOWED_TO_SELL, NOT_ELIGIBLE, or APPROVAL_REQUIRED.",
      "resource": "restriction"
    },
    {
      "name": "Estimate by ASIN",
      "value": "estimateByAsin",
      "action": "Estimate sales by ASIN",
      "routing": {
        "request": {
          "url": "=/products/{{$parameter.asin}}/sales",
          "method": "GET"
        }
      },
      "description": "Estimate approximate monthly sales number for a product by ASIN",
      "resource": "sales"
    },
    {
      "name": "Estimate by BSR",
      "value": "estimateByBsr",
      "action": "Estimate sales by BSR and category",
      "routing": {
        "request": {
          "url": "=/estimate-sales",
          "method": "POST"
        }
      },
      "description": "Estimate approximate monthly sales number based on BSR and category",
      "resource": "sales"
    }
  ]

npm_readme:
  # n8n-nodes-sellerassistant

  This is an n8n community node. It lets you use [Seller Assistant](https://sellerassistant.app) in your n8n workflows.

  Seller Assistant is an Amazon product research tool that helps sellers analyze products, check restrictions, estimate sales, and calculate profitability.

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.

  [Installation](#installation)
  [Operations](#operations)
  [Credentials](#credentials)
  [Compatibility](#compatibility)
  [Usage](#usage)
  [Resources](#resources)

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ## Operations

  ### Converter

  - **ASIN to Identifiers** - Get array of product identifiers (UPC, EAN, etc.) based on ASIN
  - **Identifier to ASINs** - Get array of product ASINs based on UPC or EAN identifier

  ### FBA Inbound

  - **Get Inbound Plan** - Get detailed information about a specific FBA inbound plan
  - **Get Shipment** - Get detailed information about a specific shipment
  - **Get Shipment Fees** - Get transportation and placement fees for a specific shipment
  - **List Inbound Plans** - Get a paginated list of FBA inbound plans with filtering and sorting options
  - **List Plan Boxes** - Get all boxes across all shipments for a specific FBA inbound plan
  - **List Plan Shipments** - Get all shipments for a specific FBA inbound plan
  - **List Shipment Boxes** - Get all boxes for a specific shipment
  - **List Shipment Items** - Get all items in a specific shipment

  ### Keepa

  - **Get Product** - Get detailed product info from Keepa including price history, statistics, and marketplace offers
  - **Product Finder** - Search for products matching specified criteria with aggregated KPIs and insights
  - **Search Products** - Search for Amazon products using keywords, returning up to 100 results per search term

  ### Listings Items

  - **Search Listings Items** - Search for listings items by identifiers (SKU, ASIN, EAN, UPC, etc.) with filtering and pagination
  - **Get Listings Item** - Get a listings item by seller SKU with detailed information
  - **Create or Update Listing from ASIN** - Create or update a listing by copying product data from an existing ASIN
  - **Delete Listings Item** - Delete a listings item by seller SKU

  ### Product

  - **Calculate Product Profit** - Calculate product profit for FBA and FBM including ROI, margin, and break-even analysis
  - **Get Product Info** - Retrieve detailed product information from Amazon by ASIN
  - **Get Product Statistics** - Fetch detailed statistics for a product including monthly sales, prices, BSR, offers, reviews, and BSR drops count

  ### Restriction

  - **Check ASIN Restrictions** - Check if you are allowed to sell a product (returns ALLOWED_TO_SELL, NOT_ELIGIBLE, or APPROVAL_REQUIRED)

  ### Sales

  - **Estimate by ASIN** - Estimate approximate monthly sales number for a product by ASIN
  - **Estimate by BSR** - Estimate approximate monthly sales number based on BSR and category

  ## Credentials

  To use this node, you need a Seller Assistant API key:

  1. Sign up for a [Seller Assistant account](https://sellerassistant.app)
  2. Navigate to [Settings > API Keys](https://app.sellerassistant.app/settings/api-keys)
  3. Generate a new API key
  4. In n8n, create new credentials for "Seller Assistant API" and paste your API key

  ## Compatibility

  - Requires n8n version 1.0.0 or later
  - Tested with n8n versions 1.x

  ## Usage

  ### ASIN to Identifiers

  Use the "ASIN to Identifiers" operation to get product identifiers (UPC, EAN, etc.) from an ASIN:

  1. Add the Seller Assistant node to your workflow
  2. Select "Converter" as the resource
  3. Select "ASIN to Identifiers" as the operation
  4. Enter the ASIN of the product
  5. Select the Amazon marketplace domain
  6. Execute the node to get product identifiers

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns an array of identifiers (UPC, EAN, etc.) for the product. Returns an empty array if no identifiers are found.

  ### Identifier to ASINs

  Use the "Identifier to ASINs" operation to convert a UPC or EAN to Amazon ASINs:

  1. Add the Seller Assistant node to your workflow
  2. Select "Converter" as the resource
  3. Select "Identifier to ASINs" as the operation
  4. Enter the UPC or EAN identifier
  5. Select the Amazon marketplace domain
  6. Execute the node to get matching ASINs

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns an array of ASINs that match the provided UPC or EAN. Returns an empty array if no products are found.

  ### Get Inbound Plan

  Use the "Get Inbound Plan" operation to retrieve detailed information about a specific FBA inbound plan:

  1. Add the Seller Assistant node to your workflow
  2. Select "FBA Inbound" as the resource
  3. Select "Get Inbound Plan" as the operation
  4. Enter the Inbound Plan ID (Workflow ID)
  5. Select the Amazon marketplace domain
  6. Execute the node to get inbound plan details

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns detailed inbound plan information including:
  - Plan ID, name, and status
  - Source address
  - Creation and last updated timestamps
  - Marketplace IDs
  - Packing options with status
  - Placement options with status
  - Shipments list with IDs and status

  ### Get Shipment

  Use the "Get Shipment" operation to retrieve detailed information about a specific shipment:

  1. Add the Seller Assistant node to your workflow
  2. Select "FBA Inbound" as the resource
  3. Select "Get Shipment" as the operation
  4. Enter the Inbound Plan ID (Workflow ID)
  5. Enter the Shipment ID (sh... or FBA... format)
  6. Select the Amazon marketplace domain
  7. Execute the node to get shipment details

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns detailed shipment information including:
  - Shipment ID and status
  - Amazon reference ID
  - Source and destination addresses
  - Contact information
  - Tracking details (LTL and SPD)
  - Delivery window information
  - Freight information
  - Self-ship appointment details

  ### Get Shipment Fees

  Use the "Get Shipment Fees" operation to retrieve transportation and placement fees for a specific shipment:

  1. Add the Seller Assistant node to your workflow
  2. Select "FBA Inbound" as the resource
  3. Select "Get Shipment Fees" as the operation
  4. Enter the Inbound Plan ID (Workflow ID)
  5. Enter the Shipment ID (sh... or FBA... format)
  6. Select the Amazon marketplace domain
  7. Execute the node to get shipment fees

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns fee information including:
  - Placement fees with discounts and status
  - Transportation options with carrier info, shipping mode, and quotes
  - Fee amounts and currency codes
  - Expiration dates for quotes

  ### List Inbound Plans

  Use the "List Inbound Plans" operation to retrieve a paginated list of FBA inbound plans:

  1. Add the Seller Assistant node to your workflow
  2. Select "FBA Inbound" as the resource
  3. Select "List Inbound Plans" as the operation
  4. Select the Amazon marketplace domain
  5. Optionally configure filtering options (status, sort by, sort order, page size)
  6. Execute the node to get inbound plans

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns a list of inbound plans including:
  - Plan ID and name
  - Status (ACTIVE, VOIDED, SHIPPED)
  - Source address
  - Creation and last updated timestamps
  - Marketplace IDs
  - Pagination token for fetching additional pages

  ### List Plan Boxes

  Use the "List Plan Boxes" operation to retrieve all boxes across all shipments for a specific FBA inbound plan:

  1. Add the Seller Assistant node to your workflow
  2. Select "FBA Inbound" as the resource
  3. Select "List Plan Boxes" as the operation
  4. Enter the Inbound Plan ID (Workflow ID)
  5. Select the Amazon marketplace domain
  6. Optionally configure page size and pagination token
  7. Execute the node to get boxes

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns box information including:
  - Box ID
  - Package ID
  - Destination address (fulfillment center code)
  - Dimensions (length, width, height, unit)
  - Weight and weight unit
  - Items in the box with quantities
  - Expiration dates (if applicable)
  - Pagination token for fetching additional pages

  ### List Plan Shipments

  Use the "List Plan Shipments" operation to retrieve all shipments for a specific FBA inbound plan:

  1. Add the Seller Assistant node to your workflow
  2. Select "FBA Inbound" as the resource
  3. Select "List Plan Shipments" as the operation
  4. Enter the Inbound Plan ID (Workflow ID)
  5. Select the Amazon marketplace domain
  6. Execute the node to get shipments

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns shipment information including:
  - Shipment ID and status
  - Source and destination addresses
  - Contact information
  - Tracking details (LTL and SPD)
  - Delivery window information
  - Freight information
  - Self-ship appointment details

  ### List Shipment Boxes

  Use the "List Shipment Boxes" operation to retrieve all boxes for a specific shipment:

  1. Add the Seller Assistant node to your workflow
  2. Select "FBA Inbound" as the resource
  3. Select "List Shipment Boxes" as the operation
  4. Enter the Inbound Plan ID (Workflow ID)
  5. Enter the Shipment ID (sh... or FBA... format)
  6. Select the Amazon marketplace domain
  7. Optionally configure page size and pagination token
  8. Execute the node to get shipment boxes

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns box information including:
  - Package ID and Box ID
  - Destination region (warehouse ID, country, state)
  - Dimensions (length, width, height, unit)
  - Weight and weight unit
  - Items in the box with quantities and prep instructions
  - Template name
  - Pagination token for fetching additional pages

  ### List Shipment Items

  Use the "List Shipment Items" operation to retrieve all items in a specific shipment:

  1. Add the Seller Assistant node to your workflow
  2. Select "FBA Inbound" as the resource
  3. Select "List Shipment Items" as the operation
  4. Enter the Inbound Plan ID (Workflow ID)
  5. Enter the Shipment ID (sh... or FBA... format)
  6. Select the Amazon marketplace domain
  7. Optionally configure page size and detailed mode
  8. Execute the node to get shipment items

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns item information including:
  - ASIN and FNSKU
  - Merchant SKU (MSKU)
  - Quantity
  - Label owner (AMAZON, SELLER, NONE)
  - Prep instructions with fees
  - Expiration date (if applicable)
  - Manufacturing lot code (if applicable)

  ### Create or Update Listing from ASIN

  Use the "Create or Update Listing from ASIN" operation to create or update a listing by copying product data from an existing ASIN:

  1. Add the Seller Assistant node to your workflow
  2. Select "Listings Items" as the resource
  3. Select "Create or Update Listing from ASIN" as the operation
  4. Enter your unique SKU for the listing
  5. Enter the ASIN of the existing product (must not be a parent ASIN)
  6. Enter the selling price and currency
  7. Select the item condition (new, used, refurbished, collectible)
  8. Select the Amazon marketplace domain
  9. Optionally configure FBA settings, dangerous goods regulation, and attribute overrides
  10. Execute the node to create or update the listing

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns submission information including:
  - SKU of the listing
  - Status (ACCEPTED, INVALID, or VALID for validation preview)
  - Submission ID
  - Any issues with severity levels (ERROR, WARNING, INFO)

  You can use the "Validation Mode" option to preview and check for errors before actual submission.

  ### Delete Listings Item

  Use the "Delete Listings Item" operation to delete a listings item by seller SKU:

  1. Add the Seller Assistant node to your workflow
  2. Select "Listings Items" as the resource
  3. Select "Delete Listings Item" as the operation
  4. Enter the seller SKU of the item to delete
  5. Select the Amazon marketplace domain
  6. Execute the node to delete the listing

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns submission information including:
  - SKU of the deleted listing
  - Status (ACCEPTED, INVALID)
  - Submission ID
  - Any issues with severity levels (ERROR, WARNING, INFO)

  ### Get Listings Item

  Use the "Get Listings Item" operation to retrieve a listings item by seller SKU:

  1. Add the Seller Assistant node to your workflow
  2. Select "Listings Items" as the resource
  3. Select "Get Listings Item" as the operation
  4. Enter the seller SKU of the item
  5. Select the Amazon marketplace domain
  6. Optionally configure included data (summaries, attributes, issues, offers, etc.)
  7. Execute the node to get item details

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns listing item information including:
  - SKU and ASIN
  - Product summaries (condition, status, FNSKU, item name, images)
  - Attributes (structured product data)
  - Issues with severity levels and enforcement actions
  - Offers and pricing data
  - Fulfillment availability
  - Procurement information
  - Relationships (variations, package hierarchy)
  - Product types

  ### Search Listings Items

  Use the "Search Listings Items" operation to search for listings items by identifiers:

  1. Add the Seller Assistant node to your workflow
  2. Select "Listings Items" as the resource
  3. Select "Search Listings Items" as the operation
  4. Enter comma-delimited identifiers (max 20)
  5. Select the identifier type (SKU, ASIN, EAN, UPC, etc.)
  6. Select the Amazon marketplace domain
  7. Optionally configure filtering options (included data, dates, status filters, sorting, pagination)
  8. Execute the node to search listings

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns listing items including:
  - SKU and ASIN
  - Product summaries and attributes
  - Fulfillment availability
  - Issues with severity levels
  - Offers and procurement data
  - Relationships and variation info
  - Pagination token for fetching additional pages

  ### Get Keepa Product

  Use the "Get Product" operation to retrieve detailed Keepa data for an Amazon product:

  1. Add the Seller Assistant node to your workflow
  2. Select "Keepa" as the resource
  3. Select "Get Product" as the operation
  4. Choose lookup method (ASIN or UPC/EAN code)
  5. Enter the ASIN or UPC/EAN code
  6. Select the Amazon marketplace domain
  7. Configure optional parameters (stats, history, offers, etc.)
  8. Execute the node to get Keepa product data

  The node returns comprehensive Keepa data including:
  - Product details (title, brand, manufacturer, dimensions, etc.)
  - Price history for all price types (Amazon, new, used, Buy Box, etc.)
  - Sales rank history
  - Marketplace offers data (with offers parameter)
  - Buy Box information
  - Rating and review history

  ### Keepa Product Finder

  Use the "Product Finder" operation to search for products matching specified criteria:

  1. Add the Seller Assistant node to your workflow
  2. Select "Keepa" as the resource
  3. Select "Product Finder" as the operation
  4. Select the Amazon marketplace domain
  5. Enter filter criteria as JSON (see examples below)
  6. Configure optional parameters (page, perPage, sort, include search insights)
  7. Execute the node to find matching products

  The node accepts a JSON object with filter criteria. Common filter examples:

  ```json
  {
    "title": "board game",
    "current_SALES_gte": 1,
    "current_SALES_lte": 100000
  }
  ```

  ```json
  {
    "brand": ["CATAN", "Hasbro"],
    "monthlySold_gte": 100,
    "current_AMAZON_gte": 1000,
    "current_AMAZON_lte": 5000
  }
  ```

  ```json
  {
    "rootCategory": [165793011],
    "hasReviews": true,
    "current_RATING_gte": 40
  }
  ```

  Available filter categories:
  - **Text filters**: title, brand, manufacturer, model, color, size, edition, format
  - **Price filters**: current_AMAZON_gte/lte, current_NEW_gte/lte, current_BUY_BOX_SHIPPING_gte/lte
  - **Sales filters**: current_SALES_gte/lte, monthlySold_gte/lte
  - **Rating filters**: current_RATING_gte/lte, current_COUNT_REVIEWS_gte/lte
  - **Dimension filters**: packageHeight_gte/lte, packageWeight_gte/lte, itemWeight_gte/lte
  - **Buy Box filters**: buyBoxIsAmazon, buyBoxIsFBA, buyBoxStatsAmazon30_gte/lte
  - **Boolean filters**: hasReviews, isAdultProduct, isSNS, isEligibleForTradeIn

  See the [Seller Assistant API documentation](https://developer.sellerassistant.app) for the complete list of available filters.

  ### Search Keepa Products

  Use the "Search Products" operation to search for Amazon products using keywords:

  1. Add the Seller Assistant node to your workflow
  2. Select "Keepa" as the resource
  3. Select "Search Products" as the operation
  4. Enter the search term (e.g., "Catan")
  5. Select the Amazon marketplace domain
  6. Configure optional parameters (ASINs only, page, stats, history, etc.)
  7. Execute the node to search products

  The node returns up to 100 results per search term, with results ordered as on Amazon (excluding sponsored content). Each page provides up to 10 results, iterate through pages 0-9 for more results.

  Options include:
  - **ASINs Only** - Return only ASINs instead of full product objects
  - **Page** - Page number (0-9) for pagination
  - **Stats** - Include price statistics for last X days or a date range
  - **Update** - Force refresh if data is older than specified hours
  - **Include History** - Include historical data fields
  - **Include Rating** - Include rating and review count history

  ### Calculate Product Profit

  Use the "Calculate Product Profit" operation to calculate profit metrics for an Amazon product:

  1. Add the Seller Assistant node to your workflow
  2. Select "Product" as the resource
  3. Select "Calculate Product Profit" as the operation
  4. Enter the ASIN of the product
  5. Select the Amazon marketplace domain
  6. Enter the Cost of Goods (COG)
  7. Enter the FBA 3PL cost (Third Party Logistics for FBA)
  8. Enter the FBM 3PL cost (Third Party Logistics for FBM)
  9. Optionally enter an override price to use instead of the Amazon price
  10. Execute the node to calculate profit

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns profit calculations including:
  - FBA and FBM profit breakdowns (price, COG, referral fee, FBA/FBM fees, storage fee, profit)
  - ROI and margin percentages
  - Break-even price analysis
  - ROI 10% and ROI 20% price targets
  - Estimated monthly sales and revenue

  ### Get Product Info

  Use the "Get Product Info" operation to retrieve detailed information about an Amazon product:

  1. Add the Seller Assistant node to your workflow
  2. Select "Product" as the resource
  3. Select "Get Product Info" as the operation
  4. Enter the ASIN (Amazon Standard Identification Number) of the product
  5. Execute the node to retrieve product data

  The node returns comprehensive product information including title, pricing, category, and more.

  ### Get Product Statistics

  Use the "Get Product Statistics" operation to retrieve detailed statistics for an Amazon product:

  1. Add the Seller Assistant node to your workflow
  2. Select "Product" as the resource
  3. Select "Get Product Statistics" as the operation
  4. Enter the ASIN of the product
  5. Select the Amazon marketplace domain
  6. Execute the node to retrieve product statistics

  The node returns statistics including:
  - Monthly sales estimates
  - Price metrics (Amazon price, marketplace new price, Buy Box price, FBM price, FBA price) with current and historical averages
  - Best Sellers Rank (BSR) with current and historical averages
  - Number of offers and reviews
  - BSR drops count (indicator of sales spikes)

  ### Check ASIN Restrictions

  Use the "Check ASIN Restrictions" operation to check if you are allowed to sell a product:

  1. Add the Seller Assistant node to your workflow
  2. Select "Restriction" as the resource
  3. Select "Check ASIN Restrictions" as the operation
  4. Enter the ASIN of the product
  5. Select the Amazon marketplace domain
  6. Execute the node to check restrictions

  **Note:** This operation requires an Amazon seller account integration. Connect your account at [Seller Assistant Integrations](https://app.sellerassistant.app/integrations/seller-central).

  The node returns one of the following statuses:
  - `ALLOWED_TO_SELL` - You can sell this product
  - `NOT_ELIGIBLE` - You are not eligible to sell this product
  - `APPROVAL_REQUIRED` - You need approval to sell this product

  ### Estimate Sales by ASIN

  Use the "Estimate by ASIN" operation to estimate monthly sales for a product:

  1. Add the Seller Assistant node to your workflow
  2. Select "Sales" as the resource
  3. Select "Estimate by ASIN" as the operation
  4. Enter the ASIN of the product
  5. Select the Amazon marketplace domain
  6. Execute the node to get sales estimate

  The node returns the estimated monthly sales number for the product.

  ### Estimate Sales by BSR

  Use the "Estimate by BSR" operation to estimate monthly sales based on BSR and category:

  1. Add the Seller Assistant node to your workflow
  2. Select "Sales" as the resource
  3. Select "Estimate by BSR" as the operation
  4. Enter the Best Seller Rank (BSR) number
  5. Enter the root category name (e.g., "Home & Kitchen")
  6. Select the Amazon marketplace domain
  7. Execute the node to get sales estimate

  The node returns the estimated monthly sales number based on the provided BSR and category.

  ## Resources

  - [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  - [Seller Assistant Website](https://sellerassistant.app)
  - [Seller Assistant API Documentation](https://developer.sellerassistant.app)

ai_documentation_summary:
  {"purpose":"This n8n community node enables interaction with the Seller Assistant API to analyze Amazon products, manage FBA shipments, and calculate profitability for e-commerce sellers.","capabilities":["Convert ASIN to product identifiers (UPC, EAN)","Convert product identifiers (UPC, EAN) to ASINs","Retrieve and manage FBA inbound plans and shipments","Fetch product data and statistics from Keepa (price history, market offers)","Search and manage seller listings by SKU/ASIN","Calculate product profit margins and break-even analysis","Check product selling restrictions","Estimate monthly sales based on ASIN or BSR"],"authentication":"API key required (Seller Assistant API key)","commonUseCases":["Amazon product research and competitor analysis","FBA shipment planning and tracking","Product profitability calculation for e-commerce sellers","Inventory management via SKU/ASIN lookups"],"limitations":["Requires a paid Seller Assistant account and API key","Requires Amazon seller account integration for certain operations (ASIN conversion, FBA data)","Limited to Seller Assistant API capabilities","Product statistics and Keepa data depend on Keepa API availability"],"relatedNodes":["Amazon Product Research","FBA Shipment Manager","Keepa"]}


node_type: @vlm-run/n8n-nodes-vlmrun.vlmRun
package_name: @vlm-run/n8n-nodes-vlmrun
display_name: VLM Run
description: Interact with VLM Run API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: shahrear-vlm
author_github_url: https://github.com/vlm-run/n8n-nodes-vlmrun.git
npm_package_name: @vlm-run/n8n-nodes-vlmrun
npm_version: 2.2.9
community_fetched_at: 2026-04-16T09:43:36.327Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "operation",
      "type": "options",
      "default": "document",
      "options": [
        {
          "name": "Analyze Audio",
          "value": "audio",
          "action": "Analyze audio",
          "description": "Analyze audio files for transcription, speaker identification, sentiment analysis, and more"
        },
        {
          "name": "Analyze Document",
          "value": "document",
          "action": "Analyze document",
          "description": "Extract structured data from documents such as resumes, invoices, presentations, and more"
        },
        {
          "name": "Analyze Image",
          "value": "image",
          "action": "Analyze image",
          "description": "Extract information or generate captions from images"
        },
        {
          "name": "Analyze Video",
          "value": "video",
          "action": "Analyze video",
          "description": "Extract insights or transcribe content from video files"
        },
        {
          "name": "Execute Agent",
          "value": "executeAgent",
          "action": "Execute agent",
          "description": "Execute an agent"
        },
        {
          "name": "Manage Files",
          "value": "file",
          "action": "Manage files",
          "description": "List uploaded files or upload new files to VLM Run"
        }
      ],
      "displayName": "Operation",
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Analyze Audio",
      "value": "audio",
      "action": "Analyze audio",
      "description": "Analyze audio files for transcription, speaker identification, sentiment analysis, and more"
    },
    {
      "name": "Analyze Document",
      "value": "document",
      "action": "Analyze document",
      "description": "Extract structured data from documents such as resumes, invoices, presentations, and more"
    },
    {
      "name": "Analyze Image",
      "value": "image",
      "action": "Analyze image",
      "description": "Extract information or generate captions from images"
    },
    {
      "name": "Analyze Video",
      "value": "video",
      "action": "Analyze video",
      "description": "Extract insights or transcribe content from video files"
    },
    {
      "name": "Execute Agent",
      "value": "executeAgent",
      "action": "Execute agent",
      "description": "Execute an agent"
    },
    {
      "name": "Manage Files",
      "value": "file",
      "action": "Manage files",
      "description": "List uploaded files or upload new files to VLM Run"
    }
  ]

npm_readme:
  <div align="center">
  <p align="center" style="width: 100%;">
      <img src="https://raw.githubusercontent.com/vlm-run/.github/refs/heads/main/profile/assets/vlm-black.svg" alt="VLM Run Logo" width="80" style="margin-bottom: -5px; color: #2e3138; vertical-align: middle; padding-right: 5px;"><br>
  </p>
  <h2>n8n-nodes-vlmrun</h2>
  <p align="center">
  <a href="https://vlm.run"><b>Website</b></a> | <a href="https://app.vlm.run/"><b>Platform</b></a> | <a href="https://docs.vlm.run/"><b>Docs</b></a> | <a href="https://docs.vlm.run/blog"><b>Blog</b></a> | <a href="https://discord.gg/4jgyECY4rq"><b>Discord</b></a>
  </p>
  <p align="center">
  <a href="https://www.npmjs.com/package/@vlm-run/n8n-nodes-vlmrun"><img alt="NPM Version" src="https://badge.fury.io/js/%40vlm-run%2Fn8n-nodes-vlmrun.svg"></a>
  <a href="https://github.com/vlm-run/n8n-nodes-vlmrun/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/vlm-run/n8n-nodes-vlmrun.svg"></a>
  <a href="https://discord.gg/4jgyECY4rq"><img alt="Discord" src="https://img.shields.io/badge/discord-chat-purple?color=%235765F2&label=discord&logo=discord"></a>
  <a href="https://twitter.com/vlmrun"><img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/vlmrun.svg?style=social&logo=twitter"></a>
  </p>
  </div>

  This is an n8n community node. It lets you use [VLM Run](https://vlm.run) in your n8n workflows.

  VLM Run is a unified gateway for Visual AI that enables you to extract structured data from unstructured visual content like images, videos, audio, and documents using Vision Language Models (VLMs).


  ## 💾 Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ```bash
  npm i @vlm-run/n8n-nodes-vlmrun
  ```

  ## ⚙️ Operations

  - **Analyze Audio**: Analyze audio files for transcription, speaker identification, sentiment analysis, and more.
  - **Analyze Document**: Extract structured data from documents such as resumes, invoices, presentations, and more.
  - **Analyze Image**: Extract information or generate captions from images.
  - **Analyze Video**: Extract insights or transcribe content from video files.
  - **Manage Files**: List uploaded files or upload new files to VLM Run.

  ## 🔑 Credentials

  1. Sign up for a VLM Run account at [vlm.run](https://app.vlm.run/)
  2. Get your API key from the dashboard
  3. Use the API key in the n8n VLM Run node credentials

  ## 📖 Usage

  1. **Configure Credentials**:
     - Add your VLM Run API credentials in n8n
     - Set the API base URL (default: https://api.vlm.run/v1)

  2. **Add VLM Run Node**:
     - Search for "VLM Run" in the n8n nodes panel
     - Add it to your workflow

  3. **Configure Node**:
     - Select the operation (Analyze Audio, Analyze Document, Analyze Image, Analyze Video, or Manage Files)
     - Provide the required input fields (such as file data, model, or domain)
     - Configure any additional parameters as needed

  4. **Run the workflow** to process your visual or audio data with VLM Run's AI models.

  ## 📸 Screenshots

  Here are some screenshots of the n8n-nodes-vlmrun in action:

  ![VLM Run Node Overview](assets/vlmrun-overview.png)
  _Overview of the VLM Run node in n8n_

  ![VLM Run Workflow Example](assets/vlmrun-workflow.png)
  _Example workflow using the VLM Run node_

  ## 🔗 Resources

  - [n8n Community Nodes Documentation](https://docs.n8n.io/integrations/community-nodes/)
  - [VLM Run Documentation](https://docs.vlm.run/introduction)
  - [VLM Run API Reference](https://docs.vlm.run/api-reference/v1/health)
  - [Example Workflows](https://n8n.io/workflows)

  ## 🛠️ Example Use Cases

  - Extracting structured data from resumes, invoices, or utility bills
  - Cataloging and captioning product images
  - Transcribing and analyzing audio interviews or calls
  - Extracting insights from video content
  - Managing files in your VLM Run account from n8n workflows

  ## 📄 License

  [MIT](LICENSE)

ai_documentation_summary:
  {"purpose":"This n8n community node enables interaction with the VLM Run API to process visual and audio content using Vision Language Models.","capabilities":["Analyze audio files for transcription and sentiment analysis","Analyze documents to extract structured data (resumes, invoices, presentations)","Analyze images to extract information or generate captions","Analyze video files for insights or transcription","Manage files (list or upload) in the VLM Run account"],"authentication":"API key required","commonUseCases":["Extracting structured data from resumes, invoices, or utility bills","Cataloging and captioning product images","Transcribing and analyzing audio interviews or calls","Extracting insights from video content"],"limitations":[],"relatedNodes":[]}


node_type: n8n-nodes-immojump.immojump
package_name: n8n-nodes-immojump
display_name: Immojump
description: Interact with the ImmoJump API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-04-16 09:43:36
author_name: immojump
author_github_url: https://github.com/immoJUMP/
npm_package_name: n8n-nodes-immojump
npm_version: 1.11.4
community_fetched_at: 2026-04-16T09:43:36.327Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "immobilie",
      "options": [
        {
          "name": "Immobilie",
          "value": "immobilie"
        },
        {
          "name": "Contact",
          "value": "contact"
        },
        {
          "name": "Activity",
          "value": "activity"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get immobilien",
          "routing": {
            "request": {
              "qs": {
                "page": "={{$parameter.page || 1}}",
                "per_page": "={{$parameter.perPage || 20}}",
                "organisation_id": "={{$credentials.organisationId || $credentials.organizationId || $parameter.organisationId || undefined}}"
              },
              "url": "/api/v2/immobilien",
              "method": "GET"
            }
          },
          "description": "Get immobilien for the current organisation"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get immobilie",
          "routing": {
            "request": {
              "url": "=/api/v2/immobilien/{{$parameter.immobilieId}}",
              "method": "GET"
            }
          },
          "description": "Get a single immobilie by ID"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create immobilie",
          "routing": {
            "request": {
              "qs": {
                "name": "={{$parameter.name || undefined}}",
                "type": "={{$parameter.type || undefined}}",
                "organisation_id": "={{$credentials.organisationId || undefined}}"
              },
              "url": "/api/v2/immobilien",
              "body": "={{ ((parameter, credentials) => {\n    var _a;\n    const resolveExpressionValue = (value) => {\n        if (typeof value !== 'string') {\n            return value;\n        }\n        const trimmed = value.trim();\n        if (trimmed === '') {\n            return value;\n        }\n        const hasExpression = trimmed.startsWith('=');\n        if (!hasExpression || typeof $evaluateExpression !== 'function') {\n            return value;\n        }\n        const expression = trimmed.startsWith('=') ? trimmed.slice(1) : trimmed;\n        return $evaluateExpression(expression);\n    };\n    const parseObject = (value, fieldName) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null || resolved === '') {\n            return undefined;\n        }\n        if (typeof resolved === 'string') {\n            const trimmed = resolved.trim();\n            if (trimmed === '') {\n                return undefined;\n            }\n            try {\n                return JSON.parse(trimmed);\n            }\n            catch {\n                throw new Error(`${fieldName} must be valid JSON`);\n            }\n        }\n        if (typeof resolved === 'object') {\n            return resolved;\n        }\n        throw new Error(`${fieldName} must be an object or JSON string`);\n    };\n    const body = {\n        type: resolveExpressionValue(parameter.type),\n        name: resolveExpressionValue(parameter.name),\n        organisation_id: credentials.organisationId,\n    };\n    const additional = (_a = parameter.additionalFields) !== null && _a !== void 0 ? _a : {};\n    const overrides = parseObject(parameter.additionalFieldsExpression, 'additionalFieldsExpression');\n    const merged = overrides ? { ...additional, ...overrides } : additional;\n    const daten = {};\n    if (merged.adresse) {\n        daten.adresse = resolveExpressionValue(merged.adresse);\n    }\n    if (merged.kaufpreis !== undefined) {\n        daten.kaufpreis = resolveExpressionValue(merged.kaufpreis);\n    }\n    if (merged.flaeche !== undefined) {\n        daten.wohnflaeche = resolveExpressionValue(merged.flaeche);\n    }\n    if (merged.baujahr !== undefined) {\n        daten.baujahr = resolveExpressionValue(merged.baujahr);\n    }\n    if (merged.zustand) {\n        daten.zustand = resolveExpressionValue(merged.zustand);\n    }\n    if (merged.datenJson) {\n        const raw = resolveExpressionValue(merged.datenJson);\n        const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;\n        if (parsed && typeof parsed === 'object') {\n            Object.assign(daten, parsed);\n        }\n    }\n    if (Object.keys(daten).length > 0) {\n        body.daten = daten;\n    }\n    return body;\n})($parameter, $credentials) }}",
              "json": true,
              "method": "POST"
            }
          },
          "description": "Create a new immobilie in the current organisation"
        },
        {
          "name": "Add Resource Link",
          "value": "addResourceLink",
          "action": "Add resource link",
          "routing": {
            "request": {
              "url": "=/api/resource-links/immobilie/{{$parameter.immobilieId}}",
              "body": "={{ (() => {\n\tconst body = {\n\t\ttitle: $parameter.linkTitle,\n\t\turl: $parameter.linkUrl,\n\t};\n\tconst optional = $parameter.linkOptionalFields ?? {};\n\tif (optional.notes) {\n\t\tbody.notes = optional.notes;\n\t}\n\tif (optional.icon) {\n\t\tbody.icon = optional.icon;\n\t}\n\tif (optional.color) {\n\t\tbody.color = optional.color;\n\t}\n\tif (optional.orderIndex !== undefined && optional.orderIndex !== null) {\n\t\tbody.order_index = optional.orderIndex;\n\t}\n\treturn body;\n})() }}",
              "method": "POST"
            }
          },
          "description": "Attach an external link to an immobilie"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update immobilie",
          "routing": {
            "request": {
              "qs": {
                "name": "={{$parameter.updateFields?.name || undefined}}",
                "type": "={{$parameter.updateFields?.type || undefined}}",
                "adresse": "={{$parameter.updateFields?.adresse || undefined}}",
                "baujahr": "={{$parameter.updateFields?.baujahr || undefined}}",
                "zustand": "={{$parameter.updateFields?.zustand || undefined}}",
                "kaufpreis": "={{$parameter.updateFields?.kaufpreis || undefined}}",
                "sale_price": "={{$parameter.updateFields?.salePrice || undefined}}",
                "wohnflaeche": "={{$parameter.updateFields?.flaeche || undefined}}",
                "asking_price": "={{$parameter.updateFields?.askingPrice || undefined}}",
                "preview_image_id": "={{$parameter.updateFields?.previewImageId || undefined}}",
                "acquisition_price": "={{$parameter.updateFields?.acquisitionPrice || undefined}}",
                "target_sale_price": "={{$parameter.updateFields?.targetSalePrice || undefined}}"
              },
              "url": "=/api/v2/immobilien/{{$parameter.immobilieId}}",
              "body": "={{ ((parameter) => {\n    var _a;\n    const resolveJsonPath = (path) => {\n        if (typeof $json !== 'object' || $json === null) {\n            return undefined;\n        }\n        const segments = path.split('.').filter((segment) => segment !== '');\n        let current = $json;\n        for (const segment of segments) {\n            if (typeof current !== 'object' || current === null || !(segment in current)) {\n                return undefined;\n            }\n            current = current[segment];\n        }\n        return current;\n    };\n    const resolveExpressionValue = (value) => {\n        if (typeof value !== 'string') {\n            return value;\n        }\n        const trimmed = value.trim();\n        if (trimmed === '') {\n            return value;\n        }\n        const expressionCandidate = trimmed.startsWith('=') ? trimmed.slice(1).trim() : trimmed;\n        const moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n        const normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n        const isExpression = trimmed.startsWith('=') || moustacheMatch !== null;\n        if (!isExpression) {\n            return value;\n        }\n        const jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n        if (jsonPathMatch) {\n            const resolved = resolveJsonPath(jsonPathMatch[1]);\n            if (resolved !== undefined) {\n                return resolved;\n            }\n        }\n        if (typeof $evaluateExpression !== 'function') {\n            return undefined;\n        }\n        const evaluated = $evaluateExpression(normalizedExpression);\n        if (typeof evaluated === 'string' && evaluated.trim() === trimmed) {\n            return undefined;\n        }\n        return evaluated;\n    };\n    const parseObject = (value, fieldName) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null || resolved === '') {\n            return undefined;\n        }\n        if (typeof resolved === 'string') {\n            const trimmed = resolved.trim();\n            if (trimmed === '') {\n                return undefined;\n            }\n            try {\n                return JSON.parse(trimmed);\n            }\n            catch {\n                throw new Error(`${fieldName} must be valid JSON`);\n            }\n        }\n        if (typeof resolved === 'object') {\n            return resolved;\n        }\n        throw new Error(`${fieldName} must be an object or JSON string`);\n    };\n    const payload = {};\n    const fields = (_a = parameter.updateFields) !== null && _a !== void 0 ? _a : {};\n    const overrides = parseObject(parameter.updateFieldsExpression, 'updateFieldsExpression');\n    const merged = overrides ? { ...fields, ...overrides } : fields;\n    if (merged.name) {\n        payload.name = resolveExpressionValue(merged.name);\n    }\n    if (merged.type) {\n        payload.type = resolveExpressionValue(merged.type);\n    }\n    const numericMappings = [\n        ['acquisitionPrice', 'acquisition_price'],\n        ['salePrice', 'sale_price'],\n        ['askingPrice', 'asking_price'],\n        ['targetSalePrice', 'target_sale_price'],\n    ];\n    for (const [sourceKey, targetKey] of numericMappings) {\n        const value = merged[sourceKey];\n        if (value !== undefined) {\n            payload[targetKey] = resolveExpressionValue(value);\n        }\n    }\n    if (merged.previewImageId !== undefined) {\n        const previewImageId = resolveExpressionValue(merged.previewImageId);\n        payload.preview_image_id = previewImageId || null;\n    }\n    const daten = {};\n    if (merged.adresse) {\n        daten.adresse = resolveExpressionValue(merged.adresse);\n    }\n    if (merged.kaufpreis !== undefined) {\n        daten.kaufpreis = resolveExpressionValue(merged.kaufpreis);\n    }\n    if (merged.flaeche !== undefined) {\n        daten.wohnflaeche = resolveExpressionValue(merged.flaeche);\n    }\n    if (merged.baujahr !== undefined) {\n        daten.baujahr = resolveExpressionValue(merged.baujahr);\n    }\n    if (merged.zustand) {\n        daten.zustand = resolveExpressionValue(merged.zustand);\n    }\n    if (merged.datenJson) {\n        const raw = resolveExpressionValue(merged.datenJson);\n        const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;\n        if (parsed && typeof parsed === 'object') {\n            Object.assign(daten, parsed);\n        }\n    }\n    if (merged.resetDaten === true) {\n        payload.daten = {};\n    }\n    else if (Object.keys(daten).length > 0) {\n        payload.daten = daten;\n    }\n    if (Object.keys(payload).length === 0) {\n        throw new Error('At least one update field is required');\n    }\n    return payload;\n})($parameter) }}",
              "json": true,
              "method": "PATCH",
              "headers": {
                "Content-Type": "application/json"
              },
              "sendBody": true
            }
          },
          "description": "Patch an existing immobilie"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete immobilie",
          "routing": {
            "request": {
              "url": "=/api/v2/immobilien/{{$parameter.immobilieId}}",
              "method": "DELETE"
            }
          },
          "description": "Delete an immobilie by ID"
        },
        {
          "name": "Update Status",
          "value": "updateStatus",
          "action": "Update immobilie status",
          "routing": {
            "request": {
              "url": "=/api/statuses/immobilien/{{$parameter.immobilieId}}/status",
              "body": "={{ ({ status_id: $parameter.statusId }) }}",
              "method": "PUT"
            }
          },
          "description": "Update the status of an immobilie"
        },
        {
          "name": "Set Tags",
          "value": "setTags",
          "action": "Set immobilie tags",
          "routing": {
            "request": {
              "url": "=/api/immobilie/{{$parameter.immobilieId}}/tags",
              "body": "={{ $parameter.tagIds ?? [] }}",
              "method": "PUT"
            }
          },
          "description": "Replace the tags assigned to an immobilie"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "immobilie"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get contacts",
          "routing": {
            "request": {
              "qs": {
                "q": "={{$parameter.search || undefined}}",
                "page": "={{$parameter.page || 1}}",
                "sort": "={{$parameter.sort || undefined}}",
                "order": "={{$parameter.order || undefined}}",
                "per_page": "={{$parameter.perPage || 50}}",
                "organisation_id": "={{$credentials.organisationId || undefined}}"
              },
              "url": "/api/contacts",
              "method": "GET"
            }
          },
          "description": "List contacts with optional pagination and search"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get contact",
          "routing": {
            "request": {
              "url": "=/api/contacts/{{$parameter.contactId}}",
              "method": "GET"
            }
          },
          "description": "Retrieve a contact by ID"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create contact",
          "routing": {
            "request": {
              "qs": {
                "role": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.role')) ?? undefined;\n\t})() }}",
                "email": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.email')) ?? undefined;\n\t})() }}",
                "phone": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.phone')) ?? undefined;\n\t})() }}",
                "mobile": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.mobile')) ?? undefined;\n\t})() }}",
                "address": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.address')) ?? undefined;\n\t})() }}",
                "company": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.company')) ?? undefined;\n\t})() }}",
                "last_name": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'lastName')) ?? undefined;\n\t})() }}",
                "first_name": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'firstName')) ?? undefined;\n\t})() }}",
                "organisation_id": "={{$credentials.organisationId || $credentials.organizationId || undefined}}"
              },
              "url": "/api/contacts",
              "body": "={{ ((parameter, credentials) => {\n    var _a, _b;\n    const resolveJsonPath = (path) => {\n        if (typeof $json !== 'object' || $json === null) {\n            return undefined;\n        }\n        const segments = path.split('.').filter((segment) => segment !== '');\n        let current = $json;\n        for (const segment of segments) {\n            if (typeof current !== 'object' || current === null || !(segment in current)) {\n                return undefined;\n            }\n            current = current[segment];\n        }\n        return current;\n    };\n    const resolveExpressionValue = (value) => {\n        if (typeof value !== 'string') {\n            return value;\n        }\n        const trimmed = value.trim();\n        if (trimmed === '') {\n            return value;\n        }\n        const hasExpression = trimmed.startsWith('=');\n        if (!hasExpression) {\n            return value;\n        }\n        const expressionCandidate = trimmed.slice(1).trim();\n        const moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n        const normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n        const jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n        if (jsonPathMatch) {\n            const resolved = resolveJsonPath(jsonPathMatch[1]);\n            if (resolved !== undefined) {\n                return resolved;\n            }\n        }\n        if (typeof $evaluateExpression === 'function') {\n            const evaluated = $evaluateExpression(normalizedExpression);\n            if (typeof evaluated === 'string' && evaluated.trim() === trimmed) {\n                return undefined;\n            }\n            return evaluated;\n        }\n        return undefined;\n    };\n    const resolveStringValue = (value) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null) {\n            return undefined;\n        }\n        const trimmed = String(resolved).trim();\n        return trimmed === '' ? undefined : trimmed;\n    };\n    const sanitizeEmail = (value) => {\n        const resolved = resolveStringValue(value);\n        if (!resolved) {\n            if (typeof value === 'string') {\n                const trimmed = value.trim();\n                if (trimmed.startsWith('=')) {\n                    const fallback = trimmed.slice(1).trim();\n                    if (fallback !== '' && fallback.includes('@')) {\n                        return fallback;\n                    }\n                }\n            }\n            return undefined;\n        }\n        const cleaned = resolved.replace(/^=/, '');\n        return cleaned === '' ? undefined : cleaned;\n    };\n    const parseObject = (value, fieldName) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null || resolved === '') {\n            return undefined;\n        }\n        if (typeof resolved === 'string') {\n            const trimmed = resolved.trim();\n            if (trimmed === '') {\n                return undefined;\n            }\n            try {\n                return JSON.parse(trimmed);\n            }\n            catch {\n                throw new Error(`${fieldName} must be valid JSON`);\n            }\n        }\n        if (typeof resolved === 'object') {\n            return resolved;\n        }\n        throw new Error(`${fieldName} must be an object or JSON string`);\n    };\n    const pickValue = (primary, fallback) => primary === undefined || primary === null || primary === '' ? fallback : primary;\n    const additional = (_a = parameter.additionalFields) !== null && _a !== void 0 ? _a : {};\n    const overrides = parseObject(parameter.additionalFieldsExpression, 'additionalFieldsExpression');\n    const merged = overrides ? { ...additional, ...overrides } : additional;\n    const body = {\n        first_name: resolveExpressionValue(parameter.firstName),\n        last_name: resolveExpressionValue(parameter.lastName),\n        organisation_id: (_b = credentials.organisationId) !== null && _b !== void 0 ? _b : credentials.organizationId,\n    };\n    const emailValue = pickValue(merged.email, parameter.email);\n    const email = sanitizeEmail(emailValue);\n    if (email !== undefined) {\n        body.email = email;\n    }\n    const phone = resolveStringValue(merged.phone);\n    if (phone !== undefined) {\n        body.phone = phone;\n    }\n    const mobile = resolveStringValue(merged.mobile);\n    if (mobile !== undefined) {\n        body.mobile = mobile;\n    }\n    const address = resolveStringValue(merged.address);\n    if (address !== undefined) {\n        body.address = address;\n    }\n    const role = resolveStringValue(merged.role);\n    if (role !== undefined) {\n        body.role = role;\n    }\n    const company = resolveStringValue(merged.company);\n    if (company !== undefined) {\n        body.company = company;\n    }\n    return body;\n})($parameter, $credentials) }}",
              "json": true,
              "method": "POST"
            }
          },
          "description": "Create a new contact for the current organisation"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update contact",
          "routing": {
            "request": {
              "qs": {
                "role": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.role')) ?? undefined;\n\t})() }}",
                "email": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.email')) ?? undefined;\n\t})() }}",
                "phone": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.phone')) ?? undefined;\n\t})() }}",
                "mobile": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.mobile')) ?? undefined;\n\t})() }}",
                "address": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.address')) ?? undefined;\n\t})() }}",
                "company": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.company')) ?? undefined;\n\t})() }}",
                "last_name": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.lastName')) ?? undefined;\n\t})() }}",
                "first_name": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.firstName')) ?? undefined;\n\t})() }}"
              },
              "url": "=/api/contacts/{{$parameter.contactId}}",
              "body": "={{ ((parameter) => {\n    var _a;\n    const resolveJsonPath = (path) => {\n        if (typeof $json !== 'object' || $json === null) {\n            return undefined;\n        }\n        const segments = path.split('.').filter((segment) => segment !== '');\n        let current = $json;\n        for (const segment of segments) {\n            if (typeof current !== 'object' || current === null || !(segment in current)) {\n                return undefined;\n            }\n            current = current[segment];\n        }\n        return current;\n    };\n    const resolveExpressionValue = (value) => {\n        if (typeof value !== 'string') {\n            return value;\n        }\n        const trimmed = value.trim();\n        if (trimmed === '') {\n            return value;\n        }\n        const hasExpression = trimmed.startsWith('=');\n        if (!hasExpression) {\n            return value;\n        }\n        const expressionCandidate = trimmed.slice(1).trim();\n        const moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n        const normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n        const jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n        if (jsonPathMatch) {\n            const resolved = resolveJsonPath(jsonPathMatch[1]);\n            if (resolved !== undefined) {\n                return resolved;\n            }\n        }\n        if (typeof $evaluateExpression === 'function') {\n            const evaluated = $evaluateExpression(normalizedExpression);\n            if (typeof evaluated === 'string' && evaluated.trim() === trimmed) {\n                return undefined;\n            }\n            return evaluated;\n        }\n        return undefined;\n    };\n    const resolveStringValue = (value) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null) {\n            return undefined;\n        }\n        const trimmed = String(resolved).trim();\n        return trimmed === '' ? undefined : trimmed;\n    };\n    const sanitizeEmail = (value) => {\n        const resolved = resolveStringValue(value);\n        if (!resolved) {\n            if (typeof value === 'string') {\n                const trimmed = value.trim();\n                if (trimmed.startsWith('=')) {\n                    const fallback = trimmed.slice(1).trim();\n                    if (fallback !== '' && fallback.includes('@')) {\n                        return fallback;\n                    }\n                }\n            }\n            return undefined;\n        }\n        const cleaned = resolved.replace(/^=/, '');\n        return cleaned === '' ? undefined : cleaned;\n    };\n    const parseObject = (value, fieldName) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null || resolved === '') {\n            return undefined;\n        }\n        if (typeof resolved === 'string') {\n            const trimmed = resolved.trim();\n            if (trimmed === '') {\n                return undefined;\n            }\n            try {\n                return JSON.parse(trimmed);\n            }\n            catch {\n                throw new Error(`${fieldName} must be valid JSON`);\n            }\n        }\n        if (typeof resolved === 'object') {\n            return resolved;\n        }\n        throw new Error(`${fieldName} must be an object or JSON string`);\n    };\n    const pickValue = (primary, fallback) => primary === undefined || primary === null || primary === '' ? fallback : primary;\n    const fields = (_a = parameter.updateFields) !== null && _a !== void 0 ? _a : {};\n    const overrides = parseObject(parameter.updateFieldsExpression, 'updateFieldsExpression');\n    const merged = overrides ? { ...fields, ...overrides } : fields;\n    const payload = {};\n    const firstName = resolveStringValue(merged.firstName);\n    if (firstName !== undefined) {\n        payload.first_name = firstName;\n    }\n    const lastName = resolveStringValue(merged.lastName);\n    if (lastName !== undefined) {\n        payload.last_name = lastName;\n    }\n    const emailValue = pickValue(merged.email, parameter.email);\n    const email = sanitizeEmail(emailValue);\n    if (email !== undefined) {\n        payload.email = email;\n    }\n    const phone = resolveStringValue(merged.phone);\n    if (phone !== undefined) {\n        payload.phone = phone;\n    }\n    const mobile = resolveStringValue(merged.mobile);\n    if (mobile !== undefined) {\n        payload.mobile = mobile;\n    }\n    const address = resolveStringValue(merged.address);\n    if (address !== undefined) {\n        payload.address = address;\n    }\n    const role = resolveStringValue(merged.role);\n    if (role !== undefined) {\n        payload.role = role;\n    }\n    const company = resolveStringValue(merged.company);\n    if (company !== undefined) {\n        payload.company = company;\n    }\n    return payload;\n})($parameter) }}",
              "json": true,
              "method": "PUT"
            }
          },
          "description": "Update an existing contact"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete contact",
          "routing": {
            "request": {
              "url": "=/api/contacts/{{$parameter.contactId}}",
              "method": "DELETE"
            }
          },
          "description": "Remove a contact by ID"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "contact"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get activities",
          "routing": {
            "request": {
              "qs": {
                "q": "={{$parameter.additionalOptions?.search || $parameter.search || undefined}}",
                "page": "={{$parameter.page || 1}}",
                "type": "={{$parameter.additionalOptions?.typeFilter || $parameter.typeFilter || undefined}}",
                "status": "={{$parameter.additionalOptions?.statusFilter || $parameter.statusFilter || undefined}}",
                "per_page": "={{$parameter.perPage || 25}}",
                "priority": "={{$parameter.additionalOptions?.priorityFilter || $parameter.priorityFilter || undefined}}",
                "immobilie": "={{$parameter.additionalOptions?.immobilienId || $parameter.immobilienId || undefined}}",
                "organisation_id": "={{$credentials.organisationId || $credentials.organizationId || $parameter.organisationId || undefined}}"
              },
              "url": "/api/activities/activities",
              "method": "GET"
            }
          },
          "description": "List activities with optional filters"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get activity",
          "routing": {
            "request": {
              "qs": {
                "title": "={{$parameter.activityTitle}}"
              },
              "url": "=/api/activities/activities/immobilie/{{$parameter.immobilienIdGet}}/by-title",
              "method": "GET"
            }
          },
          "description": "Retrieve a single activity by property and title"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create activity",
          "routing": {
            "request": {
              "qs": {
                "organisation_id": "={{$credentials.organisationId || $credentials.organizationId || undefined}}"
              },
              "url": "/api/activities/activities",
              "body": {
                "type": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'type')) ?? undefined;\n\t})() }}",
                "title": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'title')) ?? undefined;\n\t})() }}",
                "status": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'status')) ?? undefined;\n\t})() }}",
                "priority": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'priority')) ?? undefined;\n\t})() }}",
                "actual_end": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.actualEnd')) ?? undefined;\n\t})() }}",
                "contact_ids": "={{ (() => {\n\tconst raw = (() => {\n\t\tconst value = $parameter.additionalFields?.contactIds;\n\t\tif (value === undefined || value === null || value === '') return undefined;\n\t\tif (typeof value !== 'string') return value;\n\t\tconst trimmed = value.trim();\n\t\tif (!trimmed.startsWith('=')) return trimmed;\n\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\tif (jsonPathMatch) {\n\t\t\tconst getByPath = (obj, path) => {\n\t\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\t\tlet current = obj;\n\t\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\t\tif (!segment) continue;\n\t\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\t\tcurrent = current[segment];\n\t\t\t\t}\n\t\t\t\treturn current;\n\t\t\t};\n\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t}\n\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\treturn evaluated;\n\t\t}\n\t\treturn undefined;\n\t})();\n\tif (raw === undefined || raw === null || raw === '') return undefined;\n\tif (Array.isArray(raw)) return raw;\n\tif (typeof raw === 'string') {\n\t\tconst trimmed = raw.trim();\n\t\tif (trimmed === '') return undefined;\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(trimmed);\n\t\t\treturn Array.isArray(parsed) ? parsed : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn undefined;\n})() }}",
                "description": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.description')) ?? undefined;\n\t})() }}",
                "actual_start": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.actualStart')) ?? undefined;\n\t})() }}",
                "immobilien_id": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\tconst primary = resolveValue(getByPath($parameter, 'additionalFields.immobilienId'));\n\t\tif (primary !== undefined && primary !== null && primary !== '') return primary;\n\t\tconst fallbackPaths = [\"immobilienId\"];\n\t\tfor (const fallbackPath of fallbackPaths) {\n\t\t\tconst fallback = resolveValue(getByPath($parameter, fallbackPath));\n\t\t\tif (fallback !== undefined && fallback !== null && fallback !== '') return fallback;\n\t\t}\n\t\treturn undefined;\n\t})() }}",
                "scheduled_end": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.scheduledEnd')) ?? undefined;\n\t})() }}",
                "assigned_to_id": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.assignedToId')) ?? undefined;\n\t})() }}",
                "organisation_id": "={{$credentials.organisationId || $credentials.organizationId || undefined}}",
                "scheduled_start": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.scheduledStart')) ?? undefined;\n\t})() }}"
              },
              "json": true,
              "method": "POST"
            }
          },
          "description": "Create a new activity, optionally linked to a property"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update activity",
          "routing": {
            "request": {
              "qs": {
                "type": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.type')) ?? undefined;\n\t})() }}",
                "title": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\tconst primary = resolveValue(getByPath($parameter, 'updateFields.title'));\n\t\tif (primary !== undefined && primary !== null && primary !== '') return primary;\n\t\tconst fallbackPaths = [\"additionalOptionsUpdate.activityTitle\",\"activityTitleUpdate\"];\n\t\tfor (const fallbackPath of fallbackPaths) {\n\t\t\tconst fallback = resolveValue(getByPath($parameter, fallbackPath));\n\t\t\tif (fallback !== undefined && fallback !== null && fallback !== '') return fallback;\n\t\t}\n\t\treturn undefined;\n\t})() }}",
                "status": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.status')) ?? undefined;\n\t})() }}",
                "priority": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.priority')) ?? undefined;\n\t})() }}",
                "actual_end": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.actualEnd')) ?? undefined;\n\t})() }}",
                "description": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.description')) ?? undefined;\n\t})() }}",
                "actual_start": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.actualStart')) ?? undefined;\n\t})() }}",
                "immobilien_id": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.immobilienId')) ?? undefined;\n\t})() }}",
                "scheduled_end": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.scheduledEnd')) ?? undefined;\n\t})() }}",
                "assigned_to_id": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.assignedToId')) ?? undefined;\n\t})() }}",
                "scheduled_start": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.scheduledStart')) ?? undefined;\n\t})() }}"
              },
              "url": "=/api/activities/activities/{{$parameter.activityIdUpdate}}",
              "body": "={{ ((parameter) => {\n    var _a, _b;\n    const resolveExpressionValue = (value) => {\n        if (typeof value !== 'string') {\n            return value;\n        }\n        const trimmed = value.trim();\n        if (trimmed === '') {\n            return value;\n        }\n        const hasExpression = trimmed.startsWith('=');\n        if (!hasExpression || typeof $evaluateExpression !== 'function') {\n            return value;\n        }\n        const expression = trimmed.startsWith('=') ? trimmed.slice(1) : trimmed;\n        return $evaluateExpression(expression);\n    };\n    const resolveStringValue = (value) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null) {\n            return undefined;\n        }\n        const trimmed = String(resolved).trim();\n        return trimmed === '' ? undefined : trimmed;\n    };\n    const parseObject = (value, fieldName) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null || resolved === '') {\n            return undefined;\n        }\n        if (typeof resolved === 'string') {\n            const trimmed = resolved.trim();\n            if (trimmed === '') {\n                return undefined;\n            }\n            try {\n                return JSON.parse(trimmed);\n            }\n            catch {\n                throw new Error(`${fieldName} must be valid JSON`);\n            }\n        }\n        if (typeof resolved === 'object') {\n            return resolved;\n        }\n        throw new Error(`${fieldName} must be an object or JSON string`);\n    };\n    const pickValue = (primary, fallback) => primary === undefined || primary === null || primary === '' ? fallback : primary;\n    const payload = {};\n    const fields = (_a = parameter.updateFields) !== null && _a !== void 0 ? _a : {};\n    const additionalOptions = (_b = parameter.additionalOptionsUpdate) !== null && _b !== void 0 ? _b : {};\n    const overrides = parseObject(parameter.updateFieldsExpression, 'updateFieldsExpression');\n    const merged = overrides ? { ...fields, ...overrides } : fields;\n    const titleSource = pickValue(merged.title, pickValue(additionalOptions.activityTitle, parameter.activityTitleUpdate));\n    if (titleSource !== undefined && titleSource !== null && titleSource !== '') {\n        payload.title = resolveExpressionValue(titleSource);\n    }\n    if (merged.type !== undefined) {\n        payload.type = resolveExpressionValue(merged.type);\n    }\n    if (merged.status !== undefined) {\n        payload.status = resolveExpressionValue(merged.status);\n    }\n    if (merged.priority !== undefined) {\n        payload.priority = resolveExpressionValue(merged.priority);\n    }\n    const descriptionSource = pickValue(merged.description, parameter.descriptionExpression);\n    const description = resolveStringValue(descriptionSource);\n    if (description !== undefined) {\n        payload.description = description;\n    }\n    if (merged.scheduledStart !== undefined) {\n        payload.scheduled_start = resolveExpressionValue(merged.scheduledStart);\n    }\n    if (merged.scheduledEnd !== undefined) {\n        payload.scheduled_end = resolveExpressionValue(merged.scheduledEnd);\n    }\n    if (merged.actualStart !== undefined) {\n        payload.actual_start = resolveExpressionValue(merged.actualStart);\n    }\n    if (merged.actualEnd !== undefined) {\n        payload.actual_end = resolveExpressionValue(merged.actualEnd);\n    }\n    if (merged.assignedToId !== undefined) {\n        const assignedToId = resolveStringValue(merged.assignedToId);\n        if (assignedToId !== undefined) {\n            payload.assigned_to_id = assignedToId;\n        }\n    }\n    if (merged.immobilienId !== undefined) {\n        const immobilienId = resolveExpressionValue(merged.immobilienId);\n        payload.immobilien_id = immobilienId || null;\n    }\n    const rawContactIds = resolveExpressionValue(merged.contactIds);\n    if (rawContactIds !== undefined && rawContactIds !== '' && rawContactIds !== null) {\n        let parsedContactIds = rawContactIds;\n        if (typeof rawContactIds === 'string') {\n            try {\n                parsedContactIds = JSON.parse(rawContactIds);\n            }\n            catch {\n                throw new Error('contactIds must be valid JSON (e.g. [\"uuid1\",\"uuid2\"])');\n            }\n        }\n        if (Array.isArray(parsedContactIds)) {\n            payload.contact_ids = parsedContactIds;\n        }\n        else {\n            throw new Error('contactIds must be an array of UUID strings');\n        }\n    }\n    return payload;\n})($parameter) }}",
              "json": true,
              "method": "PUT"
            }
          },
          "description": "Update an existing activity by ID"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete activity",
          "routing": {
            "request": {
              "url": "=/api/activities/activities/{{$parameter.activityIdDelete}}",
              "method": "DELETE"
            }
          },
          "description": "Delete an activity by ID"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "activity"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get immobilien",
      "routing": {
        "request": {
          "qs": {
            "page": "={{$parameter.page || 1}}",
            "per_page": "={{$parameter.perPage || 20}}",
            "organisation_id": "={{$credentials.organisationId || $credentials.organizationId || $parameter.organisationId || undefined}}"
          },
          "url": "/api/v2/immobilien",
          "method": "GET"
        }
      },
      "description": "Get immobilien for the current organisation",
      "resource": "immobilie"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get immobilie",
      "routing": {
        "request": {
          "url": "=/api/v2/immobilien/{{$parameter.immobilieId}}",
          "method": "GET"
        }
      },
      "description": "Get a single immobilie by ID",
      "resource": "immobilie"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create immobilie",
      "routing": {
        "request": {
          "qs": {
            "name": "={{$parameter.name || undefined}}",
            "type": "={{$parameter.type || undefined}}",
            "organisation_id": "={{$credentials.organisationId || undefined}}"
          },
          "url": "/api/v2/immobilien",
          "body": "={{ ((parameter, credentials) => {\n    var _a;\n    const resolveExpressionValue = (value) => {\n        if (typeof value !== 'string') {\n            return value;\n        }\n        const trimmed = value.trim();\n        if (trimmed === '') {\n            return value;\n        }\n        const hasExpression = trimmed.startsWith('=');\n        if (!hasExpression || typeof $evaluateExpression !== 'function') {\n            return value;\n        }\n        const expression = trimmed.startsWith('=') ? trimmed.slice(1) : trimmed;\n        return $evaluateExpression(expression);\n    };\n    const parseObject = (value, fieldName) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null || resolved === '') {\n            return undefined;\n        }\n        if (typeof resolved === 'string') {\n            const trimmed = resolved.trim();\n            if (trimmed === '') {\n                return undefined;\n            }\n            try {\n                return JSON.parse(trimmed);\n            }\n            catch {\n                throw new Error(`${fieldName} must be valid JSON`);\n            }\n        }\n        if (typeof resolved === 'object') {\n            return resolved;\n        }\n        throw new Error(`${fieldName} must be an object or JSON string`);\n    };\n    const body = {\n        type: resolveExpressionValue(parameter.type),\n        name: resolveExpressionValue(parameter.name),\n        organisation_id: credentials.organisationId,\n    };\n    const additional = (_a = parameter.additionalFields) !== null && _a !== void 0 ? _a : {};\n    const overrides = parseObject(parameter.additionalFieldsExpression, 'additionalFieldsExpression');\n    const merged = overrides ? { ...additional, ...overrides } : additional;\n    const daten = {};\n    if (merged.adresse) {\n        daten.adresse = resolveExpressionValue(merged.adresse);\n    }\n    if (merged.kaufpreis !== undefined) {\n        daten.kaufpreis = resolveExpressionValue(merged.kaufpreis);\n    }\n    if (merged.flaeche !== undefined) {\n        daten.wohnflaeche = resolveExpressionValue(merged.flaeche);\n    }\n    if (merged.baujahr !== undefined) {\n        daten.baujahr = resolveExpressionValue(merged.baujahr);\n    }\n    if (merged.zustand) {\n        daten.zustand = resolveExpressionValue(merged.zustand);\n    }\n    if (merged.datenJson) {\n        const raw = resolveExpressionValue(merged.datenJson);\n        const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;\n        if (parsed && typeof parsed === 'object') {\n            Object.assign(daten, parsed);\n        }\n    }\n    if (Object.keys(daten).length > 0) {\n        body.daten = daten;\n    }\n    return body;\n})($parameter, $credentials) }}",
          "json": true,
          "method": "POST"
        }
      },
      "description": "Create a new immobilie in the current organisation",
      "resource": "immobilie"
    },
    {
      "name": "Add Resource Link",
      "value": "addResourceLink",
      "action": "Add resource link",
      "routing": {
        "request": {
          "url": "=/api/resource-links/immobilie/{{$parameter.immobilieId}}",
          "body": "={{ (() => {\n\tconst body = {\n\t\ttitle: $parameter.linkTitle,\n\t\turl: $parameter.linkUrl,\n\t};\n\tconst optional = $parameter.linkOptionalFields ?? {};\n\tif (optional.notes) {\n\t\tbody.notes = optional.notes;\n\t}\n\tif (optional.icon) {\n\t\tbody.icon = optional.icon;\n\t}\n\tif (optional.color) {\n\t\tbody.color = optional.color;\n\t}\n\tif (optional.orderIndex !== undefined && optional.orderIndex !== null) {\n\t\tbody.order_index = optional.orderIndex;\n\t}\n\treturn body;\n})() }}",
          "method": "POST"
        }
      },
      "description": "Attach an external link to an immobilie",
      "resource": "immobilie"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update immobilie",
      "routing": {
        "request": {
          "qs": {
            "name": "={{$parameter.updateFields?.name || undefined}}",
            "type": "={{$parameter.updateFields?.type || undefined}}",
            "adresse": "={{$parameter.updateFields?.adresse || undefined}}",
            "baujahr": "={{$parameter.updateFields?.baujahr || undefined}}",
            "zustand": "={{$parameter.updateFields?.zustand || undefined}}",
            "kaufpreis": "={{$parameter.updateFields?.kaufpreis || undefined}}",
            "sale_price": "={{$parameter.updateFields?.salePrice || undefined}}",
            "wohnflaeche": "={{$parameter.updateFields?.flaeche || undefined}}",
            "asking_price": "={{$parameter.updateFields?.askingPrice || undefined}}",
            "preview_image_id": "={{$parameter.updateFields?.previewImageId || undefined}}",
            "acquisition_price": "={{$parameter.updateFields?.acquisitionPrice || undefined}}",
            "target_sale_price": "={{$parameter.updateFields?.targetSalePrice || undefined}}"
          },
          "url": "=/api/v2/immobilien/{{$parameter.immobilieId}}",
          "body": "={{ ((parameter) => {\n    var _a;\n    const resolveJsonPath = (path) => {\n        if (typeof $json !== 'object' || $json === null) {\n            return undefined;\n        }\n        const segments = path.split('.').filter((segment) => segment !== '');\n        let current = $json;\n        for (const segment of segments) {\n            if (typeof current !== 'object' || current === null || !(segment in current)) {\n                return undefined;\n            }\n            current = current[segment];\n        }\n        return current;\n    };\n    const resolveExpressionValue = (value) => {\n        if (typeof value !== 'string') {\n            return value;\n        }\n        const trimmed = value.trim();\n        if (trimmed === '') {\n            return value;\n        }\n        const expressionCandidate = trimmed.startsWith('=') ? trimmed.slice(1).trim() : trimmed;\n        const moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n        const normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n        const isExpression = trimmed.startsWith('=') || moustacheMatch !== null;\n        if (!isExpression) {\n            return value;\n        }\n        const jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n        if (jsonPathMatch) {\n            const resolved = resolveJsonPath(jsonPathMatch[1]);\n            if (resolved !== undefined) {\n                return resolved;\n            }\n        }\n        if (typeof $evaluateExpression !== 'function') {\n            return undefined;\n        }\n        const evaluated = $evaluateExpression(normalizedExpression);\n        if (typeof evaluated === 'string' && evaluated.trim() === trimmed) {\n            return undefined;\n        }\n        return evaluated;\n    };\n    const parseObject = (value, fieldName) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null || resolved === '') {\n            return undefined;\n        }\n        if (typeof resolved === 'string') {\n            const trimmed = resolved.trim();\n            if (trimmed === '') {\n                return undefined;\n            }\n            try {\n                return JSON.parse(trimmed);\n            }\n            catch {\n                throw new Error(`${fieldName} must be valid JSON`);\n            }\n        }\n        if (typeof resolved === 'object') {\n            return resolved;\n        }\n        throw new Error(`${fieldName} must be an object or JSON string`);\n    };\n    const payload = {};\n    const fields = (_a = parameter.updateFields) !== null && _a !== void 0 ? _a : {};\n    const overrides = parseObject(parameter.updateFieldsExpression, 'updateFieldsExpression');\n    const merged = overrides ? { ...fields, ...overrides } : fields;\n    if (merged.name) {\n        payload.name = resolveExpressionValue(merged.name);\n    }\n    if (merged.type) {\n        payload.type = resolveExpressionValue(merged.type);\n    }\n    const numericMappings = [\n        ['acquisitionPrice', 'acquisition_price'],\n        ['salePrice', 'sale_price'],\n        ['askingPrice', 'asking_price'],\n        ['targetSalePrice', 'target_sale_price'],\n    ];\n    for (const [sourceKey, targetKey] of numericMappings) {\n        const value = merged[sourceKey];\n        if (value !== undefined) {\n            payload[targetKey] = resolveExpressionValue(value);\n        }\n    }\n    if (merged.previewImageId !== undefined) {\n        const previewImageId = resolveExpressionValue(merged.previewImageId);\n        payload.preview_image_id = previewImageId || null;\n    }\n    const daten = {};\n    if (merged.adresse) {\n        daten.adresse = resolveExpressionValue(merged.adresse);\n    }\n    if (merged.kaufpreis !== undefined) {\n        daten.kaufpreis = resolveExpressionValue(merged.kaufpreis);\n    }\n    if (merged.flaeche !== undefined) {\n        daten.wohnflaeche = resolveExpressionValue(merged.flaeche);\n    }\n    if (merged.baujahr !== undefined) {\n        daten.baujahr = resolveExpressionValue(merged.baujahr);\n    }\n    if (merged.zustand) {\n        daten.zustand = resolveExpressionValue(merged.zustand);\n    }\n    if (merged.datenJson) {\n        const raw = resolveExpressionValue(merged.datenJson);\n        const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;\n        if (parsed && typeof parsed === 'object') {\n            Object.assign(daten, parsed);\n        }\n    }\n    if (merged.resetDaten === true) {\n        payload.daten = {};\n    }\n    else if (Object.keys(daten).length > 0) {\n        payload.daten = daten;\n    }\n    if (Object.keys(payload).length === 0) {\n        throw new Error('At least one update field is required');\n    }\n    return payload;\n})($parameter) }}",
          "json": true,
          "method": "PATCH",
          "headers": {
            "Content-Type": "application/json"
          },
          "sendBody": true
        }
      },
      "description": "Patch an existing immobilie",
      "resource": "immobilie"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete immobilie",
      "routing": {
        "request": {
          "url": "=/api/v2/immobilien/{{$parameter.immobilieId}}",
          "method": "DELETE"
        }
      },
      "description": "Delete an immobilie by ID",
      "resource": "immobilie"
    },
    {
      "name": "Update Status",
      "value": "updateStatus",
      "action": "Update immobilie status",
      "routing": {
        "request": {
          "url": "=/api/statuses/immobilien/{{$parameter.immobilieId}}/status",
          "body": "={{ ({ status_id: $parameter.statusId }) }}",
          "method": "PUT"
        }
      },
      "description": "Update the status of an immobilie",
      "resource": "immobilie"
    },
    {
      "name": "Set Tags",
      "value": "setTags",
      "action": "Set immobilie tags",
      "routing": {
        "request": {
          "url": "=/api/immobilie/{{$parameter.immobilieId}}/tags",
          "body": "={{ $parameter.tagIds ?? [] }}",
          "method": "PUT"
        }
      },
      "description": "Replace the tags assigned to an immobilie",
      "resource": "immobilie"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get contacts",
      "routing": {
        "request": {
          "qs": {
            "q": "={{$parameter.search || undefined}}",
            "page": "={{$parameter.page || 1}}",
            "sort": "={{$parameter.sort || undefined}}",
            "order": "={{$parameter.order || undefined}}",
            "per_page": "={{$parameter.perPage || 50}}",
            "organisation_id": "={{$credentials.organisationId || undefined}}"
          },
          "url": "/api/contacts",
          "method": "GET"
        }
      },
      "description": "List contacts with optional pagination and search",
      "resource": "contact"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get contact",
      "routing": {
        "request": {
          "url": "=/api/contacts/{{$parameter.contactId}}",
          "method": "GET"
        }
      },
      "description": "Retrieve a contact by ID",
      "resource": "contact"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create contact",
      "routing": {
        "request": {
          "qs": {
            "role": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.role')) ?? undefined;\n\t})() }}",
            "email": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.email')) ?? undefined;\n\t})() }}",
            "phone": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.phone')) ?? undefined;\n\t})() }}",
            "mobile": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.mobile')) ?? undefined;\n\t})() }}",
            "address": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.address')) ?? undefined;\n\t})() }}",
            "company": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.company')) ?? undefined;\n\t})() }}",
            "last_name": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'lastName')) ?? undefined;\n\t})() }}",
            "first_name": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'firstName')) ?? undefined;\n\t})() }}",
            "organisation_id": "={{$credentials.organisationId || $credentials.organizationId || undefined}}"
          },
          "url": "/api/contacts",
          "body": "={{ ((parameter, credentials) => {\n    var _a, _b;\n    const resolveJsonPath = (path) => {\n        if (typeof $json !== 'object' || $json === null) {\n            return undefined;\n        }\n        const segments = path.split('.').filter((segment) => segment !== '');\n        let current = $json;\n        for (const segment of segments) {\n            if (typeof current !== 'object' || current === null || !(segment in current)) {\n                return undefined;\n            }\n            current = current[segment];\n        }\n        return current;\n    };\n    const resolveExpressionValue = (value) => {\n        if (typeof value !== 'string') {\n            return value;\n        }\n        const trimmed = value.trim();\n        if (trimmed === '') {\n            return value;\n        }\n        const hasExpression = trimmed.startsWith('=');\n        if (!hasExpression) {\n            return value;\n        }\n        const expressionCandidate = trimmed.slice(1).trim();\n        const moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n        const normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n        const jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n        if (jsonPathMatch) {\n            const resolved = resolveJsonPath(jsonPathMatch[1]);\n            if (resolved !== undefined) {\n                return resolved;\n            }\n        }\n        if (typeof $evaluateExpression === 'function') {\n            const evaluated = $evaluateExpression(normalizedExpression);\n            if (typeof evaluated === 'string' && evaluated.trim() === trimmed) {\n                return undefined;\n            }\n            return evaluated;\n        }\n        return undefined;\n    };\n    const resolveStringValue = (value) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null) {\n            return undefined;\n        }\n        const trimmed = String(resolved).trim();\n        return trimmed === '' ? undefined : trimmed;\n    };\n    const sanitizeEmail = (value) => {\n        const resolved = resolveStringValue(value);\n        if (!resolved) {\n            if (typeof value === 'string') {\n                const trimmed = value.trim();\n                if (trimmed.startsWith('=')) {\n                    const fallback = trimmed.slice(1).trim();\n                    if (fallback !== '' && fallback.includes('@')) {\n                        return fallback;\n                    }\n                }\n            }\n            return undefined;\n        }\n        const cleaned = resolved.replace(/^=/, '');\n        return cleaned === '' ? undefined : cleaned;\n    };\n    const parseObject = (value, fieldName) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null || resolved === '') {\n            return undefined;\n        }\n        if (typeof resolved === 'string') {\n            const trimmed = resolved.trim();\n            if (trimmed === '') {\n                return undefined;\n            }\n            try {\n                return JSON.parse(trimmed);\n            }\n            catch {\n                throw new Error(`${fieldName} must be valid JSON`);\n            }\n        }\n        if (typeof resolved === 'object') {\n            return resolved;\n        }\n        throw new Error(`${fieldName} must be an object or JSON string`);\n    };\n    const pickValue = (primary, fallback) => primary === undefined || primary === null || primary === '' ? fallback : primary;\n    const additional = (_a = parameter.additionalFields) !== null && _a !== void 0 ? _a : {};\n    const overrides = parseObject(parameter.additionalFieldsExpression, 'additionalFieldsExpression');\n    const merged = overrides ? { ...additional, ...overrides } : additional;\n    const body = {\n        first_name: resolveExpressionValue(parameter.firstName),\n        last_name: resolveExpressionValue(parameter.lastName),\n        organisation_id: (_b = credentials.organisationId) !== null && _b !== void 0 ? _b : credentials.organizationId,\n    };\n    const emailValue = pickValue(merged.email, parameter.email);\n    const email = sanitizeEmail(emailValue);\n    if (email !== undefined) {\n        body.email = email;\n    }\n    const phone = resolveStringValue(merged.phone);\n    if (phone !== undefined) {\n        body.phone = phone;\n    }\n    const mobile = resolveStringValue(merged.mobile);\n    if (mobile !== undefined) {\n        body.mobile = mobile;\n    }\n    const address = resolveStringValue(merged.address);\n    if (address !== undefined) {\n        body.address = address;\n    }\n    const role = resolveStringValue(merged.role);\n    if (role !== undefined) {\n        body.role = role;\n    }\n    const company = resolveStringValue(merged.company);\n    if (company !== undefined) {\n        body.company = company;\n    }\n    return body;\n})($parameter, $credentials) }}",
          "json": true,
          "method": "POST"
        }
      },
      "description": "Create a new contact for the current organisation",
      "resource": "contact"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update contact",
      "routing": {
        "request": {
          "qs": {
            "role": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.role')) ?? undefined;\n\t})() }}",
            "email": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.email')) ?? undefined;\n\t})() }}",
            "phone": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.phone')) ?? undefined;\n\t})() }}",
            "mobile": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.mobile')) ?? undefined;\n\t})() }}",
            "address": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.address')) ?? undefined;\n\t})() }}",
            "company": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.company')) ?? undefined;\n\t})() }}",
            "last_name": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.lastName')) ?? undefined;\n\t})() }}",
            "first_name": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.firstName')) ?? undefined;\n\t})() }}"
          },
          "url": "=/api/contacts/{{$parameter.contactId}}",
          "body": "={{ ((parameter) => {\n    var _a;\n    const resolveJsonPath = (path) => {\n        if (typeof $json !== 'object' || $json === null) {\n            return undefined;\n        }\n        const segments = path.split('.').filter((segment) => segment !== '');\n        let current = $json;\n        for (const segment of segments) {\n            if (typeof current !== 'object' || current === null || !(segment in current)) {\n                return undefined;\n            }\n            current = current[segment];\n        }\n        return current;\n    };\n    const resolveExpressionValue = (value) => {\n        if (typeof value !== 'string') {\n            return value;\n        }\n        const trimmed = value.trim();\n        if (trimmed === '') {\n            return value;\n        }\n        const hasExpression = trimmed.startsWith('=');\n        if (!hasExpression) {\n            return value;\n        }\n        const expressionCandidate = trimmed.slice(1).trim();\n        const moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n        const normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n        const jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n        if (jsonPathMatch) {\n            const resolved = resolveJsonPath(jsonPathMatch[1]);\n            if (resolved !== undefined) {\n                return resolved;\n            }\n        }\n        if (typeof $evaluateExpression === 'function') {\n            const evaluated = $evaluateExpression(normalizedExpression);\n            if (typeof evaluated === 'string' && evaluated.trim() === trimmed) {\n                return undefined;\n            }\n            return evaluated;\n        }\n        return undefined;\n    };\n    const resolveStringValue = (value) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null) {\n            return undefined;\n        }\n        const trimmed = String(resolved).trim();\n        return trimmed === '' ? undefined : trimmed;\n    };\n    const sanitizeEmail = (value) => {\n        const resolved = resolveStringValue(value);\n        if (!resolved) {\n            if (typeof value === 'string') {\n                const trimmed = value.trim();\n                if (trimmed.startsWith('=')) {\n                    const fallback = trimmed.slice(1).trim();\n                    if (fallback !== '' && fallback.includes('@')) {\n                        return fallback;\n                    }\n                }\n            }\n            return undefined;\n        }\n        const cleaned = resolved.replace(/^=/, '');\n        return cleaned === '' ? undefined : cleaned;\n    };\n    const parseObject = (value, fieldName) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null || resolved === '') {\n            return undefined;\n        }\n        if (typeof resolved === 'string') {\n            const trimmed = resolved.trim();\n            if (trimmed === '') {\n                return undefined;\n            }\n            try {\n                return JSON.parse(trimmed);\n            }\n            catch {\n                throw new Error(`${fieldName} must be valid JSON`);\n            }\n        }\n        if (typeof resolved === 'object') {\n            return resolved;\n        }\n        throw new Error(`${fieldName} must be an object or JSON string`);\n    };\n    const pickValue = (primary, fallback) => primary === undefined || primary === null || primary === '' ? fallback : primary;\n    const fields = (_a = parameter.updateFields) !== null && _a !== void 0 ? _a : {};\n    const overrides = parseObject(parameter.updateFieldsExpression, 'updateFieldsExpression');\n    const merged = overrides ? { ...fields, ...overrides } : fields;\n    const payload = {};\n    const firstName = resolveStringValue(merged.firstName);\n    if (firstName !== undefined) {\n        payload.first_name = firstName;\n    }\n    const lastName = resolveStringValue(merged.lastName);\n    if (lastName !== undefined) {\n        payload.last_name = lastName;\n    }\n    const emailValue = pickValue(merged.email, parameter.email);\n    const email = sanitizeEmail(emailValue);\n    if (email !== undefined) {\n        payload.email = email;\n    }\n    const phone = resolveStringValue(merged.phone);\n    if (phone !== undefined) {\n        payload.phone = phone;\n    }\n    const mobile = resolveStringValue(merged.mobile);\n    if (mobile !== undefined) {\n        payload.mobile = mobile;\n    }\n    const address = resolveStringValue(merged.address);\n    if (address !== undefined) {\n        payload.address = address;\n    }\n    const role = resolveStringValue(merged.role);\n    if (role !== undefined) {\n        payload.role = role;\n    }\n    const company = resolveStringValue(merged.company);\n    if (company !== undefined) {\n        payload.company = company;\n    }\n    return payload;\n})($parameter) }}",
          "json": true,
          "method": "PUT"
        }
      },
      "description": "Update an existing contact",
      "resource": "contact"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete contact",
      "routing": {
        "request": {
          "url": "=/api/contacts/{{$parameter.contactId}}",
          "method": "DELETE"
        }
      },
      "description": "Remove a contact by ID",
      "resource": "contact"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get activities",
      "routing": {
        "request": {
          "qs": {
            "q": "={{$parameter.additionalOptions?.search || $parameter.search || undefined}}",
            "page": "={{$parameter.page || 1}}",
            "type": "={{$parameter.additionalOptions?.typeFilter || $parameter.typeFilter || undefined}}",
            "status": "={{$parameter.additionalOptions?.statusFilter || $parameter.statusFilter || undefined}}",
            "per_page": "={{$parameter.perPage || 25}}",
            "priority": "={{$parameter.additionalOptions?.priorityFilter || $parameter.priorityFilter || undefined}}",
            "immobilie": "={{$parameter.additionalOptions?.immobilienId || $parameter.immobilienId || undefined}}",
            "organisation_id": "={{$credentials.organisationId || $credentials.organizationId || $parameter.organisationId || undefined}}"
          },
          "url": "/api/activities/activities",
          "method": "GET"
        }
      },
      "description": "List activities with optional filters",
      "resource": "activity"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get activity",
      "routing": {
        "request": {
          "qs": {
            "title": "={{$parameter.activityTitle}}"
          },
          "url": "=/api/activities/activities/immobilie/{{$parameter.immobilienIdGet}}/by-title",
          "method": "GET"
        }
      },
      "description": "Retrieve a single activity by property and title",
      "resource": "activity"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create activity",
      "routing": {
        "request": {
          "qs": {
            "organisation_id": "={{$credentials.organisationId || $credentials.organizationId || undefined}}"
          },
          "url": "/api/activities/activities",
          "body": {
            "type": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'type')) ?? undefined;\n\t})() }}",
            "title": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'title')) ?? undefined;\n\t})() }}",
            "status": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'status')) ?? undefined;\n\t})() }}",
            "priority": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'priority')) ?? undefined;\n\t})() }}",
            "actual_end": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.actualEnd')) ?? undefined;\n\t})() }}",
            "contact_ids": "={{ (() => {\n\tconst raw = (() => {\n\t\tconst value = $parameter.additionalFields?.contactIds;\n\t\tif (value === undefined || value === null || value === '') return undefined;\n\t\tif (typeof value !== 'string') return value;\n\t\tconst trimmed = value.trim();\n\t\tif (!trimmed.startsWith('=')) return trimmed;\n\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\tif (jsonPathMatch) {\n\t\t\tconst getByPath = (obj, path) => {\n\t\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\t\tlet current = obj;\n\t\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\t\tif (!segment) continue;\n\t\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\t\tcurrent = current[segment];\n\t\t\t\t}\n\t\t\t\treturn current;\n\t\t\t};\n\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t}\n\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\treturn evaluated;\n\t\t}\n\t\treturn undefined;\n\t})();\n\tif (raw === undefined || raw === null || raw === '') return undefined;\n\tif (Array.isArray(raw)) return raw;\n\tif (typeof raw === 'string') {\n\t\tconst trimmed = raw.trim();\n\t\tif (trimmed === '') return undefined;\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(trimmed);\n\t\t\treturn Array.isArray(parsed) ? parsed : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn undefined;\n})() }}",
            "description": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.description')) ?? undefined;\n\t})() }}",
            "actual_start": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.actualStart')) ?? undefined;\n\t})() }}",
            "immobilien_id": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\tconst primary = resolveValue(getByPath($parameter, 'additionalFields.immobilienId'));\n\t\tif (primary !== undefined && primary !== null && primary !== '') return primary;\n\t\tconst fallbackPaths = [\"immobilienId\"];\n\t\tfor (const fallbackPath of fallbackPaths) {\n\t\t\tconst fallback = resolveValue(getByPath($parameter, fallbackPath));\n\t\t\tif (fallback !== undefined && fallback !== null && fallback !== '') return fallback;\n\t\t}\n\t\treturn undefined;\n\t})() }}",
            "scheduled_end": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.scheduledEnd')) ?? undefined;\n\t})() }}",
            "assigned_to_id": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.assignedToId')) ?? undefined;\n\t})() }}",
            "organisation_id": "={{$credentials.organisationId || $credentials.organizationId || undefined}}",
            "scheduled_start": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'additionalFields.scheduledStart')) ?? undefined;\n\t})() }}"
          },
          "json": true,
          "method": "POST"
        }
      },
      "description": "Create a new activity, optionally linked to a property",
      "resource": "activity"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update activity",
      "routing": {
        "request": {
          "qs": {
            "type": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.type')) ?? undefined;\n\t})() }}",
            "title": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\tconst primary = resolveValue(getByPath($parameter, 'updateFields.title'));\n\t\tif (primary !== undefined && primary !== null && primary !== '') return primary;\n\t\tconst fallbackPaths = [\"additionalOptionsUpdate.activityTitle\",\"activityTitleUpdate\"];\n\t\tfor (const fallbackPath of fallbackPaths) {\n\t\t\tconst fallback = resolveValue(getByPath($parameter, fallbackPath));\n\t\t\tif (fallback !== undefined && fallback !== null && fallback !== '') return fallback;\n\t\t}\n\t\treturn undefined;\n\t})() }}",
            "status": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.status')) ?? undefined;\n\t})() }}",
            "priority": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.priority')) ?? undefined;\n\t})() }}",
            "actual_end": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.actualEnd')) ?? undefined;\n\t})() }}",
            "description": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.description')) ?? undefined;\n\t})() }}",
            "actual_start": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.actualStart')) ?? undefined;\n\t})() }}",
            "immobilien_id": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.immobilienId')) ?? undefined;\n\t})() }}",
            "scheduled_end": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.scheduledEnd')) ?? undefined;\n\t})() }}",
            "assigned_to_id": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.assignedToId')) ?? undefined;\n\t})() }}",
            "scheduled_start": "={{ (() => {\n\t\tconst getByPath = (obj, path) => {\n\t\t\tif (!obj || typeof obj !== 'object') return undefined;\n\t\t\tlet current = obj;\n\t\t\tfor (const segment of path.split('.')) {\n\t\t\t\tif (!segment) continue;\n\t\t\t\tif (!current || typeof current !== 'object' || !(segment in current)) return undefined;\n\t\t\t\tcurrent = current[segment];\n\t\t\t}\n\t\t\treturn current;\n\t\t};\n\t\tconst resolveValue = (raw) => {\n\t\t\tif (raw === undefined || raw === null) return undefined;\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\tconst trimmed = raw.trim();\n\t\t\tif (!trimmed.startsWith('=')) return trimmed === '' ? undefined : trimmed;\n\n\t\t\tconst expressionCandidate = trimmed.slice(1).trim();\n\t\t\tconst moustacheMatch = expressionCandidate.match(/^\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n\t\t\tconst normalizedExpression = moustacheMatch ? moustacheMatch[1] : expressionCandidate;\n\t\t\tconst jsonPathMatch = normalizedExpression.match(/^\\$json\\.([A-Za-z0-9_.]+)$/);\n\t\t\tif (jsonPathMatch) {\n\t\t\t\tconst resolvedFromJson = getByPath($json, jsonPathMatch[1]);\n\t\t\t\tif (resolvedFromJson !== undefined) return resolvedFromJson;\n\t\t\t}\n\t\t\tif (typeof $evaluateExpression === 'function') {\n\t\t\t\tconst evaluated = $evaluateExpression(normalizedExpression);\n\t\t\t\tif (typeof evaluated === 'string' && evaluated.trim() === trimmed) return undefined;\n\t\t\t\treturn evaluated;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn resolveValue(getByPath($parameter, 'updateFields.scheduledStart')) ?? undefined;\n\t})() }}"
          },
          "url": "=/api/activities/activities/{{$parameter.activityIdUpdate}}",
          "body": "={{ ((parameter) => {\n    var _a, _b;\n    const resolveExpressionValue = (value) => {\n        if (typeof value !== 'string') {\n            return value;\n        }\n        const trimmed = value.trim();\n        if (trimmed === '') {\n            return value;\n        }\n        const hasExpression = trimmed.startsWith('=');\n        if (!hasExpression || typeof $evaluateExpression !== 'function') {\n            return value;\n        }\n        const expression = trimmed.startsWith('=') ? trimmed.slice(1) : trimmed;\n        return $evaluateExpression(expression);\n    };\n    const resolveStringValue = (value) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null) {\n            return undefined;\n        }\n        const trimmed = String(resolved).trim();\n        return trimmed === '' ? undefined : trimmed;\n    };\n    const parseObject = (value, fieldName) => {\n        const resolved = resolveExpressionValue(value);\n        if (resolved === undefined || resolved === null || resolved === '') {\n            return undefined;\n        }\n        if (typeof resolved === 'string') {\n            const trimmed = resolved.trim();\n            if (trimmed === '') {\n                return undefined;\n            }\n            try {\n                return JSON.parse(trimmed);\n            }\n            catch {\n                throw new Error(`${fieldName} must be valid JSON`);\n            }\n        }\n        if (typeof resolved === 'object') {\n            return resolved;\n        }\n        throw new Error(`${fieldName} must be an object or JSON string`);\n    };\n    const pickValue = (primary, fallback) => primary === undefined || primary === null || primary === '' ? fallback : primary;\n    const payload = {};\n    const fields = (_a = parameter.updateFields) !== null && _a !== void 0 ? _a : {};\n    const additionalOptions = (_b = parameter.additionalOptionsUpdate) !== null && _b !== void 0 ? _b : {};\n    const overrides = parseObject(parameter.updateFieldsExpression, 'updateFieldsExpression');\n    const merged = overrides ? { ...fields, ...overrides } : fields;\n    const titleSource = pickValue(merged.title, pickValue(additionalOptions.activityTitle, parameter.activityTitleUpdate));\n    if (titleSource !== undefined && titleSource !== null && titleSource !== '') {\n        payload.title = resolveExpressionValue(titleSource);\n    }\n    if (merged.type !== undefined) {\n        payload.type = resolveExpressionValue(merged.type);\n    }\n    if (merged.status !== undefined) {\n        payload.status = resolveExpressionValue(merged.status);\n    }\n    if (merged.priority !== undefined) {\n        payload.priority = resolveExpressionValue(merged.priority);\n    }\n    const descriptionSource = pickValue(merged.description, parameter.descriptionExpression);\n    const description = resolveStringValue(descriptionSource);\n    if (description !== undefined) {\n        payload.description = description;\n    }\n    if (merged.scheduledStart !== undefined) {\n        payload.scheduled_start = resolveExpressionValue(merged.scheduledStart);\n    }\n    if (merged.scheduledEnd !== undefined) {\n        payload.scheduled_end = resolveExpressionValue(merged.scheduledEnd);\n    }\n    if (merged.actualStart !== undefined) {\n        payload.actual_start = resolveExpressionValue(merged.actualStart);\n    }\n    if (merged.actualEnd !== undefined) {\n        payload.actual_end = resolveExpressionValue(merged.actualEnd);\n    }\n    if (merged.assignedToId !== undefined) {\n        const assignedToId = resolveStringValue(merged.assignedToId);\n        if (assignedToId !== undefined) {\n            payload.assigned_to_id = assignedToId;\n        }\n    }\n    if (merged.immobilienId !== undefined) {\n        const immobilienId = resolveExpressionValue(merged.immobilienId);\n        payload.immobilien_id = immobilienId || null;\n    }\n    const rawContactIds = resolveExpressionValue(merged.contactIds);\n    if (rawContactIds !== undefined && rawContactIds !== '' && rawContactIds !== null) {\n        let parsedContactIds = rawContactIds;\n        if (typeof rawContactIds === 'string') {\n            try {\n                parsedContactIds = JSON.parse(rawContactIds);\n            }\n            catch {\n                throw new Error('contactIds must be valid JSON (e.g. [\"uuid1\",\"uuid2\"])');\n            }\n        }\n        if (Array.isArray(parsedContactIds)) {\n            payload.contact_ids = parsedContactIds;\n        }\n        else {\n            throw new Error('contactIds must be an array of UUID strings');\n        }\n    }\n    return payload;\n})($parameter) }}",
          "json": true,
          "method": "PUT"
        }
      },
      "description": "Update an existing activity by ID",
      "resource": "activity"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete activity",
      "routing": {
        "request": {
          "url": "=/api/activities/activities/{{$parameter.activityIdDelete}}",
          "method": "DELETE"
        }
      },
      "description": "Delete an activity by ID",
      "resource": "activity"
    }
  ]

npm_readme:
  # n8n-nodes-immojump

  ImmoJump n8n Community Nodes (Trigger + Actions)

  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.

  [Installation](#installation)
  [Operations](#operations)
  [Credentials](#credentials)
  [Compatibility](#compatibility)
  [Usage](#usage)
  [Resources](#resources)
  [Version history](#version-history)

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.

  ## Operations

  The ImmoJump n8n Community Nodes provide comprehensive automation capabilities:

  ### Actions (ImmoJump Node)
  * **User Operations**
    - Get Many Users
    - Get User by ID
    - Create User

  * **Company Operations**
    - Get Many Companies (with pagination)

  * **Immobilie Operations**
    - Get Many Immobilies (with pagination)
    - Get Immobilie by ID
    - Create Immobilie
    - Update Status
    - Assign Tag
    - Remove Tag

  ### Triggers (ImmoJump Trigger Node)
  * **Status Changed** - Triggers when an immobilie status changes
  * **Tag Assigned** - Triggers when a tag is assigned to an immobilie
  * **Tag Removed** - Triggers when a tag is removed from an immobilie
  * **New Immobilie Created** - Triggers when a new immobilie is created

  Each trigger supports filtering options to only trigger on specific conditions.

  ### Dynamic option loading (Tags & Status)

  Both the action and trigger nodes reuse the same tag and immobilie status catalogue that powers our Vite frontend. When you open a dropdown in n8n the node performs a live lookup against your running ImmoJump backend using the credentials you configure:

  - `GET /api/statuses/statuses` → returns the status list (matching the TypeScript shape in `immobilien-ka/src/types/ImmobilienStatus.ts` and the helper in `immobilien-ka/src/Services/Api/ImmobilienStatusApiService.ts`). The endpoint scopes results to the organisation from `X-Organisation-Id` (or the caller’s `current_organisation_id`).
  - `GET /api/integrations/tags` → returns the tag list (see `immobilien-ka/src/Services/Api/TagApiService.ts`).

  Because the node talks directly to the app, any change you make in the UI (adding a new status, renaming a tag, rearranging pipeline stages, etc.) is immediately reflected inside n8n. For local development start the stack with `make start-flask` (or `make tmux` for the full environment) so the endpoints are reachable from n8n. Custom implementations must respond with JSON arrays, for example:

  ```json
  [
    { "id": 42, "name": "Besichtigung geplant" }
  ]
  ```

  If the endpoint is unreachable the node falls back to a debug placeholder so you instantly see that the lookup failed.

  ## Credentials

  _If users need to authenticate with the app/service, provide details here. You should include prerequisites (such as signing up with the service), available authentication methods, and how to set them up._

  ## Compatibility

  _State the minimum n8n version, as well as which versions you test against. You can also include any known version incompatibility issues._

  ## Usage

  _This is an optional section. Use it to help users with any difficult or confusing aspects of the node._

  _By the time users are looking for community nodes, they probably already know n8n basics. But if you expect new users, you can link to the [Try it out](https://docs.n8n.io/try-it-out/) documentation to help them get started._

  ## Resources

  * [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  * [ImmoJump API documentation](https://docs.immojump.com/)

ai_documentation_summary:
  {"purpose":"This n8n community node enables automation by interacting with the ImmoJump API to manage users, companies, and real estate properties (immobilien), including triggers for status and tag changes.","capabilities":["Get Many Users","Get User by ID","Create User","Get Many Companies (with pagination)","Get Many Immobilies (with pagination)","Get Immobilie by ID","Create Immobilie","Update Immobilie Status","Assign Tag to Immobilie","Remove Tag from Immobilie"],"authentication":"None (Requires ImmoJump API access/credentials configured in n8n)","commonUseCases":["Automating real estate property management workflows","Monitoring changes in property status or tags","Synchronizing user and company data from ImmoJump","Creating and updating property records programmatically"],"limitations":["Requires direct access to the ImmoJump backend API endpoints","For local development, the ImmoJump stack must be running (e.g., via 'make start-flask')","Dynamic status/tag loading depends on the ImmoJump backend responding correctly; fallback to debug placeholder if unreachable"],"relatedNodes":["n8n-nodes-http.request","n8n-nodes-http.response"]}


