# nodes

node_type: @telnyx/n8n-nodes-telnyx-ai.telnyxAi
package_name: @telnyx/n8n-nodes-telnyx-ai
display_name: Telnyx AI
description: Interact with Telnyx AI API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: team-telnyx
author_github_url: https://github.com/team-telnyx/n8n-nodes-telnyx-ai.git
npm_package_name: @telnyx/n8n-nodes-telnyx-ai
npm_version: 1.5.5
community_fetched_at: 2026-03-25T21:19:40.585Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "chat",
      "options": [
        {
          "name": "Assistant",
          "value": "assistants"
        },
        {
          "name": "Assistant Event",
          "value": "assistantEvents"
        },
        {
          "name": "Chat",
          "value": "chat"
        },
        {
          "name": "Conversation",
          "value": "conversations"
        },
        {
          "name": "Voice",
          "value": "voice"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many chat models",
          "routing": {
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "data"
                  }
                },
                {
                  "type": "setKeyValue",
                  "enabled": "={{$parameter[\"simplify\"]}}",
                  "properties": {
                    "id": "={{$responseItem.id}}",
                    "created": "={{$responseItem.created}}",
                    "owned_by": "={{$responseItem.owned_by}}",
                    "languages": "={{$responseItem.languages}}",
                    "parameters": "={{$responseItem.parameters}}",
                    "context_length": "={{$responseItem.context_length}}"
                  }
                }
              ]
            },
            "request": {
              "url": "/v2/ai/models",
              "method": "GET"
            }
          },
          "description": "Returns metadata about many chat models"
        },
        {
          "name": "Create Chat Completion",
          "value": "createChatCompletion",
          "action": "Create chat completion",
          "routing": {
            "send": {
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "/v2/ai/chat/completions",
              "method": "POST"
            }
          },
          "description": "Chat a new chat completion with a language model"
        },
        {
          "name": "Summarize File",
          "value": "summarizeFile",
          "action": "Summarize file content",
          "routing": {
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "data"
                  }
                }
              ]
            },
            "request": {
              "url": "/v2/ai/summarize",
              "method": "POST"
            }
          },
          "description": "Generate a summary of a file's contents"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "chat"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many voices",
          "routing": {
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "voices"
                  }
                }
              ]
            },
            "request": {
              "url": "/v2/text-to-speech/voices",
              "method": "GET"
            }
          },
          "description": "Returns metadata about many voices"
        },
        {
          "name": "Text to Speech",
          "value": "textToSpeech",
          "action": "Convert text to speech",
          "routing": {
            "output": {
              "postReceive": [
                null
              ]
            },
            "request": {
              "url": "/v2/text-to-speech/speech",
              "method": "POST",
              "encoding": "arraybuffer",
              "returnFullResponse": true
            }
          },
          "description": "Converts text into speech and returns audio"
        },
        {
          "name": "Speech to Text",
          "value": "speechToText",
          "action": "Transcribe audio",
          "routing": {
            "send": {
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "/v2/ai/audio/transcriptions",
              "method": "POST",
              "headers": {
                "Content-Type": "multipart/form-data"
              }
            }
          },
          "description": "Transcribe an audio file"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "voice"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many assistants",
          "routing": {
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "data"
                  }
                }
              ]
            },
            "request": {
              "url": "/v2/ai/assistants",
              "method": "GET"
            }
          },
          "description": "Returns a list of many AI Assistants configured by the user"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get an assistant",
          "routing": {
            "request": {
              "url": "=/v2/ai/assistants/{{$parameter[\"assistant\"]}}",
              "method": "GET"
            }
          },
          "description": "Returns a specific AI Assistant by ID"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "assistants"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a scheduled event",
          "routing": {
            "send": {
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "=/v2/ai/assistants/{{$parameter[\"assistant\"]}}/scheduled_events",
              "method": "POST"
            }
          },
          "description": "Create a new scheduled event for an assistant"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many events",
          "routing": {
            "send": {
              "paginate": "page_number",
              "property": "page_size"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "data"
                  }
                }
              ]
            },
            "request": {
              "url": "=/v2/ai/assistants/{{$parameter[\"assistant\"]}}/scheduled_events",
              "method": "GET"
            }
          },
          "description": "Get scheduled events for an assistant with pagination and filtering"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get an event",
          "routing": {
            "request": {
              "url": "=/v2/ai/assistants/{{$parameter[\"assistant\"]}}/scheduled_events/{{$parameter[\"eventId\"]}}",
              "method": "GET"
            }
          },
          "description": "Retrieve a scheduled event by event ID"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete an event",
          "routing": {
            "output": {
              "postReceive": [
                {
                  "type": "set",
                  "properties": {
                    "value": "={{ { \"deleted\": true } }}"
                  }
                }
              ]
            },
            "request": {
              "url": "=/v2/ai/assistants/{{$parameter[\"assistant\"]}}/scheduled_events/{{$parameter[\"eventId\"]}}",
              "method": "DELETE",
              "returnFullResponse": true
            }
          },
          "description": "Delete a scheduled event by event ID"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "assistantEvents"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many conversations",
          "routing": {
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "data"
                  }
                }
              ]
            },
            "request": {
              "url": "/v2/ai/conversations",
              "method": "GET"
            }
          },
          "description": "Returns a list of conversations"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a conversation",
          "routing": {
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "data"
                  }
                }
              ]
            },
            "request": {
              "url": "=/v2/ai/conversations/{{$parameter[\"conversationId\"]}}",
              "method": "GET"
            }
          },
          "description": "Returns a specific conversation by ID"
        },
        {
          "name": "Get Messages",
          "value": "getMessages",
          "action": "Get conversation messages",
          "routing": {
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "data"
                  }
                }
              ]
            },
            "request": {
              "url": "=/v2/ai/conversations/{{$parameter[\"conversationId\"]}}/messages",
              "method": "GET"
            }
          },
          "description": "Retrieve messages for a specific conversation, including tool calls made by the assistant"
        },
        {
          "name": "Get Conversation Insights",
          "value": "getConversationInsights",
          "action": "Get insights for a conversation",
          "routing": {
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "data"
                  }
                }
              ]
            },
            "request": {
              "url": "=/v2/ai/conversations/{{$parameter[\"conversationId\"]}}/conversations-insights",
              "method": "GET"
            }
          },
          "description": "Retrieve insights for a specific conversation"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "conversations"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many chat models",
      "routing": {
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "data"
              }
            },
            {
              "type": "setKeyValue",
              "enabled": "={{$parameter[\"simplify\"]}}",
              "properties": {
                "id": "={{$responseItem.id}}",
                "created": "={{$responseItem.created}}",
                "owned_by": "={{$responseItem.owned_by}}",
                "languages": "={{$responseItem.languages}}",
                "parameters": "={{$responseItem.parameters}}",
                "context_length": "={{$responseItem.context_length}}"
              }
            }
          ]
        },
        "request": {
          "url": "/v2/ai/models",
          "method": "GET"
        }
      },
      "description": "Returns metadata about many chat models"
    },
    {
      "name": "Create Chat Completion",
      "value": "createChatCompletion",
      "action": "Create chat completion",
      "routing": {
        "send": {
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "/v2/ai/chat/completions",
          "method": "POST"
        }
      },
      "description": "Chat a new chat completion with a language model"
    },
    {
      "name": "Summarize File",
      "value": "summarizeFile",
      "action": "Summarize file content",
      "routing": {
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "data"
              }
            }
          ]
        },
        "request": {
          "url": "/v2/ai/summarize",
          "method": "POST"
        }
      },
      "description": "Generate a summary of a file's contents"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many voices",
      "routing": {
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "voices"
              }
            }
          ]
        },
        "request": {
          "url": "/v2/text-to-speech/voices",
          "method": "GET"
        }
      },
      "description": "Returns metadata about many voices"
    },
    {
      "name": "Text to Speech",
      "value": "textToSpeech",
      "action": "Convert text to speech",
      "routing": {
        "output": {
          "postReceive": [
            null
          ]
        },
        "request": {
          "url": "/v2/text-to-speech/speech",
          "method": "POST",
          "encoding": "arraybuffer",
          "returnFullResponse": true
        }
      },
      "description": "Converts text into speech and returns audio"
    },
    {
      "name": "Speech to Text",
      "value": "speechToText",
      "action": "Transcribe audio",
      "routing": {
        "send": {
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "/v2/ai/audio/transcriptions",
          "method": "POST",
          "headers": {
            "Content-Type": "multipart/form-data"
          }
        }
      },
      "description": "Transcribe an audio file"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many assistants",
      "routing": {
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "data"
              }
            }
          ]
        },
        "request": {
          "url": "/v2/ai/assistants",
          "method": "GET"
        }
      },
      "description": "Returns a list of many AI Assistants configured by the user"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get an assistant",
      "routing": {
        "request": {
          "url": "=/v2/ai/assistants/{{$parameter[\"assistant\"]}}",
          "method": "GET"
        }
      },
      "description": "Returns a specific AI Assistant by ID"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a scheduled event",
      "routing": {
        "send": {
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "=/v2/ai/assistants/{{$parameter[\"assistant\"]}}/scheduled_events",
          "method": "POST"
        }
      },
      "description": "Create a new scheduled event for an assistant"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many events",
      "routing": {
        "send": {
          "paginate": "page_number",
          "property": "page_size"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "data"
              }
            }
          ]
        },
        "request": {
          "url": "=/v2/ai/assistants/{{$parameter[\"assistant\"]}}/scheduled_events",
          "method": "GET"
        }
      },
      "description": "Get scheduled events for an assistant with pagination and filtering"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get an event",
      "routing": {
        "request": {
          "url": "=/v2/ai/assistants/{{$parameter[\"assistant\"]}}/scheduled_events/{{$parameter[\"eventId\"]}}",
          "method": "GET"
        }
      },
      "description": "Retrieve a scheduled event by event ID"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete an event",
      "routing": {
        "output": {
          "postReceive": [
            {
              "type": "set",
              "properties": {
                "value": "={{ { \"deleted\": true } }}"
              }
            }
          ]
        },
        "request": {
          "url": "=/v2/ai/assistants/{{$parameter[\"assistant\"]}}/scheduled_events/{{$parameter[\"eventId\"]}}",
          "method": "DELETE",
          "returnFullResponse": true
        }
      },
      "description": "Delete a scheduled event by event ID"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many conversations",
      "routing": {
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "data"
              }
            }
          ]
        },
        "request": {
          "url": "/v2/ai/conversations",
          "method": "GET"
        }
      },
      "description": "Returns a list of conversations"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a conversation",
      "routing": {
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "data"
              }
            }
          ]
        },
        "request": {
          "url": "=/v2/ai/conversations/{{$parameter[\"conversationId\"]}}",
          "method": "GET"
        }
      },
      "description": "Returns a specific conversation by ID"
    },
    {
      "name": "Get Messages",
      "value": "getMessages",
      "action": "Get conversation messages",
      "routing": {
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "data"
              }
            }
          ]
        },
        "request": {
          "url": "=/v2/ai/conversations/{{$parameter[\"conversationId\"]}}/messages",
          "method": "GET"
        }
      },
      "description": "Retrieve messages for a specific conversation, including tool calls made by the assistant"
    },
    {
      "name": "Get Conversation Insights",
      "value": "getConversationInsights",
      "action": "Get insights for a conversation",
      "routing": {
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "data"
              }
            }
          ]
        },
        "request": {
          "url": "=/v2/ai/conversations/{{$parameter[\"conversationId\"]}}/conversations-insights",
          "method": "GET"
        }
      },
      "description": "Retrieve insights for a specific conversation"
    }
  ]

npm_readme:
  # @telnyx/n8n-nodes-telnyx-ai

  This is an n8n community node. It lets you use Telnyx Voice AI in your n8n workflows.

  Telnyx Voice AI is a comprehensive platform that provides advanced conversational AI capabilities, including text-to-speech, speech-to-text, chat completions, and voice assistants with real-time interaction features.

  [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)  
  [Version history](#version-history)

  ## Installation

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

  You can install this package using npm:

  ```bash
  npm install @telnyx/n8n-nodes-telnyx-ai
  ```

  ## Operations

  This node supports the following resources and operations:

  ### Assistants

  - **Get Many**: Retrieve a list of AI Assistants configured by the user
  - **Get**: Get details of a specific assistant

  ### Assistant Events

  - **Create**: Create a scheduled event for an assistant
  - **Get Many**: Get a list of assistant events
  - **Get**: Get details of a specific event
  - **Delete**: Delete an assistant event

  ### Chat

  - **Get Many Models**: Get metadata about available chat models
  - **Create Chat Completion**: Generate chat completions using AI models
  - **Summarize**: Summarize file content using AI

  ### Conversations

  - **Get Many**: Retrieve a list of conversations
  - **Get**: Get details of a specific conversation
  - **Get Messages**: Get messages from a conversation
  - **Get Insights**: Get AI-generated insights for a conversation

  ### Voice

  - **Get Many Voices**: Get metadata about available voice models
  - **Text to Speech**: Convert text to speech audio
  - **Speech to Text**: Transcribe audio to text

  ## Credentials

  To use this node, you need to authenticate with the Telnyx API using your API key.

  ### Prerequisites

  1. Sign up for a [Telnyx account](https://telnyx.com)
  2. Navigate to your [API Keys page](https://portal.telnyx.com/#/app/api-keys) in the Telnyx Portal
  3. Generate a new API key or use an existing one

  ### Setting up credentials in n8n

  1. In n8n, go to **Credentials** and create new credentials
  2. Search for "Telnyx API" and select it
  3. Enter your Telnyx API key in the **API Key** field
  4. Test the credentials to ensure they work correctly

  For more information about Telnyx API keys, refer to the [official documentation](https://support.telnyx.com/en/articles/4305158-api-keys-and-how-to-use-them).

  ## Compatibility

  - **Minimum n8n version**: 1.0.0
  - **Node.js version**: >=20.15
  - **Tested with**: n8n 1.x.x

  This node is built using n8n's programmatic-style node architecture and follows the latest n8n development best practices.

  ## Resources

  - [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  - [Telnyx Voice AI API documentation](https://developers.telnyx.com/docs/api/v2/ai)
  - [Telnyx Portal](https://portal.telnyx.com)
  - [Telnyx Support](https://support.telnyx.com)

  ## Version history

  ### 1.5.5 (Current)

  - Comprehensive Telnyx Voice AI integration
  - Support for Assistants, Chat, Voice, Conversations, and Assistant Events
  - Full CRUD operations for assistant events
  - Text-to-speech and speech-to-text capabilities
  - Chat completions and file summarization
  - Conversation insights and message management

ai_documentation_summary:
  {"purpose":"This n8n community node enables integration with Telnyx Voice AI to perform conversational AI tasks including text-to-speech, speech-to-text, chat completions, and voice assistant management within n8n workflows.","capabilities":["Retrieve and manage AI Assistants (Get, Get Many)","Create, retrieve, delete, and list Assistant Events","Manage conversations (Get, Get Many, Get Messages, Get Insights)","Generate chat completions using AI models","Summarize file content using AI","Retrieve available chat models and voice models metadata","Perform Text-to-Speech conversion","Perform Speech-to-Text transcription"],"authentication":"API key required","commonUseCases":["Automated customer service interactions using voice assistants","Converting text documents or transcripts to speech for announcements","Transcribing audio recordings for analysis or storage","Integrating AI chatbots into n8n workflows for customer support","Generating summaries from large text files or conversation logs"],"limitations":["Node is a community node and may lack official n8n support updates","Requires valid Telnyx API key and account access"],"relatedNodes":["Telnyx Voice","Telnyx Twilio","Generic HTTP Request","Generic AI"]}


node_type: @wix/n8n-nodes-wix.wix
package_name: @wix/n8n-nodes-wix
display_name: Wix
description: Interact with the Wix API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: wix-ci
author_github_url: https://github.com/wix-incubator/n8n-nodes-wix.git
npm_package_name: @wix/n8n-nodes-wix
npm_version: 1.0.4
community_fetched_at: 2026-03-25T21:19:40.585Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "order",
      "options": [
        {
          "name": "Order",
          "value": "order"
        },
        {
          "name": "Product",
          "value": "product"
        }
      ],
      "routing": {
        "send": {
          "preSend": [
            null
          ]
        }
      },
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Cancel",
          "value": "cancel",
          "action": "Cancel an order",
          "routing": {
            "request": {
              "url": "=/ecom/v1/orders/{{$parameter.orderId}}/cancel",
              "method": "POST"
            }
          },
          "description": "Cancel an existing order"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create an order",
          "routing": {
            "request": {
              "url": "/ecom/v1/orders",
              "method": "POST"
            }
          },
          "description": "Create a new order"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get an order",
          "routing": {
            "request": {
              "url": "=/ecom/v1/orders/{{$parameter.orderId}}",
              "method": "GET"
            }
          },
          "description": "Get a single order by ID"
        },
        {
          "name": "Get Many",
          "value": "getMany",
          "action": "Get many orders",
          "routing": {
            "output": {
              "postReceive": [
                null
              ]
            },
            "request": {
              "url": "/ecom/v1/orders/search",
              "method": "POST"
            }
          },
          "description": "Query and list orders"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update an order",
          "routing": {
            "request": {
              "url": "=/ecom/v1/orders/{{$parameter.orderId}}",
              "method": "PATCH"
            }
          },
          "description": "Update an existing order"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "order"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a product",
          "routing": {
            "request": {
              "url": "/stores/v3/products-with-inventory",
              "method": "POST"
            }
          },
          "description": "Create a new product with inventory"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a product",
          "routing": {
            "request": {
              "url": "=/stores/v3/products/{{$parameter.productId}}",
              "method": "DELETE"
            }
          },
          "description": "Delete an existing product"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a product",
          "routing": {
            "request": {
              "url": "=/stores/v3/products/{{$parameter.productId}}",
              "method": "GET"
            }
          },
          "description": "Get a single product by ID"
        },
        {
          "name": "Get Many",
          "value": "getMany",
          "action": "Get many products",
          "routing": {
            "output": {
              "postReceive": [
                null
              ]
            },
            "request": {
              "url": "/stores/v3/products/search",
              "method": "POST"
            }
          },
          "description": "Query and list products"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a product",
          "routing": {
            "request": {
              "url": "=/stores/v3/products-with-inventory/{{$parameter.productId}}",
              "method": "PATCH"
            }
          },
          "description": "Update an existing product with inventory"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "product"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Cancel",
      "value": "cancel",
      "action": "Cancel an order",
      "routing": {
        "request": {
          "url": "=/ecom/v1/orders/{{$parameter.orderId}}/cancel",
          "method": "POST"
        }
      },
      "description": "Cancel an existing order"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create an order",
      "routing": {
        "request": {
          "url": "/ecom/v1/orders",
          "method": "POST"
        }
      },
      "description": "Create a new order"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get an order",
      "routing": {
        "request": {
          "url": "=/ecom/v1/orders/{{$parameter.orderId}}",
          "method": "GET"
        }
      },
      "description": "Get a single order by ID"
    },
    {
      "name": "Get Many",
      "value": "getMany",
      "action": "Get many orders",
      "routing": {
        "output": {
          "postReceive": [
            null
          ]
        },
        "request": {
          "url": "/ecom/v1/orders/search",
          "method": "POST"
        }
      },
      "description": "Query and list orders"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update an order",
      "routing": {
        "request": {
          "url": "=/ecom/v1/orders/{{$parameter.orderId}}",
          "method": "PATCH"
        }
      },
      "description": "Update an existing order"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a product",
      "routing": {
        "request": {
          "url": "/stores/v3/products-with-inventory",
          "method": "POST"
        }
      },
      "description": "Create a new product with inventory"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a product",
      "routing": {
        "request": {
          "url": "=/stores/v3/products/{{$parameter.productId}}",
          "method": "DELETE"
        }
      },
      "description": "Delete an existing product"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a product",
      "routing": {
        "request": {
          "url": "=/stores/v3/products/{{$parameter.productId}}",
          "method": "GET"
        }
      },
      "description": "Get a single product by ID"
    },
    {
      "name": "Get Many",
      "value": "getMany",
      "action": "Get many products",
      "routing": {
        "output": {
          "postReceive": [
            null
          ]
        },
        "request": {
          "url": "/stores/v3/products/search",
          "method": "POST"
        }
      },
      "description": "Query and list products"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a product",
      "routing": {
        "request": {
          "url": "=/stores/v3/products-with-inventory/{{$parameter.productId}}",
          "method": "PATCH"
        }
      },
      "description": "Update an existing product with inventory"
    }
  ]

npm_readme:
  # @wix/n8n-nodes-wix

  This is an n8n community node that lets you use **Wix** in your n8n workflows.

  [Wix](https://www.wix.com/) is a leading cloud-based website development platform that enables users to create professional websites, online stores, bookings, and more. This node allows you to automate workflows by integrating with Wix's eCommerce, CRM, and other services.

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

  - [Installation](#installation)
  - [Operations](#operations)
  - [Triggers](#triggers)
  - [Credentials](#credentials)
  - [Resources](#resources)

  ## Installation

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

  ## Operations

  This node provides actions for managing Products and Orders in your Wix store.

  ### Product

  | Operation | Description |
  |-----------|-------------|
  | **Create** | Create a new product with inventory |
  | **Delete** | Delete an existing product |
  | **Get** | Get a single product by ID |
  | **Get Many** | Query and list products |
  | **Update** | Update an existing product with inventory |

  > **Note**: Product operations use **Catalog V3** and are not compatible with Catalog V1 sites.

  📚 [Wix Stores Products API Documentation](https://dev.wix.com/docs/rest/business-solutions/stores/catalog/products/introduction)

  ### Order

  | Operation | Description |
  |-----------|-------------|
  | **Cancel** | Cancel an existing order |
  | **Create** | Create a new order |
  | **Get** | Get a single order by ID |
  | **Get Many** | Query and list orders |
  | **Update** | Update an existing order |

  📚 [Wix eCommerce Orders API Documentation](https://dev.wix.com/docs/rest/business-solutions/e-commerce/orders/order-object)

  ## Triggers

  The **Wix Trigger** node allows you to start workflows based on events happening in your Wix site. Triggers are powered by [Wix Automations](https://dev.wix.com/docs/rest/business-management/automations/automations/introduction).

  ### Available Triggers

  <details>
  <summary><strong>Back in Stock</strong></summary>

  - Back in stock request created
  - Product back in stock

  </details>

  <details>
  <summary><strong>Bookings</strong></summary>

  - Appointment request approved
  - Appointment request declined
  - Appointment requested
  - Appointment or class updated
  - Booking canceled
  - Client checks in
  - Client checked in X times
  - Client marked as "no-show"
  - Course updated
  - Failed to apply booking fee
  - Failed to collect applied booking fees
  - No sessions left
  - Session booked
  - Session double-booked
  - Session ends
  - Session starts

  </details>

  <details>
  <summary><strong>Blog</strong></summary>

  - Blog post liked
  - Blog post new comment
  - New blog post published

  </details>

  <details>
  <summary><strong>Chat & Inbox</strong></summary>

  - Chat message received
  - Chat message sent
  - Message received (Wix Chat or Inbox preset email)
  - Pre-chat form submitted
  - Visitor on site

  </details>

  <details>
  <summary><strong>CMS</strong></summary>

  - CMS form submitted

  </details>

  <details>
  <summary><strong>Contacts & Segments</strong></summary>

  - Contact Assigned
  - Contact enters segment
  - Contact leaves segment
  - Label added to contact
  - Label removed from contact
  - New contact created

  </details>

  <details>
  <summary><strong>Donations</strong></summary>

  - Donation is made
  - Recurring donation is canceled
  - Recurring donation is charged

  </details>

  <details>
  <summary><strong>Events & Tickets</strong></summary>

  - Daily report
  - Daily RSVP report
  - Event canceled
  - Event ends
  - Event published
  - Event reminder date has arrived
  - Event starts
  - Guest checked in
  - Guest registers to waitlist
  - Guest RSVPd NO email
  - Guest RSVPd YES email
  - Guest RSVPs
  - New spot opened for waitlist guests
  - Order confirmed but ticket couldn't be generated
  - Order placed
  - RSVP confirmed
  - Ticket order email
  - Ticket ordered
  - Ticket sale ended
  - Ticket sale started

  </details>

  <details>
  <summary><strong>File Share</strong></summary>

  - Folder access given
  - Folder access requested

  </details>

  <details>
  <summary><strong>Forms</strong></summary>

  - Form submitted

  </details>

  <details>
  <summary><strong>Forum</strong></summary>

  - Forum comment deleted
  - Forum comment published
  - Forum post published
  - Reacted to Forum comment
  - Reacted to Forum post
  - Removed reaction from Forum comment
  - Removed reaction from Forum post

  </details>

  <details>
  <summary><strong>Groups</strong></summary>

  - Group create request declined
  - Group has new member generic update
  - Group Updated
  - Member comments on a post
  - Member creates a post
  - Member invited to group
  - Member joins a group
  - Member leaves a group
  - Member mentioned someone
  - Member mentions someone
  - Member pays for a group
  - Member reacts to a comment
  - Member reacts to a post
  - Member removed as payment plan expired
  - Member request to join a group approved
  - Member request to join a group declined
  - Member requests to create a group
  - Member requests to join a group
  - Member role updated
  - Members create posts
  - Members join a group
  - New group created
  - Person invited to group
  - Reminder to create group
  - Reminder to invite members
  - Someone you follow creates a post
  - Weekly Digest

  </details>

  <details>
  <summary><strong>Invoices</strong></summary>

  - Invoice overdue
  - Invoice paid
  - Invoice payment received
  - Invoice sent

  </details>

  <details>
  <summary><strong>Loyalty Program</strong></summary>

  - Customer earns points
  - Customer enters tier
  - Customer has enough points for rewards
  - Points balance adjusted manually
  - Points expire in 14 days
  - Social media profile followed

  </details>

  <details>
  <summary><strong>Members</strong></summary>

  - Member approved
  - Member blocked
  - Member deleted
  - Member email confirmation required
  - Member logs in
  - Set contact as member
  - Site member asks to reset password
  - Visitor signs up to your site

  </details>

  <details>
  <summary><strong>Online Programs</strong></summary>

  - Certificate available to download
  - Member invited to join a program
  - Member joins a program
  - Member pays for a program
  - Member's join request approved
  - Member's join request declined
  - Offline payment declined
  - Offline program payment received
  - Participant access denied
  - Participant completes a program
  - Participant completes a step
  - Participant data exported
  - Participant leaves a program
  - Participant manually added to program
  - Payment failed
  - Program end date has passed
  - Program ended without all steps completed
  - Program join request received
  - Reminder to complete scheduled steps
  - Visitor access denied

  </details>

  <details>
  <summary><strong>Pay Links</strong></summary>

  - Payment received
  - Send a pay link

  </details>

  <details>
  <summary><strong>Portfolio</strong></summary>

  - New project created

  </details>

  <details>
  <summary><strong>Price Quotes</strong></summary>

  - Price quote accepted
  - Price quote expired
  - Price quote sent

  </details>

  <details>
  <summary><strong>Pricing Plans</strong></summary>

  - Plan canceled
  - Plan ordered

  </details>

  <details>
  <summary><strong>Proposals</strong></summary>

  - Proposal accepted
  - Proposal sent

  </details>

  <details>
  <summary><strong>Recurring Invoices</strong></summary>

  - Recurring payment canceled
  - Recurring payment starts

  </details>

  <details>
  <summary><strong>Restaurants</strong></summary>

  - Delivery order ready
  - Future order due by
  - Order accepted
  - Order canceled
  - Order created
  - Order edited
  - Order fulfilled
  - Order out for Delivery
  - Pickup order ready
  - Restaurant order placed

  </details>

  <details>
  <summary><strong>Reviews</strong></summary>

  - Review created
  - Review deleted
  - Review moderation status changed
  - Review request created

  </details>

  <details>
  <summary><strong>Stores (eCommerce)</strong></summary>

  - Checkout abandoned
  - Credit card is charged
  - Order placed
  - Payment Added to Order
  - Store order placed

  </details>

  <details>
  <summary><strong>Subscriptions</strong></summary>

  - Customer's payment method updated
  - Payment fails - grace period starts
  - Payment successful - grace period ends
  - Subscription's billing date updated
  - Subscription's item price updated

  </details>

  <details>
  <summary><strong>Table Reservations</strong></summary>

  - Table reservation added manually
  - Table reservation canceled
  - Table reservation starts
  - Table reservation time updated
  - Table reserved by guest

  </details>

  <details>
  <summary><strong>Tasks</strong></summary>

  - Task overdue

  </details>

  <details>
  <summary><strong>Workflows</strong></summary>

  - Card added or moved

  </details>

  📚 [Wix Automations API Documentation](https://dev.wix.com/docs/rest/business-management/automations/automations/introduction)

  ## Credentials

  To use this node, you need to set up Wix API credentials:

  ### Prerequisites

  1. A [Wix](https://www.wix.com/) account with a published website

  ### Setup

  1. Go to the [API Keys Manager](https://manage.wix.com/account/api-keys) to create a new API key
  2. Assign the appropriate permissions for the APIs you want to access
  3. Copy your API key and store it securely
  4. Get your **Site ID** from your site's dashboard URL (it appears after `/dashboard/` in the URL)
  5. In n8n, create new credentials for **Wix API** and enter:
     - **API Key**: Your Wix API key
     - **Site ID**: Your Wix site ID

  > **Important**: Store your API keys securely and use them only in server-side code.

  📚 [How to Generate an API Key](https://dev.wix.com/docs/go-headless/develop-your-project/admin-operations/generate-an-api-key)

  ## Resources

  - [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
  - [Wix REST API Documentation](https://dev.wix.com/docs/api-reference)
  - [Wix Developer Center](https://dev.wix.com/)
  - [Wix Stores API](https://dev.wix.com/docs/rest/business-solutions/stores/catalog/products/introduction)
  - [Wix eCommerce Orders API](https://dev.wix.com/docs/rest/business-solutions/e-commerce/orders/order-object)

ai_documentation_summary:
  {"purpose":"A community n8n node that enables automation by interacting with Wix's eCommerce, CRM, and other services through its API.","capabilities":["Create products with inventory","Delete products","Get single product by ID","List all products","Update product inventory","Cancel orders","Create orders","Get single order by ID","List all orders","Update orders"],"authentication":"API key required","commonUseCases":["Automate product inventory management","Process and track customer orders","React to Wix form submissions","Monitor booking and appointment changes","Sync customer data between Wix and other systems"],"limitations":["Product operations require Catalog V3 and are not compatible with Catalog V1 sites"],"relatedNodes":["Wix Trigger","Wix Order","Wix Product"]}


node_type: @klardaten/n8n-nodes-datevconnect.documentManagement
package_name: @klardaten/n8n-nodes-datevconnect
display_name: Klardaten DATEVconnect: Document Management
description: Interact with document management resources / DMS
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: johannespfeiffer
author_github_url: https://github.com/Klardaten/n8n-nodes-datevconnect.git
npm_package_name: @klardaten/n8n-nodes-datevconnect
npm_version: 1.0.12
community_fetched_at: 2026-03-25T21:19:40.586Z
ai_summary_generated_at: 2026-03-25 22:47:20
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "document",
      "options": [
        {
          "name": "Document",
          "value": "document"
        },
        {
          "name": "Document File",
          "value": "documentFile"
        },
        {
          "name": "Document State",
          "value": "documentState"
        },
        {
          "name": "Domain",
          "value": "domain"
        },
        {
          "name": "Individual Property",
          "value": "individualProperty"
        },
        {
          "name": "Individual Reference 1",
          "value": "individualReference1"
        },
        {
          "name": "Individual Reference 2",
          "value": "individualReference2"
        },
        {
          "name": "Info",
          "value": "info"
        },
        {
          "name": "Property Template",
          "value": "propertyTemplate"
        },
        {
          "name": "Secure Area",
          "value": "secureArea"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Add Structure Item",
          "value": "addStructureItem",
          "action": "Add structure item",
          "description": "Add a structure item to a document"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create a document",
          "description": "Create a new document"
        },
        {
          "name": "Create Dispatcher Information",
          "value": "createDispatcherInformation",
          "action": "Create dispatcher information",
          "description": "Create dispatcher information for a document"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a document",
          "description": "Delete a document"
        },
        {
          "name": "Delete Permanently",
          "value": "deletePermanently",
          "action": "Delete a document permanently",
          "description": "Permanently delete a document"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a document",
          "description": "Retrieve a specific document"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many documents",
          "description": "Retrieve a list of documents"
        },
        {
          "name": "Get Structure Item",
          "value": "getStructureItem",
          "action": "Get structure item",
          "description": "Get a specific structure item"
        },
        {
          "name": "Get Structure Items",
          "value": "getStructureItems",
          "action": "Get structure items",
          "description": "Get structure items of a document"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a document",
          "description": "Update a document"
        },
        {
          "name": "Update Structure Item",
          "value": "updateStructureItem",
          "action": "Update structure item",
          "description": "Update a structure item"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "document"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Get",
          "value": "get",
          "action": "Get a document file",
          "description": "Get a document file"
        },
        {
          "name": "Upload",
          "value": "upload",
          "action": "Upload a document file",
          "description": "Upload a document file"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "documentFile"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many domains",
          "description": "Get many domains"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "domain"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many document states",
          "description": "Get many document states"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a document state",
          "description": "Get a specific document state"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create a document state",
          "description": "Create a new document state"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "documentState"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many secure areas",
          "description": "Retrieve many secure areas"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "secureArea"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many property templates",
          "description": "Retrieve many property templates"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "propertyTemplate"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many individual properties",
          "description": "Retrieve many individual properties"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "individualProperty"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many individual references 1",
          "description": "Retrieve many individual references 1"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create individual reference 1",
          "description": "Create a new individual reference 1"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "individualReference1"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many individual references 2",
          "description": "Retrieve many individual references 2"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create individual reference 2",
          "description": "Create a new individual reference 2"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "individualReference2"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Get",
          "value": "get",
          "action": "Get system info",
          "description": "Get system information"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "info"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Add Structure Item",
      "value": "addStructureItem",
      "action": "Add structure item",
      "description": "Add a structure item to a document"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a document",
      "description": "Create a new document"
    },
    {
      "name": "Create Dispatcher Information",
      "value": "createDispatcherInformation",
      "action": "Create dispatcher information",
      "description": "Create dispatcher information for a document"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a document",
      "description": "Delete a document"
    },
    {
      "name": "Delete Permanently",
      "value": "deletePermanently",
      "action": "Delete a document permanently",
      "description": "Permanently delete a document"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a document",
      "description": "Retrieve a specific document"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many documents",
      "description": "Retrieve a list of documents"
    },
    {
      "name": "Get Structure Item",
      "value": "getStructureItem",
      "action": "Get structure item",
      "description": "Get a specific structure item"
    },
    {
      "name": "Get Structure Items",
      "value": "getStructureItems",
      "action": "Get structure items",
      "description": "Get structure items of a document"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a document",
      "description": "Update a document"
    },
    {
      "name": "Update Structure Item",
      "value": "updateStructureItem",
      "action": "Update structure item",
      "description": "Update a structure item"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a document file",
      "description": "Get a document file"
    },
    {
      "name": "Upload",
      "value": "upload",
      "action": "Upload a document file",
      "description": "Upload a document file"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many domains",
      "description": "Get many domains"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many document states",
      "description": "Get many document states"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a document state",
      "description": "Get a specific document state"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a document state",
      "description": "Create a new document state"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many secure areas",
      "description": "Retrieve many secure areas"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many property templates",
      "description": "Retrieve many property templates"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many individual properties",
      "description": "Retrieve many individual properties"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many individual references 1",
      "description": "Retrieve many individual references 1"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create individual reference 1",
      "description": "Create a new individual reference 1"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many individual references 2",
      "description": "Retrieve many individual references 2"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create individual reference 2",
      "description": "Create a new individual reference 2"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get system info",
      "description": "Get system information"
    }
  ]

npm_readme:
  # n8n DATEVConnect

  This repository provides custom n8n nodes for integrating with the DATEVconnect APIs. It requires the [Klardaten DATEVconnect Gateway](https://klardaten.com/products/datevconnect-gateway) to be set up.

  ## Available Nodes

  | Node                           | Description                                                                                                             |
  | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
  | `Accounting`                   | Access DATEV Accounting API for clients, fiscal years, accounts receivable, account postings, and accounting sequences. |
  | `MasterData`                   | Fetches master data entities from DATEV using the configured credentials.                                               |
  | `Document Management`          | Access DATEV Document Management or DMS for documents, structure items, property templates.                             |
  | `Order Management`             | Read and manage orders, suborders, invoices, fees, and related employee data.                                           |
  | `Identity & Access Management` | Manage SCIM-based DATEV IAM resources such as service configuration, schemas, users, and groups.                        |

  Refer to the node descriptions inside the n8n editor for input parameters and output structure.

  ## Installation

  ### Through the n8n UI

  1. Navigate to **Settings → Community Nodes → Install** inside your n8n instance.
  2. Enter `@klardaten/n8n-nodes-datevconnect` as the package name.
  3. Confirm the warning prompt and restart n8n so the nodes are registered.

  ### Manual (filesystem) installation

  ```bash
  cd ~/.n8n
  npm install @klardaten/n8n-nodes-datevconnect
  ```

  Copy or symlink the contents of the installed `node_modules/@klardaten/n8n-nodes-datevconnect/dist/{nodes,credentials}` directories into your `~/.n8n/custom/` folder if you prefer the legacy custom-nodes workflow, then restart n8n.

  ## Development

  1. **Install dependencies**
     ```bash
     npm install
     ```
  2. **Build** (optional during development; n8n compiles on the fly)
     ```bash
     npm run dev
     ```

  ## Required Credentials

  You need a Klardaten account and an instance id (of the DATEV instance you want to access).

  - `email`
  - `password`
  - `clientInstanceId`

  ## Running Tests

  Use the npm test runner to execute the automated tests:

  ```bash
  npm test
  ```

  ## Usage in n8n

  1. Create a new workflow in n8n.
  2. Add the **Klardaten DATEVconnect: Master Data** or **Accounting** etc. node and choose the credential you configured.
  3. Select the desired operation (e.g., list client entries) and configure any filters.
  4. Execute the workflow or schedule it as part of a larger automation.

  All nodes authenticate using the stored credentials and interact with their respective DATEVconnect APIs.

  ## Additional Notes

  DATEV provides endpoint reference details under

  - [Accounting](https://developer.datev.de/en/product-detail/accounting/1.7.4/reference)
  - [Client Master Data](https://developer.datev.de/en/product-detail/client-master-data/1.7.0/reference).
  - [Document Management](https://developer.datev.de/en/product-detail/document-management/2.3.0/reference)
  - [Order Management](https://developer.datev.de/en/product-detail/order-management/1.4.7/reference)
  - [Identity & Access Management – User Administration](https://developer.datev.de/en/product-detail/identity-and-access-management-user-administration/1.1.2/reference)

ai_documentation_summary:
  {"purpose":"This n8n node enables interaction with DATEV Document Management (DMS) resources, allowing users to manage documents, structure items, and property templates within the DATEVconnect ecosystem.","capabilities":["Access DATEV Document Management resources","Retrieve document information","Manage structure items","Handle property templates","Integrate with DATEVconnect APIs"],"authentication":"API key required (via email, password, and clientInstanceId credentials)","commonUseCases":["Automated document retrieval and processing","Document structure management","Property template configuration","Integration with DATEV accounting workflows"],"limitations":["Requires Klardaten DATEVconnect Gateway setup","Requires valid Klardaten account and DATEV instance ID","Additional documentation for specific operations must be referenced in n8n editor"],"relatedNodes":["Klardaten DATEVconnect: Accounting","Klardaten DATEVconnect: MasterData","Klardaten DATEVconnect: Order Management","Klardaten DATEVconnect: Identity & Access Management"]}


node_type: n8n-nodes-kargo-entegrator.kargoEntegrator
package_name: n8n-nodes-kargo-entegrator
display_name: Kargo Entegratör
description: Interact with Kargo Entegratör API for shipment management
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: gurmesoftcom
author_github_url: https://github.com/gurmesoft/
npm_package_name: n8n-nodes-kargo-entegrator
npm_version: 0.1.1
community_fetched_at: 2026-03-25T21:19:40.587Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "shipment",
      "options": [
        {
          "name": "Cargo",
          "value": "cargo",
          "description": "Get cargo companies information"
        },
        {
          "name": "Return",
          "value": "return",
          "description": "Manage return shipments"
        },
        {
          "name": "Setting",
          "value": "settings",
          "description": "Get system settings"
        },
        {
          "name": "Shipment",
          "value": "shipment",
          "description": "Manage shipments"
        },
        {
          "name": "Warehouse",
          "value": "warehouse",
          "description": "Manage warehouses"
        }
      ],
      "required": true,
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "create",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a shipment",
          "description": "Create a new shipment"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a shipment",
          "description": "Get shipment details"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many shipments",
          "description": "Get many shipments with pagination"
        },
        {
          "name": "Print Shipment PDF",
          "value": "printPdf",
          "action": "Print shipment PDF",
          "description": "Generate PDF document for shipments"
        }
      ],
      "required": true,
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "shipment"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "create",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a return shipment",
          "description": "Create a return shipment"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a return shipment",
          "description": "Get a return shipment by ID"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many return shipments",
          "description": "Get many return shipments"
        },
        {
          "name": "Print Returned PDF",
          "value": "printReturnedPdf",
          "action": "Print returned PDF",
          "description": "Generate PDF output for a returned shipment"
        }
      ],
      "required": true,
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "return"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many cargo companies",
          "description": "Get many cargo companies"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a cargo company",
          "description": "Get specific cargo company"
        }
      ],
      "required": true,
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "cargo"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many warehouses",
          "description": "Get many warehouses"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a warehouse",
          "description": "Get specific warehouse"
        }
      ],
      "required": true,
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "warehouse"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getShipmentSettings",
      "options": [
        {
          "name": "Get Shipment Settings",
          "value": "getShipmentSettings",
          "action": "Get shipment settings"
        },
        {
          "name": "Get Return Settings",
          "value": "getReturnSettings",
          "action": "Get return settings"
        }
      ],
      "required": true,
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "settings"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Create",
      "value": "create",
      "action": "Create a shipment",
      "description": "Create a new shipment"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a shipment",
      "description": "Get shipment details"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many shipments",
      "description": "Get many shipments with pagination"
    },
    {
      "name": "Print Shipment PDF",
      "value": "printPdf",
      "action": "Print shipment PDF",
      "description": "Generate PDF document for shipments"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a return shipment",
      "description": "Create a return shipment"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a return shipment",
      "description": "Get a return shipment by ID"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many return shipments",
      "description": "Get many return shipments"
    },
    {
      "name": "Print Returned PDF",
      "value": "printReturnedPdf",
      "action": "Print returned PDF",
      "description": "Generate PDF output for a returned shipment"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many cargo companies",
      "description": "Get many cargo companies"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a cargo company",
      "description": "Get specific cargo company"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many warehouses",
      "description": "Get many warehouses"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a warehouse",
      "description": "Get specific warehouse"
    },
    {
      "name": "Get Shipment Settings",
      "value": "getShipmentSettings",
      "action": "Get shipment settings"
    },
    {
      "name": "Get Return Settings",
      "value": "getReturnSettings",
      "action": "Get return settings"
    }
  ]

npm_readme:
  # n8n-nodes-kargo-entegrator

  This is an n8n community node. It lets you use Kargo Entegratör API in your n8n workflows.

  Kargo Entegratör is a Turkish cargo integration platform that allows you to manage shipments across multiple cargo companies from a single interface.

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

  ## Table of Contents

  - [Quick Start](#quick-start)
  - [Development Setup](#development-setup)
  - [Docker Environment](#docker-environment)
  - [Installation](#installation)
  - [Operations](#operations)
  - [Credentials](#credentials)
  - [Scripts](#scripts)
  - [Compatibility](#compatibility)
  - [Usage](#usage)
  - [Resources](#resources)
  - [Version History](#version-history)
  - [License](#license)

  ## Quick Start

  ### Prerequisites
  - Node.js >= 20.15
  - npm or yarn
  - Docker (optional, for containerized development)

  ### Local Development

  1. **Clone and Install**
     ```bash
     git clone https://github.com/gurmesoft/n8n-nodes-kargo-entegrator.git
     cd n8n-nodes-kargo-entegrator
     npm install
     ```

  2. **Build the Project**
     ```bash
     npm run build
     ```

  3. **Start Development**
     ```bash
     npm run dev
     ```

  ### Docker Development (Recommended)

  1. **Quick Start with Docker**
     ```bash
     # Build and start development environment
     npm run dev:docker
     
     # Access N8N at http://localhost:5678
     # Username: admin
     # Password: admin123
     ```

  2. **View Logs**
     ```bash
     npm run dev:docker:logs
     ```

  3. **Stop Environment**
     ```bash
     npm run dev:docker:stop
     ```

  ## Development Setup

  ### Environment Requirements
  - **Node.js**: >= 20.15
  - **TypeScript**: ^5.8.2
  - **n8n-workflow**: Latest (peer dependency)

  ### Project Structure
  ```
  n8n-nodes-kargo-entegrator/
  ├── credentials/           # API credentials configuration
  ├── nodes/                # Node implementations
  │   └── KargoEntegrator/
  │       ├── *.node.ts     # Node logic
  │       ├── actions/      # Node actions
  │       └── resources/    # Node resources
  ├── dist/                 # Built files (generated)
  ├── docker-compose*.yml   # Docker configurations
  └── package.json          # Project configuration
  ```

  ### Development Workflow

  1. **Make Changes**: Edit TypeScript files in `nodes/` or `credentials/`
  2. **Build**: Run `npm run build` to compile TypeScript
  3. **Test**: Use Docker environment or local N8N instance
  4. **Format**: Run `npm run format` to format code
  5. **Lint**: Run `npm run lint` to check code quality

  ## Docker Environment

  Three Docker configurations are available:

  ### 1. Development Environment (Recommended)
  ```bash
  npm run dev:docker
  # or
  docker-compose -f docker-compose.dev.yml up -d
  ```
  - **Services**: N8N + PostgreSQL + Node development container
  - **Features**: Hot reload, debugging, persistent data
  - **Access**: http://localhost:5678

  ### 2. Simple Environment
  ```bash
  docker-compose -f docker-compose.simple.yml up -d
  ```
  - **Services**: N8N only (SQLite database)
  - **Use Case**: Quick testing
  - **Access**: http://localhost:5678

  ### 3. Production Environment
  ```bash
  docker-compose up -d
  ```
  - **Services**: N8N + PostgreSQL
  - **Use Case**: Production deployment
  - **Access**: http://localhost:5678

  ### Docker Commands
  ```bash
  # Start development environment
  npm run dev:docker

  # View logs
  npm run dev:docker:logs

  # Restart services
  npm run dev:docker:restart

  # Stop environment
  npm run dev:docker:stop

  # Build and restart with live reload
  npm run dev:live
  ```

  ### Access Information
  - **N8N Web Interface**: http://localhost:5678
  - **Username**: admin
  - **Password**: admin123
  - **PostgreSQL**: localhost:5432 (production) / localhost:5433 (development)
    - User: n8n
    - Password: n8n123
    - Database: n8n

  ## Scripts

  | Script | Description |
  |--------|-------------|
  | `npm run build` | Build TypeScript and copy icons |
  | `npm run dev` | Watch TypeScript files for changes |
  | `npm run dev:live` | Development with auto-restart |
  | `npm run dev:docker` | Start Docker development environment |
  | `npm run dev:docker:logs` | View Docker logs |
  | `npm run dev:docker:stop` | Stop Docker environment |
  | `npm run dev:docker:restart` | Restart Docker services |
  | `npm run format` | Format code with Prettier |
  | `npm run lint` | Lint code with ESLint |
  | `npm run lintfix` | Fix linting issues automatically |  

  ## 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-kargo-entegrator`
  4. Agree to the risks of using community nodes
  5. Select **Install**

  ### Manual Installation

  ```bash
  npm install n8n-nodes-kargo-entegrator
  ```

  ## Operations

  ### Shipment
  - **Create**: Create a new shipment
  - **Get**: Retrieve shipment details
  - **Create Return**: Create a return shipment

  ### Customer
  - **Update**: Update customer information

  ### Cargo
  - **Get All**: Get all available cargo companies
  - **Get**: Get specific cargo company details

  ## Credentials

  To use this node, you need:

  1. Sign up for an account at [https://app.kargoentegrator.com](https://app.kargoentegrator.com)
  2. Get your API key from the dashboard
  3. Configure the credentials in n8n:
     - **API Key**: Your Kargo Entegratör API key
     - **Base URL**: https://app.kargoentegrator.com/api (default)

  ## Compatibility

  Tested against n8n version 1.0+.

  ## Usage

  ### Creating a Shipment

  1. Select "Shipment" as the resource
  2. Choose "Create" as the operation
  3. Fill in the required fields:
     - Receiver name, phone, and address
     - City and district
     - Cargo company ID
     - Package details (weight, count, description)

  ### Managing Returns

  Use the "Create Return" operation to create return shipments with the same interface as regular shipments.

  ### Getting Cargo Companies

  Use the "Cargo" resource to get information about available cargo companies and their IDs for use in shipment creation.

  ## Resources

  * [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
  * [Kargo Entegratör API Documentation](https://documenter.getpostman.com/view/25047990/2sAY4vg2RR)
  * [Kargo Entegratör Website](https://kargoentegrator.com/)

  ## Version History

  ### 0.1.0
  - Initial release
  - Support for shipment creation, retrieval, and PDF generation
  - Support for return shipment management
  - Cargo company and warehouse information retrieval
  - Webhook trigger for shipment status changes
  - System settings access

  ## Contributing

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

  ## Support

  For support, please contact [destek@kargoentegrator.com](mailto:destek@kargoentegrator.com) or visit our [website](https://kargoentegrator.com/).

  ## License

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

ai_documentation_summary:
  {"purpose":"This n8n community node enables workflow automation for managing shipments across multiple Turkish cargo companies via the Kargo Entegratör API.","capabilities":["Create new shipments","Retrieve shipment details","Create return shipments","Update customer information","List all available cargo companies","Retrieve specific cargo company details"],"authentication":"API key required","commonUseCases":["Automating cross-company logistics tracking","Managing return shipments for e-commerce","Centralizing cargo company data retrieval","Integrating Turkish shipping services into n8n workflows"],"limitations":["Community node (requires manual installation)","Requires separate account at Kargo Entegratör","Requires Node.js >= 20.15 for development"],"relatedNodes":["HTTP Request","Credentials","Shipment"]}


node_type: n8n-nodes-mallabe-images.mallabeImages
package_name: n8n-nodes-mallabe-images
display_name: Mallabe Images
category: Images
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: dinbrca
author_github_url: https://github.com/mallabe/
npm_package_name: n8n-nodes-mallabe-images
npm_version: 0.1.7
community_fetched_at: 2026-03-25T21:19:40.588Z
ai_summary_generated_at: 2026-03-25 22:51:43
description:
  Mallabe Images is an automation toolchain that allows you to process images, resize, crop and apply other manipulations on images on the fly.

properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "image",
      "options": [
        {
          "name": "Image",
          "value": "image"
        },
        {
          "name": "Custom API Call",
          "value": "__CUSTOM_API_CALL__"
        }
      ],
      "required": true,
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "resize",
      "options": [
        {
          "name": "Blur Image",
          "value": "blur",
          "action": "Blur image",
          "description": "Blur images and send the new image to another integration"
        },
        {
          "name": "Compress Image",
          "value": "compress",
          "action": "Compress image",
          "description": "Compress images and drive the new image to another integration.Note: if a given URL isn't an image file (png, jpg/jpeg, etc..) - the file will not convert. Note: some Zapier integrations give URLs that are protected - these can't be used & compressed."
        },
        {
          "name": "Crop Image",
          "value": "crop",
          "action": "Crop image",
          "description": "Crop images and send the new image to another integration. Note: If a given URL isn't an image file (e.g., png, jpg/jpeg, gif), the file will not convert. Also, some Zapier integrations provide protected URLs that cannot be cropped."
        },
        {
          "name": "Extract Image Metadata",
          "value": "metadata",
          "action": "Extract image metadata",
          "description": "Extract image metadata. Note: some integrations give URLs that are protected - these can't be used & resized."
        },
        {
          "name": "Flip Image",
          "value": "flip",
          "action": "Flip image",
          "description": "Flip images and send the new image to another integration"
        },
        {
          "name": "Greyscale Image",
          "value": "greyscale",
          "action": "Greyscale image",
          "description": "Greyscale images and send the new image to another integration"
        },
        {
          "name": "Join/Combine Images",
          "value": "join",
          "action": "Join combine images",
          "description": "Join images into one image and send the new image to another integration"
        },
        {
          "name": "Resize Image",
          "value": "resize",
          "action": "Resize image",
          "description": "Resize or Scale images and drive the new image to another integration. Note: if a given URL isn't an image file (png, jpg/jpeg, gif, etc..) - the file will not convert.Note: some Zapier integrations give URLs that are protected - these can't be used & resized."
        },
        {
          "name": "Rotate Image",
          "value": "rotate",
          "action": "Rotate image",
          "description": "Rotate images and send the new image to another integration"
        },
        {
          "name": "Custom API Call",
          "value": "__CUSTOM_API_CALL__"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "image"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Blur Image",
      "value": "blur",
      "action": "Blur image",
      "description": "Blur images and send the new image to another integration"
    },
    {
      "name": "Compress Image",
      "value": "compress",
      "action": "Compress image",
      "description": "Compress images and drive the new image to another integration.Note: if a given URL isn't an image file (png, jpg/jpeg, etc..) - the file will not convert. Note: some Zapier integrations give URLs that are protected - these can't be used & compressed."
    },
    {
      "name": "Crop Image",
      "value": "crop",
      "action": "Crop image",
      "description": "Crop images and send the new image to another integration. Note: If a given URL isn't an image file (e.g., png, jpg/jpeg, gif), the file will not convert. Also, some Zapier integrations provide protected URLs that cannot be cropped."
    },
    {
      "name": "Extract Image Metadata",
      "value": "metadata",
      "action": "Extract image metadata",
      "description": "Extract image metadata. Note: some integrations give URLs that are protected - these can't be used & resized."
    },
    {
      "name": "Flip Image",
      "value": "flip",
      "action": "Flip image",
      "description": "Flip images and send the new image to another integration"
    },
    {
      "name": "Greyscale Image",
      "value": "greyscale",
      "action": "Greyscale image",
      "description": "Greyscale images and send the new image to another integration"
    },
    {
      "name": "Join/Combine Images",
      "value": "join",
      "action": "Join combine images",
      "description": "Join images into one image and send the new image to another integration"
    },
    {
      "name": "Resize Image",
      "value": "resize",
      "action": "Resize image",
      "description": "Resize or Scale images and drive the new image to another integration. Note: if a given URL isn't an image file (png, jpg/jpeg, gif, etc..) - the file will not convert.Note: some Zapier integrations give URLs that are protected - these can't be used & resized."
    },
    {
      "name": "Rotate Image",
      "value": "rotate",
      "action": "Rotate image",
      "description": "Rotate images and send the new image to another integration"
    },
    {
      "name": "Custom API Call",
      "value": "__CUSTOM_API_CALL__"
    }
  ]

npm_readme:
  ![Mallabe Logo](/docs/mallabe-logo.png)

  # n8n-nodes-mallabe-images

  This is an n8n community node for [Mallabe Images](https://www.mallabe.com/).

  It allows you to manipulate images using different operations.

  For more information, refer to the [Mallabe website](https://www.mallabe.com/).

  Current supported operations:
  - Resize Image
  - Compress Image
  - Get Image Metadata
  - Crop Image
  - Rotate Image
  - Flip / Flop Image
  - Greyscale Image
  - Join 2 Images

  ## License

  [MIT](https://github.com/n8n-io/n8n-nodes-starter/blob/master/LICENSE.md)

  ## Development
  ### Running Locally (Summary)
  1. Install n8n globally using https://docs.n8n.io/integrations/creating-nodes/test/run-node-locally/
  2. Run `nvm use` to use the correct node version in the node folder.
  3. Run `pnpm install` in the node folder.
  4. Run `pnpm link --global` to register as package.
  5. Run `pnpm run build` whenever you make changes to the node.
  6. Run `cd ~/.n8n`
  7. Stop n8n if it was already running.
  8. Run `pnpm link --global n8n-nodes-mallabe-images` in n8n `custom` folder.
  9. Run `n8n start`

  Note: if you like to watch the development instead of rebuilding every time, use `npm run dev`.

ai_documentation_summary:
  {"purpose":"This n8n community node enables real-time image manipulation by processing images through operations like resizing, cropping, and applying filters.","capabilities":["Resize images","Compress images","Get image metadata","Crop images","Rotate images","Flip/Flop images","Convert to greyscale","Join two images"],"authentication":"API key required (implied by external service integration)","commonUseCases":["Automating image preprocessing for e-commerce listings","Batch image optimization for web performance","Applying consistent filters or crops to image sets","Generating image thumbnails dynamically"],"limitations":["Limited to operations supported by the external Mallabe service","Requires external API access to Mallabe Images service"],"relatedNodes":["HTTP Request","Code","Wait Until"]}


node_type: @klardaten/n8n-nodes-datevconnect.identityAndAccessManagement
package_name: @klardaten/n8n-nodes-datevconnect
display_name: Klardaten DATEVconnect: Identity & Access
description: Work with DATEV Identity and Access Management (SCIM) endpoints
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: johannespfeiffer
author_github_url: https://github.com/Klardaten/n8n-nodes-datevconnect.git
npm_package_name: @klardaten/n8n-nodes-datevconnect
npm_version: 1.0.12
community_fetched_at: 2026-03-25T21:19:40.589Z
ai_summary_generated_at: 2026-03-25 22:47:20
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "user",
      "options": [
        {
          "name": "Current User",
          "value": "currentUser"
        },
        {
          "name": "Group",
          "value": "group"
        },
        {
          "name": "Resource Type",
          "value": "resourceType"
        },
        {
          "name": "Schema",
          "value": "schema"
        },
        {
          "name": "Service Provider Config",
          "value": "serviceProviderConfig"
        },
        {
          "name": "User",
          "value": "user"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Get",
          "value": "get",
          "action": "Get service provider config",
          "description": "Retrieve the SCIM service provider configuration"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "serviceProviderConfig"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many resource types",
          "description": "Retrieve the available SCIM resource types"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "resourceType"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many schemas",
          "description": "List the supported SCIM schemas"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get schema",
          "description": "Retrieve a single SCIM schema by ID"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "schema"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a user",
          "description": "Create a new DATEV IAM user"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a user",
          "description": "Delete a DATEV IAM user"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a user",
          "description": "Retrieve a single DATEV IAM user"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many users",
          "description": "List DATEV IAM users"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a user",
          "description": "Update an existing DATEV IAM user"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "user"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Get",
          "value": "get",
          "action": "Get current user",
          "description": "Fetch the currently authenticated DATEV IAM user"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "currentUser"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a group",
          "description": "Create a new DATEV IAM group"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a group",
          "description": "Delete a DATEV IAM group"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a group",
          "description": "Retrieve a single DATEV IAM group"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many groups",
          "description": "List DATEV IAM groups"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a group",
          "description": "Update an existing DATEV IAM group"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "group"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Get",
      "value": "get",
      "action": "Get service provider config",
      "description": "Retrieve the SCIM service provider configuration"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many resource types",
      "description": "Retrieve the available SCIM resource types"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many schemas",
      "description": "List the supported SCIM schemas"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get schema",
      "description": "Retrieve a single SCIM schema by ID"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a user",
      "description": "Create a new DATEV IAM user"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a user",
      "description": "Delete a DATEV IAM user"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a user",
      "description": "Retrieve a single DATEV IAM user"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many users",
      "description": "List DATEV IAM users"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a user",
      "description": "Update an existing DATEV IAM user"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get current user",
      "description": "Fetch the currently authenticated DATEV IAM user"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a group",
      "description": "Create a new DATEV IAM group"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a group",
      "description": "Delete a DATEV IAM group"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a group",
      "description": "Retrieve a single DATEV IAM group"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many groups",
      "description": "List DATEV IAM groups"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a group",
      "description": "Update an existing DATEV IAM group"
    }
  ]

npm_readme:
  # n8n DATEVConnect

  This repository provides custom n8n nodes for integrating with the DATEVconnect APIs. It requires the [Klardaten DATEVconnect Gateway](https://klardaten.com/products/datevconnect-gateway) to be set up.

  ## Available Nodes

  | Node                           | Description                                                                                                             |
  | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
  | `Accounting`                   | Access DATEV Accounting API for clients, fiscal years, accounts receivable, account postings, and accounting sequences. |
  | `MasterData`                   | Fetches master data entities from DATEV using the configured credentials.                                               |
  | `Document Management`          | Access DATEV Document Management or DMS for documents, structure items, property templates.                             |
  | `Order Management`             | Read and manage orders, suborders, invoices, fees, and related employee data.                                           |
  | `Identity & Access Management` | Manage SCIM-based DATEV IAM resources such as service configuration, schemas, users, and groups.                        |

  Refer to the node descriptions inside the n8n editor for input parameters and output structure.

  ## Installation

  ### Through the n8n UI

  1. Navigate to **Settings → Community Nodes → Install** inside your n8n instance.
  2. Enter `@klardaten/n8n-nodes-datevconnect` as the package name.
  3. Confirm the warning prompt and restart n8n so the nodes are registered.

  ### Manual (filesystem) installation

  ```bash
  cd ~/.n8n
  npm install @klardaten/n8n-nodes-datevconnect
  ```

  Copy or symlink the contents of the installed `node_modules/@klardaten/n8n-nodes-datevconnect/dist/{nodes,credentials}` directories into your `~/.n8n/custom/` folder if you prefer the legacy custom-nodes workflow, then restart n8n.

  ## Development

  1. **Install dependencies**
     ```bash
     npm install
     ```
  2. **Build** (optional during development; n8n compiles on the fly)
     ```bash
     npm run dev
     ```

  ## Required Credentials

  You need a Klardaten account and an instance id (of the DATEV instance you want to access).

  - `email`
  - `password`
  - `clientInstanceId`

  ## Running Tests

  Use the npm test runner to execute the automated tests:

  ```bash
  npm test
  ```

  ## Usage in n8n

  1. Create a new workflow in n8n.
  2. Add the **Klardaten DATEVconnect: Master Data** or **Accounting** etc. node and choose the credential you configured.
  3. Select the desired operation (e.g., list client entries) and configure any filters.
  4. Execute the workflow or schedule it as part of a larger automation.

  All nodes authenticate using the stored credentials and interact with their respective DATEVconnect APIs.

  ## Additional Notes

  DATEV provides endpoint reference details under

  - [Accounting](https://developer.datev.de/en/product-detail/accounting/1.7.4/reference)
  - [Client Master Data](https://developer.datev.de/en/product-detail/client-master-data/1.7.0/reference).
  - [Document Management](https://developer.datev.de/en/product-detail/document-management/2.3.0/reference)
  - [Order Management](https://developer.datev.de/en/product-detail/order-management/1.4.7/reference)
  - [Identity & Access Management – User Administration](https://developer.datev.de/en/product-detail/identity-and-access-management-user-administration/1.1.2/reference)

ai_documentation_summary:
  {"purpose":"This n8n node enables management of SCIM-based Identity and Access Management (IAM) resources within DATEVconnect, including service configuration, schemas, users, and groups.","capabilities":["Manage SCIM-based DATEV IAM resources","Configure service settings","Manage user accounts","Manage group memberships","Handle schema definitions"],"authentication":"API key required (via email, password, and clientInstanceId credentials)","commonUseCases":["Automated user provisioning and deprovisioning in DATEV","Managing group access policies","Syncing user schemas between DATEV and external systems"],"limitations":["Requires a Klardaten DATEVconnect Gateway to be set up","Requires a valid Klardaten account and DATEV instance ID","Relies on external DATEV API endpoints which may have rate limits or version constraints"],"relatedNodes":["Klardaten DATEVconnect: Accounting","Klardaten DATEVconnect: MasterData","Klardaten DATEVconnect: Document Management","Klardaten DATEVconnect: Order Management"]}


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

updated_at: 2026-03-25 21:19:40
author_name: netgsm
author_github_url: https://github.com/netgsm/n8n-nodes-netgsm.git
npm_package_name: @netgsm/n8n-nodes-netgsm
npm_version: 0.1.41
community_fetched_at: 2026-03-25T21:19:40.589Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "sms",
      "options": [
        {
          "name": "SMS",
          "value": "sms"
        },
        {
          "name": "ILETI YONETIM SISTEMI",
          "value": "iys"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "smsSend",
      "options": [
        {
          "name": "SMS Send",
          "value": "smsSend",
          "action": "Send a text message",
          "routing": {
            "send": {
              "type": "body",
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "/sms/rest/v2/send",
              "json": true,
              "encoding": "json",
              "returnFullResponse": true
            }
          },
          "description": "Send a text message to a phone number"
        },
        {
          "name": "SMS Status Query",
          "value": "smsStatusQuery",
          "action": "Check SMS status",
          "routing": {
            "send": {
              "type": "body",
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "/sms/rest/v2/report",
              "json": true,
              "encoding": "json",
              "returnFullResponse": true
            }
          },
          "description": "Check delivery status of a sent SMS using Job ID"
        },
        {
          "name": "SMS Inbox Query",
          "value": "smsInboxQuery",
          "action": "Get inbox messages",
          "routing": {
            "request": {
              "url": "/sms/rest/v2/inbox",
              "method": "GET",
              "returnFullResponse": true
            }
          },
          "description": "Retrieve received SMS messages from inbox"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "sms"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "iysAdd",
      "options": [
        {
          "name": "Add IYS Address",
          "value": "iysAdd",
          "action": "Add phone number or email",
          "routing": {
            "send": {
              "type": "body",
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "/iys/add",
              "json": true,
              "encoding": "json",
              "returnFullResponse": true
            }
          },
          "description": "This service is used to add a phone number or email address to IYS"
        },
        {
          "name": "Query IYS Address",
          "value": "iysQuery",
          "action": "Query phone number or email",
          "routing": {
            "send": {
              "type": "body",
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "/iys/search",
              "json": true,
              "encoding": "json",
              "returnFullResponse": true
            }
          },
          "description": "This service is used to add a phone number or email address to IYS"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "iys"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "SMS Send",
      "value": "smsSend",
      "action": "Send a text message",
      "routing": {
        "send": {
          "type": "body",
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "/sms/rest/v2/send",
          "json": true,
          "encoding": "json",
          "returnFullResponse": true
        }
      },
      "description": "Send a text message to a phone number"
    },
    {
      "name": "SMS Status Query",
      "value": "smsStatusQuery",
      "action": "Check SMS status",
      "routing": {
        "send": {
          "type": "body",
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "/sms/rest/v2/report",
          "json": true,
          "encoding": "json",
          "returnFullResponse": true
        }
      },
      "description": "Check delivery status of a sent SMS using Job ID"
    },
    {
      "name": "SMS Inbox Query",
      "value": "smsInboxQuery",
      "action": "Get inbox messages",
      "routing": {
        "request": {
          "url": "/sms/rest/v2/inbox",
          "method": "GET",
          "returnFullResponse": true
        }
      },
      "description": "Retrieve received SMS messages from inbox"
    },
    {
      "name": "Add IYS Address",
      "value": "iysAdd",
      "action": "Add phone number or email",
      "routing": {
        "send": {
          "type": "body",
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "/iys/add",
          "json": true,
          "encoding": "json",
          "returnFullResponse": true
        }
      },
      "description": "This service is used to add a phone number or email address to IYS"
    },
    {
      "name": "Query IYS Address",
      "value": "iysQuery",
      "action": "Query phone number or email",
      "routing": {
        "send": {
          "type": "body",
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "/iys/search",
          "json": true,
          "encoding": "json",
          "returnFullResponse": true
        }
      },
      "description": "This service is used to add a phone number or email address to IYS"
    }
  ]

npm_readme:
  # n8n-nodes-netgsm

  This is the official Netgsm n8n community node. This node integrates with the SMS Gateway to send messages directly from n8n workflows.

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

  [Installation](#installation)  
  [Operations](#operations)  
  [Credentials](#credentials)  <!-- delete if no auth needed -->  
  [Compatibility](#compatibility)  
  [Usage](#usage)  <!-- delete if not using this section -->  
  [Resources](#resources)  
  [Version history](#version-history)  <!-- delete if not using this section -->  

  ## Installation

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

  ## Operations

  ### SMS
  - SMS Send
  - SMS Status Query
  - List SMS Inbox

  ## Credentials

  This node requires an API Username and Password from Netgsm, You can generate one by going to your [Dashboard](https://portal.netgsm.com.tr/webportal/abonelikislemleri/yetkilendirme/set).

  ## Compatibility

  This node has been tested with n8n 1.94.0

  ## Usage

  [To be completed]

  ## Resources

  * [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  * [Netgsm Documentation](https://www.netgsm.com.tr/dokuman)

  ## Version history

  ### 0.1.0
  - Initial version of the project

ai_documentation_summary:
  {"purpose":"This n8n community node enables interaction with the Netgsm SMS Gateway API to send SMS messages, query message status, and list messages from the inbox directly within n8n workflows.","capabilities":["Send SMS messages","Query SMS message status","List SMS inbox messages"],"authentication":"API Username and Password","commonUseCases":["Automated SMS notifications for user actions","Two-factor authentication (2FA) message delivery","Order confirmation and shipping updates"],"limitations":["Requires manual setup of API credentials in the Netgsm dashboard","Usage section in documentation is incomplete"],"relatedNodes":["HTTP Request","Code"]}


node_type: n8n-nodes-bounceban.bounceban
package_name: n8n-nodes-bounceban
display_name: BounceBan
description: Verify email addresses using BounceBan API - We verify catch-all emails
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: davis.lee
author_github_url: https://github.com/bounceban-com/
npm_package_name: n8n-nodes-bounceban
npm_version: 1.1.1
community_fetched_at: 2026-03-25T21:19:40.590Z
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "email",
      "options": [
        {
          "name": "Email",
          "value": "email",
          "description": "Validate email"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "validateEmail",
      "options": [
        {
          "name": "Verify Single Email",
          "value": "validateEmail",
          "action": "Verify a single email address",
          "description": "Verify a single email address"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "email"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Verify Single Email",
      "value": "validateEmail",
      "action": "Verify a single email address",
      "description": "Verify a single email address"
    }
  ]


node_type: @predictleads/n8n-nodes-predictleads.predictLeads
package_name: @predictleads/n8n-nodes-predictleads
display_name: PredictLeads
description: Get company insights from PredictLeads' official node
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: deankostomaj_predictleads
author_github_url: https://github.com/PredictLeads/n8n-nodes-predict-leads.git
npm_package_name: @predictleads/n8n-nodes-predictleads
npm_version: 1.0.1
community_fetched_at: 2026-03-25T21:19:40.591Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "companies",
      "options": [
        {
          "name": "Company",
          "value": "companies"
        },
        {
          "name": "Connection",
          "value": "connections"
        },
        {
          "name": "Financing Event",
          "value": "financingEvents"
        },
        {
          "name": "Job Opening",
          "value": "jobOpenings"
        },
        {
          "name": "News Event",
          "value": "newsEvents"
        },
        {
          "name": "Technology",
          "value": "technologies"
        },
        {
          "name": "Technology Detection",
          "value": "technologyDetections"
        },
        {
          "name": "Website Evolution",
          "value": "websiteEvolution"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Retrieve Company",
          "value": "retrieveCompany",
          "action": "Retrieve Company",
          "routing": {
            "request": {
              "url": "=/companies/{{$parameter.domain}}",
              "method": "GET"
            }
          },
          "description": "Returns Company"
        },
        {
          "name": "Retrieve Companies",
          "value": "retrieveCompanies",
          "action": "Retrieve Companies",
          "routing": {
            "request": {
              "qs": {
                "page": "={{ $parameter.page }}",
                "limit": "={{ $parameter.limit }}",
                "sizes": "={{ $parameter.sizes }}",
                "location": "={{ $parameter.location }}"
              },
              "url": "=/discover/companies",
              "method": "GET",
              "arrayFormat": "brackets"
            }
          },
          "description": "Returns a list of companies filtered by their location and size, ordered by last updated date"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "companies"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Retrieve Company Job Openings",
          "value": "retrieveCompanyJobOpenings",
          "action": "Retrieve Company Job Openings",
          "routing": {
            "request": {
              "qs": {
                "page": "={{ $parameter.page }}",
                "limit": "={{ $parameter.limit }}",
                "categories": "={{ $parameter.categories.join(\",\") }}",
                "not_closed": "={{ $parameter.notClosed }}",
                "active_only": "={{ $parameter.activeOnly }}",
                "last_seen_at_from": "={{ $parameter.lastSeenAtFrom }}",
                "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
                "last_seen_at_until": "={{ $parameter.lastSeenAtUntil }}",
                "with_location_only": "={{ $parameter.withLocationOnly }}",
                "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}",
                "with_description_only": "={{ $parameter.withDescriptionOnly }}"
              },
              "url": "=/companies/{{$parameter.domain}}/job_openings",
              "method": "GET"
            }
          },
          "description": "Returns a list of company's Job Openings"
        },
        {
          "name": "Retrieve Single Job Opening",
          "value": "retrieveSingleJobOpening",
          "action": "Retrieve Single Job Opening",
          "routing": {
            "request": {
              "url": "=/job_openings/{{$parameter.id}}",
              "method": "GET"
            }
          },
          "description": "Returns a single Job Opening"
        },
        {
          "name": "Retrieve a List of Job Openings",
          "value": "retrieveListOfJobOpenings",
          "action": "Retrieve a List of Job Openings",
          "routing": {
            "request": {
              "qs": {
                "page": "={{ $parameter.page }}",
                "limit": "={{ $parameter.limit }}",
                "location": "={{ $parameter.location }}",
                "onet_codes": "={{ $parameter.onetCodes }}"
              },
              "url": "=/discover/job_openings",
              "method": "GET"
            }
          },
          "description": "Returns a list of Job Openings filtered by a given profession (e.g. \"Sales Engineers\" or \"Marketing Managers\") using O*NET occupation codes and/or location of the company"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "jobOpenings"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Retrieve Technologies Used by Specific Company",
          "value": "retrieveTechnologiesUsedByCompany",
          "action": "Retrieve Technologies used by specific Company",
          "routing": {
            "request": {
              "qs": {
                "page": "={{ $parameter.page }}",
                "limit": "={{ $parameter.limit }}",
                "last_seen_at_from": "={{ $parameter.lastSeenAtFrom }}",
                "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
                "last_seen_at_until": "={{ $parameter.lastSeenAtUntil }}",
                "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
              },
              "url": "=/companies/{{$parameter.domain}}/technology_detections",
              "method": "GET"
            }
          },
          "description": "Returns Technologies used by a specific Company as a list of Technology Detections"
        },
        {
          "name": "Retrieve Companies Using Specific Technology ID or Fuzzy Name",
          "value": "retrieveCompaniesUsingSpecificTechnology",
          "action": "Retrieve Companies using specific Technology ID or Fuzzy Name",
          "routing": {
            "request": {
              "qs": {
                "page": "={{ $parameter.page }}",
                "limit": "={{ $parameter.limit }}",
                "last_seen_at_from": "={{ $parameter.lastSeenAtFrom }}",
                "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
                "last_seen_at_until": "={{ $parameter.lastSeenAtUntil }}",
                "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
              },
              "url": "=/discover/technologies/{{$parameter.idOrFuzzyName}}/technology_detections",
              "method": "GET"
            }
          },
          "description": "Returns Companies using a specific Technology as a list of Technology Detections, ordered by the first_seen_at, descending"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "technologyDetections"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Retrieve All Tracked Technologies",
          "value": "retrieveAllTrackedTechnologies",
          "action": "Retrieve All Tracked Technologies",
          "routing": {
            "request": {
              "qs": {
                "page": "={{ $parameter.page }}",
                "limit": "={{ $parameter.limit }}",
                "order_by": "={{ $parameter.orderBy }}",
                "fuzzy_name": "={{ $parameter.fuzzyName }}"
              },
              "url": "=/technologies",
              "method": "GET"
            }
          },
          "description": "Returns a list of all tracked Technologies"
        },
        {
          "name": "Retrieve a Single Technology by ID or Fuzzy Name",
          "value": "retrieveSingleTechnologyByIdOrFuzzyName",
          "action": "Retrieve a Single Technology by ID or Fuzzy Name",
          "routing": {
            "request": {
              "url": "=/technologies/{{$parameter.idOrFuzzyName}}",
              "method": "GET"
            }
          },
          "description": "Returns a single Technology"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "technologies"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Retrieve Company News Events",
          "value": "retrieveCompanyNewsEvents",
          "action": "Retrieve Company News Events",
          "routing": {
            "request": {
              "qs": {
                "page": "={{ $parameter.page }}",
                "limit": "={{ $parameter.limit }}",
                "categories": "={{ $parameter.categories.join(\",\") }}",
                "found_at_from": "={{ $parameter.foundAtFrom }}",
                "found_at_until": "={{ $parameter.foundAtUntil }}"
              },
              "url": "=/companies/{{$parameter.domain}}/news_events",
              "method": "GET"
            }
          },
          "description": "Returns a list of company's News Events"
        },
        {
          "name": "Retrieve Single News Event",
          "value": "retrieveSingleNewsEvent",
          "action": "Retrieve Single News Event",
          "routing": {
            "request": {
              "url": "=/news_events/{{$parameter.id}}",
              "method": "GET"
            }
          },
          "description": "Returns specific News Event"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "newsEvents"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Retrieve Company Financing Events",
          "value": "retrieveCompanyFinancingEvents",
          "action": "Retrieve Company Financing Events",
          "routing": {
            "request": {
              "qs": {
                "page": "={{ $parameter.page }}",
                "limit": "={{ $parameter.limit }}",
                "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
                "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
              },
              "url": "=/companies/{{$parameter.domain}}/financing_events",
              "method": "GET"
            }
          },
          "description": "Returns a list of company's Financing Events"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "financingEvents"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Retrieve Company Connections",
          "value": "retrieveCompanyConnections",
          "action": "Retrieve Company Connections",
          "routing": {
            "request": {
              "qs": {
                "page": "={{ $parameter.page }}",
                "limit": "={{ $parameter.limit }}",
                "categories": "={{ $parameter.categories.join(\",\") }}",
                "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
                "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
              },
              "url": "=/companies/{{$parameter.domain}}/connections",
              "method": "GET"
            }
          },
          "description": "Returns a list of company's Connections"
        },
        {
          "name": "Retrieve Portfolio Companies",
          "value": "retrievePortfolioCompanies",
          "action": "Retrieve Portfolio Companies",
          "routing": {
            "request": {
              "qs": {
                "page": "={{ $parameter.page }}",
                "limit": "={{ $parameter.limit }}",
                "last_seen_at_from": "={{ $parameter.lastSeenAtFrom }}",
                "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
                "last_seen_at_until": "={{ $parameter.lastSeenAtUntil }}",
                "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
              },
              "url": "=/discover/portfolio_companies/connections",
              "method": "GET"
            }
          },
          "description": "Returns the Portfolio Companies found on the portfolio pages of thousands of VCs, accelerators, and incubators, as a list of Connections categorized as investor, ordered by the first_seen_at, descending"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "connections"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Retrieve Company Website Evolution",
          "value": "retrieveCompanyWebsiteEvolution",
          "action": "Retrieve Company Website Evolution",
          "routing": {
            "request": {
              "qs": {
                "page": "={{ $parameter.page }}",
                "limit": "={{ $parameter.limit }}",
                "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
                "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
              },
              "url": "=/companies/{{$parameter.domain}}/website_evolution",
              "method": "GET"
            }
          },
          "description": "Returns a list of company's Website Evolution"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "websiteEvolution"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Retrieve Company",
      "value": "retrieveCompany",
      "action": "Retrieve Company",
      "routing": {
        "request": {
          "url": "=/companies/{{$parameter.domain}}",
          "method": "GET"
        }
      },
      "description": "Returns Company"
    },
    {
      "name": "Retrieve Companies",
      "value": "retrieveCompanies",
      "action": "Retrieve Companies",
      "routing": {
        "request": {
          "qs": {
            "page": "={{ $parameter.page }}",
            "limit": "={{ $parameter.limit }}",
            "sizes": "={{ $parameter.sizes }}",
            "location": "={{ $parameter.location }}"
          },
          "url": "=/discover/companies",
          "method": "GET",
          "arrayFormat": "brackets"
        }
      },
      "description": "Returns a list of companies filtered by their location and size, ordered by last updated date"
    },
    {
      "name": "Retrieve Company Job Openings",
      "value": "retrieveCompanyJobOpenings",
      "action": "Retrieve Company Job Openings",
      "routing": {
        "request": {
          "qs": {
            "page": "={{ $parameter.page }}",
            "limit": "={{ $parameter.limit }}",
            "categories": "={{ $parameter.categories.join(\",\") }}",
            "not_closed": "={{ $parameter.notClosed }}",
            "active_only": "={{ $parameter.activeOnly }}",
            "last_seen_at_from": "={{ $parameter.lastSeenAtFrom }}",
            "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
            "last_seen_at_until": "={{ $parameter.lastSeenAtUntil }}",
            "with_location_only": "={{ $parameter.withLocationOnly }}",
            "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}",
            "with_description_only": "={{ $parameter.withDescriptionOnly }}"
          },
          "url": "=/companies/{{$parameter.domain}}/job_openings",
          "method": "GET"
        }
      },
      "description": "Returns a list of company's Job Openings"
    },
    {
      "name": "Retrieve Single Job Opening",
      "value": "retrieveSingleJobOpening",
      "action": "Retrieve Single Job Opening",
      "routing": {
        "request": {
          "url": "=/job_openings/{{$parameter.id}}",
          "method": "GET"
        }
      },
      "description": "Returns a single Job Opening"
    },
    {
      "name": "Retrieve a List of Job Openings",
      "value": "retrieveListOfJobOpenings",
      "action": "Retrieve a List of Job Openings",
      "routing": {
        "request": {
          "qs": {
            "page": "={{ $parameter.page }}",
            "limit": "={{ $parameter.limit }}",
            "location": "={{ $parameter.location }}",
            "onet_codes": "={{ $parameter.onetCodes }}"
          },
          "url": "=/discover/job_openings",
          "method": "GET"
        }
      },
      "description": "Returns a list of Job Openings filtered by a given profession (e.g. \"Sales Engineers\" or \"Marketing Managers\") using O*NET occupation codes and/or location of the company"
    },
    {
      "name": "Retrieve Technologies Used by Specific Company",
      "value": "retrieveTechnologiesUsedByCompany",
      "action": "Retrieve Technologies used by specific Company",
      "routing": {
        "request": {
          "qs": {
            "page": "={{ $parameter.page }}",
            "limit": "={{ $parameter.limit }}",
            "last_seen_at_from": "={{ $parameter.lastSeenAtFrom }}",
            "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
            "last_seen_at_until": "={{ $parameter.lastSeenAtUntil }}",
            "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
          },
          "url": "=/companies/{{$parameter.domain}}/technology_detections",
          "method": "GET"
        }
      },
      "description": "Returns Technologies used by a specific Company as a list of Technology Detections"
    },
    {
      "name": "Retrieve Companies Using Specific Technology ID or Fuzzy Name",
      "value": "retrieveCompaniesUsingSpecificTechnology",
      "action": "Retrieve Companies using specific Technology ID or Fuzzy Name",
      "routing": {
        "request": {
          "qs": {
            "page": "={{ $parameter.page }}",
            "limit": "={{ $parameter.limit }}",
            "last_seen_at_from": "={{ $parameter.lastSeenAtFrom }}",
            "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
            "last_seen_at_until": "={{ $parameter.lastSeenAtUntil }}",
            "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
          },
          "url": "=/discover/technologies/{{$parameter.idOrFuzzyName}}/technology_detections",
          "method": "GET"
        }
      },
      "description": "Returns Companies using a specific Technology as a list of Technology Detections, ordered by the first_seen_at, descending"
    },
    {
      "name": "Retrieve All Tracked Technologies",
      "value": "retrieveAllTrackedTechnologies",
      "action": "Retrieve All Tracked Technologies",
      "routing": {
        "request": {
          "qs": {
            "page": "={{ $parameter.page }}",
            "limit": "={{ $parameter.limit }}",
            "order_by": "={{ $parameter.orderBy }}",
            "fuzzy_name": "={{ $parameter.fuzzyName }}"
          },
          "url": "=/technologies",
          "method": "GET"
        }
      },
      "description": "Returns a list of all tracked Technologies"
    },
    {
      "name": "Retrieve a Single Technology by ID or Fuzzy Name",
      "value": "retrieveSingleTechnologyByIdOrFuzzyName",
      "action": "Retrieve a Single Technology by ID or Fuzzy Name",
      "routing": {
        "request": {
          "url": "=/technologies/{{$parameter.idOrFuzzyName}}",
          "method": "GET"
        }
      },
      "description": "Returns a single Technology"
    },
    {
      "name": "Retrieve Company News Events",
      "value": "retrieveCompanyNewsEvents",
      "action": "Retrieve Company News Events",
      "routing": {
        "request": {
          "qs": {
            "page": "={{ $parameter.page }}",
            "limit": "={{ $parameter.limit }}",
            "categories": "={{ $parameter.categories.join(\",\") }}",
            "found_at_from": "={{ $parameter.foundAtFrom }}",
            "found_at_until": "={{ $parameter.foundAtUntil }}"
          },
          "url": "=/companies/{{$parameter.domain}}/news_events",
          "method": "GET"
        }
      },
      "description": "Returns a list of company's News Events"
    },
    {
      "name": "Retrieve Single News Event",
      "value": "retrieveSingleNewsEvent",
      "action": "Retrieve Single News Event",
      "routing": {
        "request": {
          "url": "=/news_events/{{$parameter.id}}",
          "method": "GET"
        }
      },
      "description": "Returns specific News Event"
    },
    {
      "name": "Retrieve Company Financing Events",
      "value": "retrieveCompanyFinancingEvents",
      "action": "Retrieve Company Financing Events",
      "routing": {
        "request": {
          "qs": {
            "page": "={{ $parameter.page }}",
            "limit": "={{ $parameter.limit }}",
            "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
            "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
          },
          "url": "=/companies/{{$parameter.domain}}/financing_events",
          "method": "GET"
        }
      },
      "description": "Returns a list of company's Financing Events"
    },
    {
      "name": "Retrieve Company Connections",
      "value": "retrieveCompanyConnections",
      "action": "Retrieve Company Connections",
      "routing": {
        "request": {
          "qs": {
            "page": "={{ $parameter.page }}",
            "limit": "={{ $parameter.limit }}",
            "categories": "={{ $parameter.categories.join(\",\") }}",
            "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
            "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
          },
          "url": "=/companies/{{$parameter.domain}}/connections",
          "method": "GET"
        }
      },
      "description": "Returns a list of company's Connections"
    },
    {
      "name": "Retrieve Portfolio Companies",
      "value": "retrievePortfolioCompanies",
      "action": "Retrieve Portfolio Companies",
      "routing": {
        "request": {
          "qs": {
            "page": "={{ $parameter.page }}",
            "limit": "={{ $parameter.limit }}",
            "last_seen_at_from": "={{ $parameter.lastSeenAtFrom }}",
            "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
            "last_seen_at_until": "={{ $parameter.lastSeenAtUntil }}",
            "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
          },
          "url": "=/discover/portfolio_companies/connections",
          "method": "GET"
        }
      },
      "description": "Returns the Portfolio Companies found on the portfolio pages of thousands of VCs, accelerators, and incubators, as a list of Connections categorized as investor, ordered by the first_seen_at, descending"
    },
    {
      "name": "Retrieve Company Website Evolution",
      "value": "retrieveCompanyWebsiteEvolution",
      "action": "Retrieve Company Website Evolution",
      "routing": {
        "request": {
          "qs": {
            "page": "={{ $parameter.page }}",
            "limit": "={{ $parameter.limit }}",
            "first_seen_at_from": "={{ $parameter.firstSeenAtFrom }}",
            "first_seen_at_until": "={{ $parameter.firstSeenAtUntil }}"
          },
          "url": "=/companies/{{$parameter.domain}}/website_evolution",
          "method": "GET"
        }
      },
      "description": "Returns a list of company's Website Evolution"
    }
  ]

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

  # n8n-nodes-predict-leads

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

  [PredictLeads](https://predictleads.com/) company intelligence data powers a wide range of business applications, from AI-powered automation to market research and investment analysis.

  ## 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:
  - retrieve company's data
  - retrieve companies' data based on their size and location
  - retrieve company's job openings
  - retrieve a single job opening
  - retrieve a list of job openings based on O*NET codes and location
  - retrieve technologies used by a specific company
  - retrieve companies using a specific technology
  - retrieve all tracked technologies
  - retrieve a single technology
  - retrieve company's news events
  - retrieve a single news event
  - retrieve company's financing events
  - retrieve company's connections
  - retrieve portfolio companies
  - retrieve company's website evolution

  ## Credentials

  You must have a PredictLeads API key and token to use this node. You can register for a free account to get your API key and token here:

  https://predictleads.com/sign_in

  Once registered, you can find your API key and token here:

  https://predictleads.com/dashboard

  Then you'll need to create credentials in n8n for PredictLeads.

  ![Add Credentials](images/add-credentials.png)

  ## Compatibility

  - Minimum n8n version: 1.84.0
  - Verified working with: 1.119.1
  - Known issues: None

  ## Usage

  Add PredictLeads node to a workflow.

  ![Add PredictLeads to Workflow](images/workflow.png)

  Configure a basic query and hit "Test step" to get the data.

  ![Test a company search through PredictLeads](images/test-company-search.png)

  ## API Resources / Search Engines

  This node currently only supports the most popular PredictLeads endpoints. If there's an endpoints missing that you would like to use, please let us know at support@predictleads.com

  In the meantime, you can also use the [generic HTTP Request node](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/) to get the data from other endpoints.

  ## Related Resources

  PredictLeads's complete API documentation is available here:

  https://docs.predictleads.com/

  ## License

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

ai_documentation_summary:
  {"purpose":"This n8n community node retrieves company intelligence data from PredictLeads, including company profiles, job openings, technologies, news, financing events, and connections.","capabilities":["Retrieve company data","Search companies by size and location","Retrieve company job openings","Retrieve specific job opening details","Search job openings by O*NET codes and location","Retrieve technologies used by a company","Find companies using a specific technology","Retrieve all tracked technologies","Retrieve specific technology details","Retrieve company news events"],"authentication":"API key and token required","commonUseCases":["Market research and competitor analysis","Investment analysis and due diligence","AI-powered automation workflows","Recruitment and talent sourcing","Technology stack analysis"],"limitations":["Only supports the most popular PredictLeads endpoints; some endpoints may be missing","Missing endpoints can be accessed via the generic HTTP Request node instead"],"relatedNodes":["n8n-nodes-base.httprequest"]}


node_type: @oneai-eu/n8n-nodes-oneai.oneAi
package_name: @oneai-eu/n8n-nodes-oneai
display_name: OneAI
description: Interact with the OneAI API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: oneai-dev
author_github_url: https://github.com/oneai-eu/n8n-nodes-oneai.git
npm_package_name: @oneai-eu/n8n-nodes-oneai
npm_version: 0.1.7
community_fetched_at: 2026-03-25T21:19:40.592Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "project",
      "options": [
        {
          "name": "AI",
          "value": "ai"
        },
        {
          "name": "Artifact",
          "value": "artifact"
        },
        {
          "name": "Chat",
          "value": "chat"
        },
        {
          "name": "Project",
          "value": "project"
        },
        {
          "name": "Reference",
          "value": "reference"
        },
        {
          "name": "Space",
          "value": "space"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "listAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create an artifact",
          "description": "Create an artifact from a file"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete an artifact",
          "description": "Delete an artifact from a space"
        },
        {
          "name": "Export PDF",
          "value": "exportPdf",
          "action": "Export artifact as PDF",
          "description": "Export an artifact as a PDF"
        },
        {
          "name": "Get Markdown",
          "value": "getMarkdown",
          "action": "Get artifact markdown",
          "description": "Get the markdown content of an artifact"
        },
        {
          "name": "List All",
          "value": "listAll",
          "action": "List all artifacts",
          "description": "List all artifacts with optional filtering"
        },
        {
          "name": "List by Space",
          "value": "listBySpace",
          "action": "List artifacts in space",
          "description": "List artifacts in a specific space"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "artifact"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a chat",
          "description": "Create a new chat"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a chat",
          "description": "Delete a chat"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a chat",
          "description": "Get chat history"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all chats",
          "description": "List chats with optional filtering"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a chat",
          "description": "Update chat details (rename or move)"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "chat"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "createResponse",
      "options": [
        {
          "name": "Create Response",
          "value": "createResponse",
          "action": "Create a response",
          "description": "Send a message to an AI model and get a response"
        },
        {
          "name": "List Available AI Models",
          "value": "listModels",
          "action": "List available AI models",
          "description": "List all available AI models"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "ai"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a project",
          "description": "Create a new project"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a project",
          "description": "Delete a project"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a project",
          "description": "Get a project by ID"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all projects",
          "description": "List all projects"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a project",
          "description": "Update a project"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "project"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "listSpaces",
      "options": [
        {
          "name": "List Files",
          "value": "listFiles",
          "action": "List reference files",
          "description": "List files accessible for attaching as references"
        },
        {
          "name": "List Spaces",
          "value": "listSpaces",
          "action": "List reference spaces",
          "description": "List spaces accessible for attaching as references"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "reference"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Add Team",
          "value": "addTeam",
          "action": "Add team to space",
          "description": "Add a team to a space"
        },
        {
          "name": "Add User",
          "value": "addUser",
          "action": "Add user to space",
          "description": "Add a user to a space"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create a space",
          "description": "Create a new space"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a space",
          "description": "Delete a space"
        },
        {
          "name": "Delete File",
          "value": "deleteFile",
          "action": "Delete file from space",
          "description": "Delete a file from a space"
        },
        {
          "name": "Download File",
          "value": "downloadFile",
          "action": "Download file from space",
          "description": "Download a file from a space"
        },
        {
          "name": "Embed Files",
          "value": "embedFiles",
          "action": "Embed files in space",
          "description": "Queue files/folders for embedding"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a space",
          "description": "Get a space by ID"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all spaces",
          "description": "List all spaces"
        },
        {
          "name": "List Files",
          "value": "listFiles",
          "action": "List files in space",
          "description": "List files in a space"
        },
        {
          "name": "List Teams",
          "value": "listTeams",
          "action": "List teams in space",
          "description": "List teams assigned to a space"
        },
        {
          "name": "List Users",
          "value": "listUsers",
          "action": "List users in space",
          "description": "List users assigned to a space"
        },
        {
          "name": "Remove Team",
          "value": "removeTeam",
          "action": "Remove team from space",
          "description": "Remove a team from a space"
        },
        {
          "name": "Remove User",
          "value": "removeUser",
          "action": "Remove user from space",
          "description": "Remove a user from a space"
        },
        {
          "name": "Sync",
          "value": "sync",
          "action": "Sync a space",
          "description": "Synchronize a linked space"
        },
        {
          "name": "Transfer File",
          "value": "transferFile",
          "action": "Transfer file between spaces",
          "description": "Move or copy a file between spaces"
        },
        {
          "name": "Upload File",
          "value": "uploadFile",
          "action": "Upload file to space",
          "description": "Upload a file to a space"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "space"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Create",
      "value": "create",
      "action": "Create an artifact",
      "description": "Create an artifact from a file"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete an artifact",
      "description": "Delete an artifact from a space"
    },
    {
      "name": "Export PDF",
      "value": "exportPdf",
      "action": "Export artifact as PDF",
      "description": "Export an artifact as a PDF"
    },
    {
      "name": "Get Markdown",
      "value": "getMarkdown",
      "action": "Get artifact markdown",
      "description": "Get the markdown content of an artifact"
    },
    {
      "name": "List All",
      "value": "listAll",
      "action": "List all artifacts",
      "description": "List all artifacts with optional filtering"
    },
    {
      "name": "List by Space",
      "value": "listBySpace",
      "action": "List artifacts in space",
      "description": "List artifacts in a specific space"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a chat",
      "description": "Create a new chat"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a chat",
      "description": "Delete a chat"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a chat",
      "description": "Get chat history"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all chats",
      "description": "List chats with optional filtering"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a chat",
      "description": "Update chat details (rename or move)"
    },
    {
      "name": "Create Response",
      "value": "createResponse",
      "action": "Create a response",
      "description": "Send a message to an AI model and get a response"
    },
    {
      "name": "List Available AI Models",
      "value": "listModels",
      "action": "List available AI models",
      "description": "List all available AI models"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a project",
      "description": "Create a new project"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a project",
      "description": "Delete a project"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a project",
      "description": "Get a project by ID"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all projects",
      "description": "List all projects"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a project",
      "description": "Update a project"
    },
    {
      "name": "List Files",
      "value": "listFiles",
      "action": "List reference files",
      "description": "List files accessible for attaching as references"
    },
    {
      "name": "List Spaces",
      "value": "listSpaces",
      "action": "List reference spaces",
      "description": "List spaces accessible for attaching as references"
    },
    {
      "name": "Add Team",
      "value": "addTeam",
      "action": "Add team to space",
      "description": "Add a team to a space"
    },
    {
      "name": "Add User",
      "value": "addUser",
      "action": "Add user to space",
      "description": "Add a user to a space"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a space",
      "description": "Create a new space"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a space",
      "description": "Delete a space"
    },
    {
      "name": "Delete File",
      "value": "deleteFile",
      "action": "Delete file from space",
      "description": "Delete a file from a space"
    },
    {
      "name": "Download File",
      "value": "downloadFile",
      "action": "Download file from space",
      "description": "Download a file from a space"
    },
    {
      "name": "Embed Files",
      "value": "embedFiles",
      "action": "Embed files in space",
      "description": "Queue files/folders for embedding"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a space",
      "description": "Get a space by ID"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all spaces",
      "description": "List all spaces"
    },
    {
      "name": "List Files",
      "value": "listFiles",
      "action": "List files in space",
      "description": "List files in a space"
    },
    {
      "name": "List Teams",
      "value": "listTeams",
      "action": "List teams in space",
      "description": "List teams assigned to a space"
    },
    {
      "name": "List Users",
      "value": "listUsers",
      "action": "List users in space",
      "description": "List users assigned to a space"
    },
    {
      "name": "Remove Team",
      "value": "removeTeam",
      "action": "Remove team from space",
      "description": "Remove a team from a space"
    },
    {
      "name": "Remove User",
      "value": "removeUser",
      "action": "Remove user from space",
      "description": "Remove a user from a space"
    },
    {
      "name": "Sync",
      "value": "sync",
      "action": "Sync a space",
      "description": "Synchronize a linked space"
    },
    {
      "name": "Transfer File",
      "value": "transferFile",
      "action": "Transfer file between spaces",
      "description": "Move or copy a file between spaces"
    },
    {
      "name": "Upload File",
      "value": "uploadFile",
      "action": "Upload file to space",
      "description": "Upload a file to a space"
    }
  ]

npm_readme:
  # n8n-nodes-oneai

  A community node for [n8n](https://n8n.io/) that integrates with the [OneAI](https://oneai.eu) platform — an AI hub providing access to multiple AI models and collaboration features.

  ## Installation

  ### In n8n (Community Node)

  1. Go to **Settings > Community Nodes**
  2. Select **Install a community node**
  3. Enter `@oneai-eu/n8n-nodes-oneai`
  4. Agree to the risks and click **Install**

  ### Manual / Development

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

  # Install dependencies
  pnpm install

  # Build
  pnpm build
  ```

  ## Authentication

  This node uses **Bearer Token** authentication via an API key.

  To configure credentials in n8n:

  | Field       | Description                                          |
  | ----------- | ---------------------------------------------------- |
  | **OneAI URL** | Base URL of your OneAI instance (e.g. `https://hub.oneai.eu`) |
  | **API Key**   | Your API key, generated from your OneAI hub settings          |

  ## Supported Resources & Operations

  ### AI (2 operations)

  Send messages to AI models and manage model availability.

  | Operation                | Description                                        |
  | ------------------------ | -------------------------------------------------- |
  | Create Response          | Send a message to an AI model and get a response   |
  | List Available AI Models | List all available AI models                       |

  **Input modes:**
  - **Messages** — user-friendly role/content fields
  - **JSON** — full control for complex conversations, images, etc.

  **Advanced options:** Temperature, Reasoning Effort, Reasoning Summary, Tools (function calling), Chat Association (route through a OneAI chat for context)

  ### Space (17 operations)

  Manage workspaces with support for multiple storage providers (Local, OneDrive, SharePoint, Google Drive, GitHub).

  | Operation       | Description                              |
  | --------------- | ---------------------------------------- |
  | Add Team        | Add a team to a space                    |
  | Add User        | Add a user to a space                    |
  | Create          | Create a new space                       |
  | Delete          | Delete a space                           |
  | Delete File     | Delete a file from a space               |
  | Download File   | Download a file from a space             |
  | Embed Files     | Queue files/folders for embedding        |
  | Get             | Get a space by ID                        |
  | List            | List all spaces                          |
  | List Files      | List files in a space                    |
  | List Teams      | List teams assigned to a space           |
  | List Users      | List users assigned to a space           |
  | Remove Team     | Remove a team from a space               |
  | Remove User     | Remove a user from a space               |
  | Sync            | Synchronize a linked space               |
  | Transfer File   | Move or copy a file between spaces       |
  | Upload File     | Upload a file to a space                 |

  ### Chat (6 operations)

  Create and manage AI chat conversations.

  | Operation   | Description                                        |
  | ----------- | -------------------------------------------------- |
  | Create      | Create a new chat                                  |
  | Delete      | Delete a chat                                      |
  | Get         | Get chat history                                   |
  | List        | List chats with optional filtering                 |
  | Update      | Update chat details (rename, move, or set branch)  |

  ### Artifact (6 operations)

  Manage documents and files within spaces.

  | Operation      | Description                              |
  | -------------- | ---------------------------------------- |
  | Create         | Create an artifact from a file           |
  | Delete         | Delete an artifact from a space          |
  | Export PDF     | Export an artifact as a PDF              |
  | Get Markdown   | Get the markdown content of an artifact  |
  | List All       | List all artifacts with optional filtering |
  | List by Space  | List artifacts in a specific space       |

  ### Project (5 operations)

  Organize work into projects.

  | Operation | Description            |
  | --------- | ---------------------- |
  | Create    | Create a new project   |
  | Delete    | Delete a project       |
  | Get       | Get a project by ID    |
  | List      | List all projects      |
  | Update    | Update a project       |

  ### Reference (2 operations)

  Access references for attaching to conversations and artifacts.

  | Operation    | Description                                         |
  | ------------ | --------------------------------------------------- |
  | List Files   | List files accessible for attaching as references   |
  | List Spaces  | List spaces accessible for attaching as references  |

  ## Features

  - **Multi-model AI access** — query multiple AI models through a single node
  - **Streaming support** — handles Server-Sent Events for real-time AI responses
  - **Automatic pagination** — list operations transparently handle paginated API responses
  - **Cloud storage integration** — connect spaces to OneDrive, SharePoint, Google Drive, or GitHub
  - **Function calling** — pass tool definitions to AI models via JSON
  - **File management** — upload, download, transfer, and embed files across spaces

  ## Development

  ```bash
  # Build
  pnpm build

  # Watch mode (recompile on changes)
  pnpm dev

  # Lint & format
  pnpm lint
  pnpm format
  ```

  ## License

  [MIT](LICENSE)

ai_documentation_summary:
  {"purpose":"A community n8n node that integrates with the OneAI platform to interact with multiple AI models, manage workspaces, and handle file operations.","capabilities":["Send messages to AI models and receive responses","List available AI models","Manage workspaces (spaces) with multiple storage providers","Upload, download, and transfer files between spaces","Create and manage AI chat conversations","Export artifacts as PDF or retrieve markdown content","Organize work into projects","Attach files and spaces as references to conversations","Support for Server-Sent Events (streaming)","Function calling (tools) for AI models"],"authentication":"API Key (Bearer Token) via OneAI URL and API Key fields","commonUseCases":["Integrating multiple AI models into a single workflow","Managing collaborative workspaces with cloud storage (OneDrive, SharePoint, Google Drive, GitHub)","Processing and organizing documents through AI-generated artifacts","Building chat assistants with context from external files"],"limitations":["Community node (requires manual installation via n8n settings)","Requires a OneAI hub instance and API key"],"relatedNodes":["n8n HTTP Request","n8n Code","n8n Function Item"]}


node_type: n8n-nodes-vge.vgeAidr
package_name: n8n-nodes-vge
display_name: VGE AIDR
description: AI Detection & Response - LLM security gateway
category: Community
development_style: declarative
version: 1
properties_schema: []
operations: []
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: tbartel74
author_github_url: https://github.com/Vigil-Guard/vge-n8n-nodes.git
npm_package_name: n8n-nodes-vge
npm_version: 1.0.3
community_fetched_at: 2026-03-25T21:19:40.593Z
ai_summary_generated_at: 2026-03-25 22:51:43
npm_readme:
  # n8n-nodes-vge

  ![n8n version](https://img.shields.io/badge/n8n-%3E%3D1.0.0-blue)
  ![Node.js](https://img.shields.io/badge/node-%3E%3D18.10-green)
  ![License](https://img.shields.io/badge/license-MIT-blue)

  n8n community node for **Vigil Guard Enterprise** - AI Detection & Response (AIDR) for LLM security.

  Protect your AI workflows from prompt injection attacks, PII leakage, harmful content, and policy violations with multi-language support.

  > **Note:** Vigil Guard Enterprise is a **self-hosted solution**. There is no public cloud API. You must deploy VGE on your own infrastructure before using this node.

  ## Overview

  The **VGE AIDR** node acts as a security gateway for LLM workflows, providing real-time protection for both inputs and outputs. Deploy it as a guard before your AI agent to filter malicious prompts, and after your AI agent to prevent sensitive data leakage and harmful content.

  ```
  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
  │ Chat Trigger │───►│  VGE AIDR    │───►│  AI Agent    │───►│  VGE AIDR    │───►│   Response   │
  │              │    │ (Input Guard)│    │  (OpenAI)    │    │(Output Guard)│    │              │
  └──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘
                             │                                       │
                             │       VGE Detection Pipeline:         │
                             │  • Prompt injection attacks           │
                             │  • Content moderation (18 categories) │
                             │  • PII detection & redaction          │
                             │  • Policy enforcement                 │
                             └───────────────────────────────────────┘
  ```

  ## Detection Capabilities

  ### Prompt Injection Detection
  Multi-layer detection using heuristics, semantic analysis, and LLM-based classification to identify and block prompt injection attacks, jailbreak attempts, and manipulation techniques.

  ### Content Moderation
  Real-time content moderation with 18 detection categories across 8 languages (EN, PL, FR, ES, IT, PT, TR, RU):

  | Category | Description |
  |----------|-------------|
  | Hate Speech | Content promoting hatred against protected groups |
  | Violence | Content depicting or promoting physical violence |
  | Sexual Content | Explicit or sexually suggestive material |
  | Self-Harm | Content promoting self-harm or suicide |
  | Toxicity | Hostile, aggressive, or inflammatory content |
  | Severe Toxicity | Extremely harmful or dangerous content |
  | Insult | Personal attacks and demeaning language |
  | Vulgar | Profane or obscene language |
  | Crime | Content promoting illegal activities |

  ### PII Detection & Redaction
  Automatic detection and redaction of Personal Identifiable Information:

  | PII Type | Examples |
  |----------|----------|
  | Email | jan.kowalski@firma.pl → `[EMAIL]` |
  | Phone | +48 123 456 789 → `[PHONE]` |
  | Credit Card | 4111-1111-1111-1111 → `[CREDIT_CARD]` |
  | National ID | PESEL, NIP, SSN → `[ID]` |
  | Names | Personal names → `[PERSON]` |
  | Addresses | Physical addresses → `[ADDRESS]` |

  ### Policy Enforcement
  Configurable rules engine for custom security policies with per-category actions (ALLOW, BLOCK, LOG).

  ## Features

  - **Dual Protection** - Guard both inputs (before LLM) and outputs (after LLM)
  - **Multi-Language Support** - Content moderation in 8 languages
  - **Automatic Text Processing** - Returns `guardedText` based on detection decision
  - **PII Redaction** - Automatic sanitization of sensitive data
  - **Fail-Safe Design** - Configurable fail-open behavior prevents workflow disruption
  - **Passthrough Fields** - Preserve session context and custom fields across nodes
  - **Full Response Details** - Optional access to all detection branch results

  ## Installation

  ### Community Nodes (Recommended)

  1. Open your n8n instance
  2. Navigate to **Settings** > **Community Nodes**
  3. Search for `n8n-nodes-vge`
  4. Click **Install**
  5. Restart n8n if prompted

  ### Manual Installation

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

  Then restart n8n.

  ### Docker Development

  Mount the package as a custom node for local development:

  ```yaml
  # docker-compose.yml
  services:
    n8n:
      image: n8nio/n8n:latest
      environment:
        - N8N_CUSTOM_EXTENSIONS=/home/node/custom-nodes
      extra_hosts:
        - "api.vigilguard:host-gateway"
      volumes:
        - n8n_data:/home/node/.n8n
        - /path/to/VGE-n8n:/home/node/custom-nodes/n8n-nodes-vge:ro
  ```

  ## Configuration

  ### Step 1: Add VGE API Credentials

  1. Go to **Settings** > **Credentials**
  2. Click **Add Credential**
  3. Search for **VGE API**
  4. Enter your credentials:

  | Field | Description | Example |
  |-------|-------------|---------|
  | API Key | Your VGE API key | `vg_live_abc123...` or `vg_test_xyz789...` |
  | Base URL | Your VGE instance URL | `https://api.vigilguard.yourdomain.com` |
  | Skip SSL Verification | Whether to skip SSL verification (self-signed only) | `false` |

  ### Step 2: Add VGE AIDR Node

  Drag the **VGE AIDR** node into your workflow and configure the parameters:

  | Parameter | Description | Default |
  |-----------|-------------|---------|
  | Stream Direction | `Input (Before LLM)` or `Output (After LLM)` | Input |
  | Text | The text to analyze | `{{ $json.chatInput }}` |
  | Original Prompt | For output mode - helps detect context manipulation | - |
  | Passthrough Fields | Comma-separated fields to preserve | `sessionId,action` |

  ### Advanced Options

  | Option | Default | Description |
  |--------|---------|-------------|
  | Timeout | 5000ms | Request timeout in milliseconds |
  | Fail Open | true | On API error, continue with original text |
  | Include Full Response | false | Include all detection branch details |
  | Custom Metadata | `{}` | Additional metadata for logging/audit |

  ## TLS and Certificates (Self-Hosted)

  For self-hosted VGE, use a valid TLS certificate and keep SSL verification enabled.

  **Recommended approach:**
  - Issue a certificate for your VGE hostname (FQDN) using a trusted CA or your own internal CA.
  - Ensure the certificate includes the correct SAN (hostname or IP).
  - Install your CA certificate in the system trust store on the host and inside containers that call VGE.
  - For Node.js, you can also set `NODE_EXTRA_CA_CERTS` to point to your CA file inside the container.

  **Emergency option only:**
  The **Skip SSL Verification** credential setting disables certificate checks. Use it only as a temporary workaround when you cannot install a proper certificate. It increases MITM risk and should not be used in production.

  ## Output Data

  The node outputs a single item with detection results:

  ```json
  {
    "sessionId": "abc-123",
    "action": "chat",
    "guardedText": "The processed text based on decision",
    "vgDecision": "ALLOWED",
    "vgScore": 15,
    "vgThreatLevel": "LOW",
    "vgCategories": ["PROMPT_INJECTION", "TOXICITY"],
    "vgRequestId": "550e8400-e29b-41d4-a716-446655440000",
    "vgLatencyMs": 45
  }
  ```

  ### Understanding guardedText

  The `guardedText` field contains different content depending on the detection decision:

  | Decision | guardedText Contains | Use Case |
  |----------|---------------------|----------|
  | `ALLOWED` | Original text (unchanged) | Safe to process |
  | `SANITIZED` | Redacted text with PII removed | Safe after cleanup |
  | `BLOCKED` | Block message explaining rejection | Do not process |

  ### Additional Fields by Decision

  **ALLOWED:**
  - Standard fields only

  **SANITIZED:**
  - `vgOriginalText` - Original text before sanitization
  - `vgRedactedText` - Text with PII markers (e.g., `[EMAIL]`, `[PHONE]`)
  - `vgSanitizedText` - Alternative sanitized version

  **BLOCKED:**
  - `vgBlockMessage` - Human-readable block reason
  - `vgOriginalText` - Original blocked text (for logging)

  ## Example Workflows

  ### Complete Input + Output Guard

  Protect both user input and AI response:

  ```
  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
  │ Chat Trigger │───>│  VGE AIDR    │───>│  AI Agent    │───>│  VGE AIDR    │───>│   Response   │
  │              │    │ (Input Mode) │    │  (OpenAI)    │    │(Output Mode) │    │              │
  └──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘
  ```

  **Input Guard Configuration:**
  - Stream Direction: `Input (Before LLM)`
  - Text: `{{ $json.chatInput }}`

  **Output Guard Configuration:**
  - Stream Direction: `Output (After LLM)`
  - Text: `{{ $json.output }}`
  - Original Prompt: `{{ $json.chatInput }}`

  ### Decision-Based Routing

  Route workflow based on detection decision using an IF node:

  ```
                      ┌──────────────┐
                      │  VGE AIDR    │
                      └──────────────┘
                             │
                             v
                      ┌──────────────┐
                      │     IF       │
                      │ vgDecision   │
                      └──────────────┘
                        │    │    │
              ALLOWED   │    │    │  BLOCKED
                        v    │    v
                 ┌──────┐    │  ┌──────────┐
                 │Proceed│   │  │  Error   │
                 │to LLM │    │  │ Response │
                 └──────┘    │  └──────────┘
                             │
                       SANITIZED
                             v
                      ┌──────────────┐
                      │ Log Warning  │
                      │ Then Proceed │
                      └──────────────┘
  ```

  **IF Node Conditions:**
  - Branch 1 (ALLOWED): `{{ $json.vgDecision === "ALLOWED" }}`
  - Branch 2 (SANITIZED): `{{ $json.vgDecision === "SANITIZED" }}`
  - Branch 3 (BLOCKED): `{{ $json.vgDecision === "BLOCKED" }}`

  ## Error Handling

  By default, the node uses **fail-open** behavior to prevent workflow disruption:

  - On API timeout or errors, items pass through with original text
  - Error details are included in the output for logging
  - Set **Fail Open** to `false` to throw errors instead

  **Fail-open output example:**

  ```json
  {
    "guardedText": "original user text",
    "vgError": "Connection timeout",
    "vgFailOpen": true,
    "vgDecision": "ALLOWED"
  }
  ```

  ## Development

  ### Build from Source

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

  # Install dependencies
  npm install

  # Build TypeScript
  npm run build

  # Copy icons to dist
  npm run copy:icons

  # Run linter
  npm run lint
  ```

  ### Project Structure

  ```
  n8n-nodes-vge/
  ├── nodes/
  │   └── VgeAidr/
  │       ├── VgeAidr.node.ts    # Main node implementation
  │       └── vge.svg            # Node icon
  ├── credentials/
  │   ├── VgeApi.credentials.ts  # API credentials
  │   └── vge.svg                # Credential icon
  ├── index.ts
  ├── package.json
  └── tsconfig.json
  ```

  ## Requirements

  | Requirement | Version |
  |-------------|---------|
  | Node.js | >= 18.10 |
  | n8n | >= 1.0.0 |
  | Vigil Guard Enterprise | Self-hosted instance required |

  ## Support

  - [Report Issues](https://github.com/vigilguard/n8n-nodes-vge/issues)
  - [Vigil Guard Documentation](https://docs.vigilguard.ai)
  - [API Reference](https://docs.vigilguard.ai/api)

  ## License

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

  ## Links

  - [Vigil Guard](https://vigilguard.ai)
  - [Python SDK](https://github.com/Vigil-Guard/vge-python-sdk)
  - [n8n Community Nodes Documentation](https://docs.n8n.io/integrations/community-nodes/)

ai_documentation_summary:
  {"purpose":"The VGE AIDR node acts as a security gateway for LLM workflows, providing real-time protection against prompt injection, harmful content, and PII leakage by analyzing both inputs and outputs.","capabilities":["Prompt injection detection using heuristics and semantic analysis","Content moderation across 18 categories in 8 languages","Automatic PII detection and redaction (email, phone, credit card, ID, names, addresses)","Policy enforcement with configurable rules (ALLOW, BLOCK, LOG)","Dual protection mode (input guard before LLM, output guard after LLM)","Fail-safe design with configurable fail-open behavior","Passthrough fields to preserve session context and custom data"],"authentication":"API Key required (VGE API Key) with Base URL configuration for self-hosted instance","commonUseCases":["Protecting AI agent inputs from malicious prompt injection and jailbreak attempts","Preventing PII leakage from AI agent outputs","Moderating content for hate speech, violence, toxicity, and policy violations","Enforcing custom security policies in enterprise LLM workflows"],"limitations":["Requires self-hosted VGE deployment; no public cloud API available","Requires manual installation or Docker configuration for self-hosted environments","Content moderation limited to 8 languages (EN, PL, FR, ES, IT, PT, TR, RU)","Requires n8n version >= 1.0.0 and Node.js >= 18.10"],"relatedNodes":["VGE API Credential node","AI Agent nodes (e.g., OpenAI)","Chat Trigger nodes"]}


node_type: n8n-nodes-scrapeless.scrapeless
package_name: n8n-nodes-scrapeless
display_name: Scrapeless Official
description: Official Scrapeless nodes for n8n
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: scrapeless_team
author_github_url: https://github.com/scrapeless-ai/
npm_package_name: n8n-nodes-scrapeless
npm_version: 0.4.1
community_fetched_at: 2026-03-25T21:19:40.593Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "scrapingApi",
      "options": [
        {
          "name": "Deep SerpApi",
          "value": "scrapingApi"
        },
        {
          "name": "Universal Scraping API",
          "value": "universalScrapingApi"
        },
        {
          "name": "Crawler",
          "value": "crawler"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "scrape",
      "options": [
        {
          "name": "Scrape",
          "value": "scrape",
          "action": "Scrape"
        },
        {
          "name": "Crawl",
          "value": "crawl",
          "action": "Crawl"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "crawler"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "webUnlocker",
      "options": [
        {
          "name": "Web Unlocker",
          "value": "webUnlocker",
          "action": "Web Unlocker"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "universalScrapingApi"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "googleSearch",
      "options": [
        {
          "name": "Google Search",
          "value": "googleSearch",
          "action": "Google Search"
        },
        {
          "name": "Google Trends",
          "value": "googleTrends",
          "action": "Google Trends"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "scrapingApi"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Scrape",
      "value": "scrape",
      "action": "Scrape"
    },
    {
      "name": "Crawl",
      "value": "crawl",
      "action": "Crawl"
    },
    {
      "name": "Web Unlocker",
      "value": "webUnlocker",
      "action": "Web Unlocker"
    },
    {
      "name": "Google Search",
      "value": "googleSearch",
      "action": "Google Search"
    },
    {
      "name": "Google Trends",
      "value": "googleTrends",
      "action": "Google Trends"
    }
  ]

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

  # n8n-node-scrapeless

  This repo contains Scrapeless's community node for n8n.

  ## Installation

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

  ## Credentials

  You must have a Scrapeless API key to use this node. You can register for a free account to get an API key here:

  https://app.scrapeless.com/passport/register

  Once registered, you can find your API key here:


  Then you'll need to create a credential in n8n for Scrapeless.

  ![Add Credentials](images/add-credentials.png)

  ## Usage

  Add the Scrapeless node to your workflow.

  ![Add Scrapeless to Workflow](images/workflow.png)

  Configure a basic query and hit "Test step" to try a search.

  ![Test a Crawler through Scrapeless](images/test-crawler.png)

  ## License

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

ai_documentation_summary:
  {"purpose":"Official Scrapeless community node for n8n that enables web scraping and crawling operations directly within n8n workflows.","capabilities":["Execute search queries via Scrapeless API","Perform web crawling operations","Retrieve scraped data from specified URLs","Integrate Scrapeless scraping functionality into n8n workflows"],"authentication":"API key required (Scrapeless API key)","commonUseCases":["Automated web data extraction","Competitor research via web scraping","Social media monitoring","Lead generation through URL crawling"],"limitations":["Requires separate Scrapeless account registration","API key must be configured in n8n credentials","Scraping limits and terms of service apply based on Scrapeless API plan"],"relatedNodes":["n8n HTTP Request","n8n Webhook","n8n Code Node"]}


node_type: @aotoki/n8n-nodes-line-messaging.lineMessaging
package_name: @aotoki/n8n-nodes-line-messaging
display_name: Line Messaging
description: Interact with the Line Messaging API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: elct9620
author_github_url: https://github.com/elct9620/n8n-nodes-line-messaging.git
npm_package_name: @aotoki/n8n-nodes-line-messaging
npm_version: 0.10.0
community_fetched_at: 2026-03-25T21:19:40.594Z
ai_summary_generated_at: 2026-03-25 22:47:20
properties_schema:
  [
    {
      "name": "operation",
      "type": "options",
      "default": "reply",
      "options": [
        {
          "name": "Get Profile",
          "value": "getProfile",
          "action": "Retrieve profile information of a user",
          "description": "Retrieve profile information of a user"
        },
        {
          "name": "Multicast",
          "value": "multicast",
          "action": "Send a message to multiple users",
          "description": "Send a message to multiple users"
        },
        {
          "name": "Reply",
          "value": "reply",
          "action": "Reply to a message using a reply token",
          "description": "Reply to a message using a reply token"
        },
        {
          "name": "Send",
          "value": "send",
          "action": "Send a message to a user",
          "description": "Send a message to a user"
        },
        {
          "name": "Show Loading Animation",
          "value": "showLoadingAnimation",
          "action": "Display a loading animation to a user",
          "description": "Display a loading animation to a user"
        }
      ],
      "displayName": "Operation",
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Get Profile",
      "value": "getProfile",
      "action": "Retrieve profile information of a user",
      "description": "Retrieve profile information of a user"
    },
    {
      "name": "Multicast",
      "value": "multicast",
      "action": "Send a message to multiple users",
      "description": "Send a message to multiple users"
    },
    {
      "name": "Reply",
      "value": "reply",
      "action": "Reply to a message using a reply token",
      "description": "Reply to a message using a reply token"
    },
    {
      "name": "Send",
      "value": "send",
      "action": "Send a message to a user",
      "description": "Send a message to a user"
    },
    {
      "name": "Show Loading Animation",
      "value": "showLoadingAnimation",
      "action": "Display a loading animation to a user",
      "description": "Display a loading animation to a user"
    }
  ]

npm_readme:
  # n8n-nodes-line-messaging

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

  LINE Messaging API enables developers to build chatbots and integrate messaging features into their services with the LINE platform, which is especially popular in Japan, Thailand, Taiwan, and other parts of Asia.

  [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)
  - [Development](#development)
  - [Resources](#resources)

  ## Installation

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

  ```bash
  npm install @aotoki/n8n-nodes-line-messaging
  ```

  ## Operations

  This node provides the following operations:

  ### Line Messaging Node

  - **Reply**: Reply to a user's message using the replyToken
  - **Send**: Proactively send messages to users using their User ID
  - **Multicast**: Send messages to multiple users simultaneously (up to 500 recipients)
  - **Get Profile**: Retrieve user profile information

  ### Line Messaging Data Node

  - **Get Content**: Retrieve content (images, videos, audio, files) from message IDs

  ### Line Messaging Trigger Node

  - **Webhook**: Receive events from LINE Messaging API

  ## Credentials

  You need to set up a LINE Bot channel in the [LINE Developers Console](https://developers.line.biz/console/) before using this node.

  1. Create a new provider and channel in the LINE Developers Console
  2. Enable the Messaging API option
  3. Get your Channel Secret and Channel Access Token from the Basic Settings and Messaging API tabs
  4. Use these credentials when setting up the node in n8n

  ## Compatibility

  - Requires n8n version 1.0.0 or later
  - Built without external dependencies to comply with n8n verified node requirements

  ## Usage

  ### Setting up a LINE Bot Webhook

  1. Create a workflow with the LINE Messaging Trigger node
  2. Configure the node to listen for the events you're interested in
  3. Copy the webhook URL from the trigger node
  4. Set this URL as your webhook URL in the LINE Developers Console
  5. Add a verification step in your workflow to validate the signature using the Channel Secret

  ### Responding to Messages

  1. When a message is received, your workflow will be triggered
  2. You can access the `replyToken` from the trigger output
  3. Use the LINE Messaging node with the "Reply" operation to send a response
  4. Set the reply token and add the messages you want to send

  ### Sending Proactive Messages

  1. Use the LINE Messaging node with the "Send" operation
  2. Provide the User ID of the recipient (format: U1234567890abcdef...)
  3. Add the messages you want to send
  4. This allows sending messages without waiting for user interaction

  ### Sending Bulk Messages (Multicast)

  1. Use the LINE Messaging node with the "Multicast" operation
  2. Provide comma-separated User IDs (e.g., "U1234..., U5678..., U9012...")
  3. Add the messages you want to send to all recipients
  4. Supports up to 500 recipients per operation for efficient bulk messaging

  ### Getting User Profiles

  1. Use the LINE Messaging node with the "Get Profile" operation
  2. Provide the User ID to retrieve profile information
  3. Get user's display name, profile picture, and status message

  ### Retrieving Media Content

  1. When a media message (image, video, audio, file) is received, you get a message ID
  2. Use the LINE Messaging Data node with the "Get Content" operation
  3. Provide the message ID to download the content as binary data
  4. The content can then be processed, saved, or sent to other services

  ### Example: Simple Echo Bot

  This example workflow receives messages and echoes them back to the user:

  1. Add a LINE Messaging Trigger node and configure it to listen for "message" events
  2. Add a LINE Messaging node configured to "Reply"
  3. Set the "Reply Token" field to use the replyToken from the trigger output: `{{$json.replyToken}}`
  4. Add a text message with the content: `You said: {{$json.message.text}}`

  ## Features

  - **Multiple Messaging Types**: Reply to messages, send to individuals, or multicast to groups
  - **Bulk Messaging**: Send to up to 500 recipients simultaneously with multicast operation
  - **Text Messages (V2)**: Send text messages with quote tokens and quick reply buttons
  - **Quick Replies**: Add postback and message action buttons to messages
  - **Quote Messages**: Reference previous messages in conversations
  - **User Profiles**: Retrieve user display names, pictures, and status messages
  - **Media Content**: Download images, videos, audio files, and documents
  - **Webhook Events**: Receive all LINE webhook events for comprehensive bot functionality
  - **No External Dependencies**: Built using only n8n-workflow types and Node.js built-ins

  ## Development

  To develop and test this node locally:

  ```bash
  # Clone the repository
  git clone git@github.com:elct9620/n8n-nodes-line-messaging.git
  cd n8n-nodes-line-messaging

  # Install dependencies
  pnpm install

  # Start the development server
  pnpm n8n-node dev
  ```

  The `pnpm n8n-node dev` command will start a local n8n instance with the node loaded for development and testing. This replaces the previous Docker-based development setup.

  For more development commands, see the available scripts:

  ```bash
  # Build the project
  pnpm build

  # Run linting
  pnpm lint

  # Format code
  pnpm format
  ```

  ## Resources

  - [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
  - [LINE Messaging API documentation](https://developers.line.biz/en/docs/messaging-api/)
  - [LINE Bot Designer](https://developers.line.biz/en/services/bot-designer/)

ai_documentation_summary:
  {"purpose":"This n8n community node enables interaction with the LINE Messaging API to build chatbots and integrate messaging features into workflows.","capabilities":["Reply to user messages using replyToken","Proactively send messages to individual users by User ID","Multicast messages to up to 500 recipients simultaneously","Retrieve user profile information (display name, profile picture, status message)","Retrieve media content (images, videos, audio, files) from message IDs","Receive webhook events from LINE Messaging API","Send text messages with quote tokens and quick reply buttons","Add postback and message action buttons to messages","Reference previous messages in conversations"],"authentication":"API key required (Channel Secret and Channel Access Token from LINE Developers Console)","commonUseCases":["Building simple echo bots that respond to user messages","Sending proactive notifications to customers","Broadcasting announcements to groups of users","Retrieving user profiles for personalized interactions","Downloading and processing media files sent by users"],"limitations":["Multicast is limited to 500 recipients per operation","Requires a LINE Bot channel to be set up in the LINE Developers Console","Requires n8n version 1.0.0 or later","Community node (not official n8n node)"],"relatedNodes":["LINE Messaging Trigger Node","LINE Messaging Data Node","n8n HTTP Request node","n8n Webhook node"]}


node_type: @klardaten/n8n-nodes-datevconnect.orderManagement
package_name: @klardaten/n8n-nodes-datevconnect
display_name: Klardaten DATEVconnect: Order Management
description: Interact with order management resources
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: johannespfeiffer
author_github_url: https://github.com/Klardaten/n8n-nodes-datevconnect.git
npm_package_name: @klardaten/n8n-nodes-datevconnect
npm_version: 1.0.12
community_fetched_at: 2026-03-25T21:19:40.594Z
ai_summary_generated_at: 2026-03-25 22:47:20
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "order",
      "options": [
        {
          "name": "Client Group",
          "value": "clientGroup"
        },
        {
          "name": "Cost Center",
          "value": "costCenter"
        },
        {
          "name": "Employee",
          "value": "employee"
        },
        {
          "name": "Fee",
          "value": "fee"
        },
        {
          "name": "Invoice",
          "value": "invoice"
        },
        {
          "name": "Order",
          "value": "order"
        },
        {
          "name": "Order Type",
          "value": "orderType"
        },
        {
          "name": "Self Client",
          "value": "selfClient"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create Expense Posting",
          "value": "createExpensePosting",
          "action": "Create expense postings",
          "description": "Create expense postings for a suborder"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get an order",
          "description": "Retrieve a specific order"
        },
        {
          "name": "Get Billing States (All)",
          "value": "getSubordersStateBillingAll",
          "action": "Get billing states",
          "description": "Retrieve billing state dates across suborders"
        },
        {
          "name": "Get Billing States (Order)",
          "value": "getSubordersStateBilling",
          "action": "Get billing states",
          "description": "Retrieve billing state dates for an order's suborders"
        },
        {
          "name": "Get Cost Items (All)",
          "value": "getCostItemsAll",
          "action": "Get cost items",
          "description": "Retrieve cost items across orders"
        },
        {
          "name": "Get Cost Items (Order)",
          "value": "getCostItemsForOrder",
          "action": "Get order cost items",
          "description": "Retrieve planned cost items for an order"
        },
        {
          "name": "Get Expense Postings (All)",
          "value": "getExpensePostingsAll",
          "action": "Get expense postings",
          "description": "Retrieve expense postings across orders"
        },
        {
          "name": "Get Expense Postings (Order)",
          "value": "getExpensePostingsForOrder",
          "action": "Get order expense postings",
          "description": "Retrieve expense postings for an order"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many orders",
          "description": "Retrieve orders"
        },
        {
          "name": "Get Monthly Values (All)",
          "value": "getMonthlyValuesAll",
          "action": "Get monthly values",
          "description": "Retrieve monthly values across orders"
        },
        {
          "name": "Get Monthly Values (Order)",
          "value": "getMonthlyValuesForOrder",
          "action": "Get order monthly values",
          "description": "Retrieve monthly values for a specific order"
        },
        {
          "name": "Get State Dates (All)",
          "value": "getStateWorkAll",
          "action": "Get order state dates",
          "description": "Retrieve state dates across orders"
        },
        {
          "name": "Get State Dates (Order)",
          "value": "getStateWork",
          "action": "Get order state dates",
          "description": "Retrieve state dates for an order"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update an order",
          "description": "Completely overwrite an order"
        },
        {
          "name": "Update Suborder",
          "value": "updateSuborder",
          "action": "Update a suborder",
          "description": "Completely overwrite a suborder"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "order"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many order types",
          "description": "Retrieve order types"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "orderType"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Get",
          "value": "get",
          "action": "Get a client group",
          "description": "Retrieve the group of a client"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "clientGroup"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many invoices",
          "description": "Retrieve invoices"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get an invoice",
          "description": "Retrieve a specific invoice"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "invoice"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getCapacities",
      "options": [
        {
          "name": "Get Capacities",
          "value": "getCapacities",
          "action": "Get employee capacities",
          "description": "Retrieve employee capacities"
        },
        {
          "name": "Get Charge Rates",
          "value": "getChargeRates",
          "action": "Get charge rates",
          "description": "Retrieve charge rates"
        },
        {
          "name": "Get Cost Rates",
          "value": "getCostRates",
          "action": "Get employee cost rates",
          "description": "Retrieve employees with cost rates"
        },
        {
          "name": "Get Employees With Group",
          "value": "getWithGroup",
          "action": "Get employees with group",
          "description": "Retrieve employees including their group"
        },
        {
          "name": "Get Qualifications",
          "value": "getQualifications",
          "action": "Get employee qualifications",
          "description": "Retrieve employees with qualifications"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "employee"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getFees",
      "options": [
        {
          "name": "Get Fees",
          "value": "getFees",
          "action": "Get fees",
          "description": "Retrieve fees"
        },
        {
          "name": "Get Fee Plans",
          "value": "getFeePlans",
          "action": "Get fee plans",
          "description": "Retrieve fee plans"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "fee"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many cost centers",
          "description": "Retrieve cost centers"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "costCenter"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many self clients",
          "description": "Retrieve self clients"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "selfClient"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Create Expense Posting",
      "value": "createExpensePosting",
      "action": "Create expense postings",
      "description": "Create expense postings for a suborder"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get an order",
      "description": "Retrieve a specific order"
    },
    {
      "name": "Get Billing States (All)",
      "value": "getSubordersStateBillingAll",
      "action": "Get billing states",
      "description": "Retrieve billing state dates across suborders"
    },
    {
      "name": "Get Billing States (Order)",
      "value": "getSubordersStateBilling",
      "action": "Get billing states",
      "description": "Retrieve billing state dates for an order's suborders"
    },
    {
      "name": "Get Cost Items (All)",
      "value": "getCostItemsAll",
      "action": "Get cost items",
      "description": "Retrieve cost items across orders"
    },
    {
      "name": "Get Cost Items (Order)",
      "value": "getCostItemsForOrder",
      "action": "Get order cost items",
      "description": "Retrieve planned cost items for an order"
    },
    {
      "name": "Get Expense Postings (All)",
      "value": "getExpensePostingsAll",
      "action": "Get expense postings",
      "description": "Retrieve expense postings across orders"
    },
    {
      "name": "Get Expense Postings (Order)",
      "value": "getExpensePostingsForOrder",
      "action": "Get order expense postings",
      "description": "Retrieve expense postings for an order"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many orders",
      "description": "Retrieve orders"
    },
    {
      "name": "Get Monthly Values (All)",
      "value": "getMonthlyValuesAll",
      "action": "Get monthly values",
      "description": "Retrieve monthly values across orders"
    },
    {
      "name": "Get Monthly Values (Order)",
      "value": "getMonthlyValuesForOrder",
      "action": "Get order monthly values",
      "description": "Retrieve monthly values for a specific order"
    },
    {
      "name": "Get State Dates (All)",
      "value": "getStateWorkAll",
      "action": "Get order state dates",
      "description": "Retrieve state dates across orders"
    },
    {
      "name": "Get State Dates (Order)",
      "value": "getStateWork",
      "action": "Get order state dates",
      "description": "Retrieve state dates for an order"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update an order",
      "description": "Completely overwrite an order"
    },
    {
      "name": "Update Suborder",
      "value": "updateSuborder",
      "action": "Update a suborder",
      "description": "Completely overwrite a suborder"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many order types",
      "description": "Retrieve order types"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a client group",
      "description": "Retrieve the group of a client"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many invoices",
      "description": "Retrieve invoices"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get an invoice",
      "description": "Retrieve a specific invoice"
    },
    {
      "name": "Get Capacities",
      "value": "getCapacities",
      "action": "Get employee capacities",
      "description": "Retrieve employee capacities"
    },
    {
      "name": "Get Charge Rates",
      "value": "getChargeRates",
      "action": "Get charge rates",
      "description": "Retrieve charge rates"
    },
    {
      "name": "Get Cost Rates",
      "value": "getCostRates",
      "action": "Get employee cost rates",
      "description": "Retrieve employees with cost rates"
    },
    {
      "name": "Get Employees With Group",
      "value": "getWithGroup",
      "action": "Get employees with group",
      "description": "Retrieve employees including their group"
    },
    {
      "name": "Get Qualifications",
      "value": "getQualifications",
      "action": "Get employee qualifications",
      "description": "Retrieve employees with qualifications"
    },
    {
      "name": "Get Fees",
      "value": "getFees",
      "action": "Get fees",
      "description": "Retrieve fees"
    },
    {
      "name": "Get Fee Plans",
      "value": "getFeePlans",
      "action": "Get fee plans",
      "description": "Retrieve fee plans"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many cost centers",
      "description": "Retrieve cost centers"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many self clients",
      "description": "Retrieve self clients"
    }
  ]

npm_readme:
[cell repeats an earlier one in this file: 3558 characters]

ai_documentation_summary:
  {"purpose":"This n8n node enables interaction with DATEVconnect APIs to read and manage orders, suborders, invoices, fees, and related employee data.","capabilities":["Read orders","Manage suborders","Manage invoices","Manage fees","Access related employee data","Filter orders based on configured parameters"],"authentication":"Password-based (email, password, clientInstanceId)","commonUseCases":["Automating invoice processing workflows","Syncing order data between DATEV and external systems","Automated fee management and reporting"],"limitations":["Requires a Klardaten DATEVconnect Gateway setup","Requires a Klardaten account and valid DATEV instance ID","Authentication requires email and password (no API key mentioned)"],"relatedNodes":["Accounting","MasterData","Document Management","Identity & Access Management"]}


node_type: n8n-nodes-jaasai.jaasai
package_name: n8n-nodes-jaasai
display_name: JaaS AI
description: Evaluate AI Agents with JaaS
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: hljaasai
author_github_url: https://github.com/jaas-a1/
npm_package_name: n8n-nodes-jaasai
npm_version: 0.3.2
community_fetched_at: 2026-03-25T21:19:40.595Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "evaluate",
      "options": [
        {
          "name": "Evaluate",
          "value": "evaluate",
          "description": "Evaluate AI agents and outputs"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "evaluate",
      "options": [
        {
          "name": "Evaluate",
          "value": "evaluate",
          "action": "Evaluate AI agent responses",
          "description": "Evaluate AI agent responses"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "evaluate"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Evaluate",
      "value": "evaluate",
      "action": "Evaluate AI agent responses",
      "description": "Evaluate AI agent responses"
    }
  ]

npm_readme:
  ## JAAS AI 

  JaaS AI is a turnkey evaluationAI service that scores, analyzes, validates and monitors any AI-generated text in seconds. Plug JaaS AI into your workflow to ensure accuracy, consistency, and quality—without needing extra reviewers. The system evaluates responses based on customizable criteria and provides detailed scoring and reasoning.
  JaaS AI has been proven to detect Hallucination in AI Agents and chat bots.

  *Put your workflows and chat bots under the microscope and understand when they start to derail, hallucinate or degrade their accuracy.*

  ### Requirements

  To configure you n8n node, you need a JaaS API key. For that you have to register for the service at jaas-ai.net.You will also need a LLM Provider API key.


  ### Understanding the evaluation process

  The evaluation process is very simple. It compares an *answer* to a specific *question* to a *ground truth answer* in a *context* given. A *score* and *reasoning* is provided based on a *criteria* or criterion given to evaluate.

  #### Inputs to the evaluation
  - . **question:** The question that you want to evaluate. Example: *Which is capital of France?*

  - . **answer:** This is the answer that is going to be evaluated. Example: *Paris*.

  - . **ground_truth_answer**: This is the source of truth. It is used as rubric to for the evaluation process to score the criterion. Example: *The capital of France is Paris.*

  - . **context:** Additional information that is used to evaluate. The context becomes more relevant in non-direct comparison criterion. As an example in cases of **hallucination:** The context is key to understand if the evaluated answer related to the conversation that has been happening. Example: *Paris is the capital and largest city of France, with an estimated population of 2,048,472 in January 2025 in an area of more than 105 km2 (41 sq mi).*

  - . **criteria:** The criteria or criterion is the base of the scoring process. The evaluation tool scores the *answer* based on the criteria given. Example: *Accuracy, Hallucination.*

  - . **type:** This is the type of evaluation. There are 4 types: *Simple (S), Conversational Simple (D),
    -  The main differences are: *conversational or non-conversations*: the *conversational* types detect chit-chat conversations that are not evaluated. This is key when evaluating or monitoring *chat bots* or transcripts of *voice conversations*.  

  - . **cohort:** The *cohort* helps in the monitoring and analysis process. It does not intervene in the evaluation itself, while indicates a grouping of evaluation. As an example, if the tool is evaluating conversations from a chat bot, each conversation can have its own cohort for better tracking and monitoring.


  #### Example of a Information for an evaluation
  ```json

  From Input to the Node:	
    "question": "What is the capital of France?",
    "answer": "Paris.",
    "ground_truth_answer": "Paris is the capital of France.",
    "context": "",

  From Configuration:
  	"evaluation_criteria": ["Accuracy", "Hallucination"],
    "type": "S",
    "cohort":"ChatBot1"

  ```
  After the evaluation is completed almost in real time, the system returns the score for each of the metrics and the reasoning behind.


  ### Using the n8n JaaS Module

  ***Configuration:*** The module requires the corresponding configuration:
  	- JaaS API Key: linked to you account in jaas-ai.com
  	- Criteria: you can enter up to 3 criteria for each node. The criteria should be available in teh Criteria Database in jaas-ai.com.
  	- Type of Evaluation: S - Simple, or C- Conversational
  	- Cohort: to groupe the evaluations in groups for better tracking
    
  ***Input:*** You should provide the no de the fields needed for the evaluation:
  	- question: question to answer
  	- answer: answer that has to be evaluated
  	- ground_truth_answer: source of truth for the answer to be evaluated
  	- context: additional information to help the evaluation process
  The inputs should be in Json Format.

  Obtain the results and monitor what is going on with your tools!

  Look for more information and examples in our website!!

  support: support@jaas-ai.com
  website: jaas-ai.net

ai_documentation_summary:
  {"purpose":"Evaluates AI-generated text by scoring it against ground truth answers and customizable criteria to detect hallucinations and ensure quality.","capabilities":["Scoring AI responses based on accuracy, hallucination, and other criteria","Analyzing conversational vs. non-conversational inputs","Grouping evaluations into cohorts for tracking","Providing detailed reasoning for evaluation scores","Supporting Simple (S) and Conversational (C) evaluation types","Accepting context to improve evaluation accuracy"],"authentication":"JaaS API key required (registered at jaas-ai.net) and LLM Provider API key required","commonUseCases":["Monitoring chatbot accuracy and hallucination rates","Validating AI agent responses against known ground truth","Quality assurance for AI-generated content","Tracking performance degradation in AI systems"],"limitations":["Only up to 3 criteria can be configured per node","Criteria must be selected from the JaaS Criteria Database","Requires external API keys for both JaaS and LLM provider services"],"relatedNodes":["n8n-nodes-jaaai.jaasai"]}


node_type: n8n-nodes-scraperapi-official.scraperApi
package_name: n8n-nodes-scraperapi-official
display_name: ScraperAPI
description: Official ScraperAPI nodes for n8n
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: scraperapi
author_github_url: https://github.com/scraperapi/
npm_package_name: n8n-nodes-scraperapi-official
npm_version: 1.2.0
community_fetched_at: 2026-03-25T21:19:40.596Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "api",
      "options": [
        {
          "name": "API",
          "value": "api"
        },
        {
          "name": "Crawler",
          "value": "crawler"
        },
        {
          "name": "Structured Data Endpoint",
          "value": "sde"
        }
      ],
      "required": true,
      "description": "Choose your ScraperAPI resource",
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "apiRequest",
      "options": [
        {
          "name": "Send a Request",
          "value": "apiRequest",
          "action": "Send a request to the API"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "api"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "amazonProduct",
      "options": [
        {
          "name": "Offers",
          "value": "amazonOffers",
          "action": "Get product offers"
        },
        {
          "name": "Product",
          "value": "amazonProduct",
          "action": "Get product details"
        },
        {
          "name": "Search",
          "value": "amazonSearch",
          "action": "Search products"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "sde"
          ],
          "sdePlatform": [
            "amazon"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "ebaySearch",
      "options": [
        {
          "name": "Product",
          "value": "ebayProduct",
          "action": "Get product details"
        },
        {
          "name": "Search",
          "value": "ebaySearch",
          "action": "Search products"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "sde"
          ],
          "sdePlatform": [
            "ebay"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "googleSearch",
      "options": [
        {
          "name": "Jobs",
          "value": "googleJobs",
          "action": "Get jobs results"
        },
        {
          "name": "Maps Search",
          "value": "googleMapsSearch",
          "action": "Search maps"
        },
        {
          "name": "News",
          "value": "googleNews",
          "action": "Get news results"
        },
        {
          "name": "Search",
          "value": "googleSearch",
          "action": "Get search results"
        },
        {
          "name": "Shopping",
          "value": "googleShopping",
          "action": "Get shopping results"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "sde"
          ],
          "sdePlatform": [
            "google"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "redfinForSale",
      "options": [
        {
          "name": "Agent",
          "value": "redfinAgent",
          "action": "Get agent details"
        },
        {
          "name": "For Rent",
          "value": "redfinForRent",
          "action": "Get rental listings"
        },
        {
          "name": "For Sale",
          "value": "redfinForSale",
          "action": "Get sale listings"
        },
        {
          "name": "Search",
          "value": "redfinSearch",
          "action": "Search listings"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "sde"
          ],
          "sdePlatform": [
            "redfin"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "walmartSearch",
      "options": [
        {
          "name": "Category",
          "value": "walmartCategory",
          "action": "Browse category"
        },
        {
          "name": "Product",
          "value": "walmartProduct",
          "action": "Get product details"
        },
        {
          "name": "Review",
          "value": "walmartReview",
          "action": "Get product reviews"
        },
        {
          "name": "Search",
          "value": "walmartSearch",
          "action": "Search products"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "sde"
          ],
          "sdePlatform": [
            "walmart"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "crawlerJobCreate",
      "options": [
        {
          "name": "Initiate a Crawler Job",
          "value": "crawlerJobCreate",
          "action": "Initiate a crawler job"
        },
        {
          "name": "Get a Job Status",
          "value": "crawlerJobGet",
          "action": "Get a job status"
        },
        {
          "name": "Cancel a Crawler Job",
          "value": "crawlerJobDelete",
          "action": "Cancel a crawler job"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "crawler"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Send a Request",
      "value": "apiRequest",
      "action": "Send a request to the API"
    },
    {
      "name": "Offers",
      "value": "amazonOffers",
      "action": "Get product offers"
    },
    {
      "name": "Product",
      "value": "amazonProduct",
      "action": "Get product details"
    },
    {
      "name": "Search",
      "value": "amazonSearch",
      "action": "Search products"
    },
    {
      "name": "Product",
      "value": "ebayProduct",
      "action": "Get product details"
    },
    {
      "name": "Search",
      "value": "ebaySearch",
      "action": "Search products"
    },
    {
      "name": "Jobs",
      "value": "googleJobs",
      "action": "Get jobs results"
    },
    {
      "name": "Maps Search",
      "value": "googleMapsSearch",
      "action": "Search maps"
    },
    {
      "name": "News",
      "value": "googleNews",
      "action": "Get news results"
    },
    {
      "name": "Search",
      "value": "googleSearch",
      "action": "Get search results"
    },
    {
      "name": "Shopping",
      "value": "googleShopping",
      "action": "Get shopping results"
    },
    {
      "name": "Agent",
      "value": "redfinAgent",
      "action": "Get agent details"
    },
    {
      "name": "For Rent",
      "value": "redfinForRent",
      "action": "Get rental listings"
    },
    {
      "name": "For Sale",
      "value": "redfinForSale",
      "action": "Get sale listings"
    },
    {
      "name": "Search",
      "value": "redfinSearch",
      "action": "Search listings"
    },
    {
      "name": "Category",
      "value": "walmartCategory",
      "action": "Browse category"
    },
    {
      "name": "Product",
      "value": "walmartProduct",
      "action": "Get product details"
    },
    {
      "name": "Review",
      "value": "walmartReview",
      "action": "Get product reviews"
    },
    {
      "name": "Search",
      "value": "walmartSearch",
      "action": "Search products"
    },
    {
      "name": "Initiate a Crawler Job",
      "value": "crawlerJobCreate",
      "action": "Initiate a crawler job"
    },
    {
      "name": "Get a Job Status",
      "value": "crawlerJobGet",
      "action": "Get a job status"
    },
    {
      "name": "Cancel a Crawler Job",
      "value": "crawlerJobDelete",
      "action": "Cancel a crawler job"
    }
  ]

npm_readme:
  # ScraperAPI Official N8N Node

  This is an n8n community node that lets you use **ScraperAPI** in your n8n workflows.

  **ScraperAPI** is a solution to help you unlock and scrape any website, no matter the scale or difficulty. It handles proxies, browsers, and CAPTCHAs so you can focus on extracting the data you need.

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

  ## Table of Contents

  - [Installation](#installation)
  - [Operations](#operations)
  - [Credentials](#credentials)
  - [Usage](#usage)
  - [Resources](#resources)
  - [Parameters](#parameters)
  - [Documentation](#documentation)
  - [Version History](#version-history)
  - [More ScraperAPI Integrations](#more-scraperapi-integrations)

  ## Installation

  1. In n8n, go to the **Nodes** panel.
  2. Search for **ScraperAPI**.
  3. Select **Install**: n8n installs the node and it becomes available in your workflows.

  ## Operations

    - API
      - Send a Request
    - Structured Data Endpoints (SDEs)
      - Amazon
        - Product
        - Search
        - Offers
        - Review
        - Prices
      - Google
        - Search
        - Jobs
        - News
        - Shopping
        - Maps Search
      - Ebay
        - Search
        - Product
      - Walmart
        - Search
        - Category
        - Product
        - Review
      - Redfin
        - For Sale
        - For Rent
        - Search
        - Agent
    - Crawler
      - Initiate a crawler job
      - Get a job status
      - Cancel a crawler job

  ## Credentials

  ### Getting Your API Key

  1. Sign up for a ScraperAPI account at [ScraperAPI Dashboard](https://dashboard.scraperapi.com/signup)
  2. Once logged in, navigate to your dashboard
  3. Copy your API key from the dashboard

  ### Configuring Credentials in n8n

  1. In your n8n workflow, add a **ScraperAPI** node
  2. Click on the **Credential to connect with** field
  3. Click **Create New Credential**
  4. Enter your API key
  5. Click **Save**

  The credentials will be automatically tested to ensure they work correctly.

  For more information, see the [ScraperAPI API Key Documentation](https://docs.scraperapi.com/dashboard-and-billing/api-key).

  ## Usage

  The ScraperAPI node supports three resources:

  - **API**: Scrape a single URL with a GET request. The node handles proxies, browser automation, and CAPTCHA solving.
  - **Structured Data Endpoint**: Extract structured data from popular websites (Amazon, Google, eBay, Walmart, Redfin) using purpose-built endpoints that return clean, parsed JSON.
  - **Crawler**: Run multi-page crawler jobs that follow links from a start URL and stream results to a webhook.

  1. Add a **ScraperAPI** node to your workflow
  2. Select the ScraperAPI resource, for example the **API**
  3. Enter the required parameters, for example the **URL** you want to scrape
  4. Configure any optional parameter you need
  5. Execute the workflow

  The node returns a JSON object with the following structure:

  ```json
  {
    "resource": "api",
    "response": {
      "body": "...",
      "headers": {...},
      "statusCode": 200,
      "statusMessage": "OK"
    }
  }
  ```

  ## Resources

  ### API

  The **API** resource allows you to scrape any website using ScraperAPI's endpoint. It supports JavaScript rendering, geo-targeting, device-specific user agents, premium proxies, automatic parsing, and multiple output formats.

  <details>
  <summary><strong>Send a Request</strong></summary>

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `url` | string | Target URL to scrape (e.g., `https://example.com`) | Yes |
  | `render` | boolean | Enable JavaScript rendering for dynamic content (default: `false`) | No |
  | `countryCode` | string | Two-letter ISO country code (e.g., `US`, `GB`, `DE`) for geo-targeted scraping | No |
  | `desktopDevice` | boolean | Scrape the page as a desktop device (incompatible with `mobileDevice`) | No |
  | `mobileDevice` | boolean | Scrape the page as a mobile device (incompatible with `desktopDevice`) | No |
  | `outputFormat` | string | Output format: `'markdown'`, `'text'`, `'csv'`, or `'json'`. CSV and JSON are only available for autoparse websites. Default: HTML | No |
  | `autoparse` | boolean | Enable automatic parsing for select websites (default: `false`) | No |
  | `premium` | boolean | Use premium residential/mobile proxies for higher success rates (incompatible with `ultraPremium`) | No |
  | `ultraPremium` | boolean | Activate advanced bypass mechanisms for the most difficult websites (incompatible with `premium`) | No |

  </details>

  ### Crawler

  The **Crawler** resource uses the [ScraperAPI Crawler API](https://docs.scraperapi.com/scraperapi-crawler-v2.0/crawler-api/job-lifecycle) to run crawling jobs to discover and scrape multiple pages, streaming results to a webhook you provide.

  <details>
  <summary><strong>Initiate a Crawler Job</strong></summary>

  Create and start a new crawler job. You receive a `jobId` to track or cancel the job.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `startUrl` | string | The URL where crawling begins (depth 0) | Yes |
  | `maxDepth` | number | Maximum depth level (start URL = 0). Either `maxDepth` or `crawlBudget` must be set | Yes* |
  | `crawlBudget` | number | Maximum ScraperAPI credits the job may consume. Either `maxDepth` or `crawlBudget` must be set | Yes* |
  | `urlRegexpInclude` | string | Regex to decide which links to crawl from each page. Use `.*` to allow all pages on the site | Yes |
  | `callbackUrl` | string | Webhook URL where ScraperAPI sends results. Both successful and failed scrape attempts are streamed here; when the job finishes, a job summary is also sent | Yes |
  | `urlRegexpExclude` | string | Regex to skip URLs (e.g., `.*/product/.*`). Leave empty to crawl all URLs that pass the include regex | No |
  | `apiParameters` | object | Per-page scrape settings (country code, device, render, premium, output format, etc.). See the API resource for supported options | No |
  | `enabled` | boolean | When `true`, the crawler runs according to the schedule. When `false`, only the configuration is created (default: `true`) | No |
  | `scheduleName` | string | Name for the crawler (e.g., for the dashboard) | No |
  | `scheduleInterval` | string | When the crawler runs: `'once'`, `'hourly'`, `'daily'`, `'weekly'`, or `'monthly'` | No |

  </details>

  <details>
  <summary><strong>Get a Job Status</strong></summary>

  Check the current state of a crawler job.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `jobId` | string | The crawler job ID returned when you initiated the job | Yes |

  </details>

  <details>
  <summary><strong>Cancel a Crawler Job</strong></summary>

  Stop a running crawler job.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `jobId` | string | The crawler job ID returned when you initiated the job | Yes |

  </details>

  ### Structured Data Endpoints (SDEs)

  The **Structured Data Endpoints** resource provides purpose-built endpoints for extracting structured data from popular platforms. Each endpoint returns clean, parsed JSON without requiring manual parsing. All SDE endpoints are accessed via `https://api.scraperapi.com/structured/{platform}/{endpoint}`.

  <details>
  <summary><strong>Amazon</strong></summary>

  #### Amazon Product

  Extract detailed product information from Amazon.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `asin` | string | The Amazon Standard Identification Number (e.g., `B08N5WRWNW`) | Yes |
  | `tld` | string | Amazon top-level domain (e.g., `com`, `co.uk`, `de`) | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `includeHtml` | boolean | Whether to include raw HTML in the response | No |
  | `language` | string | Language code for the response | No |

  #### Amazon Search

  Search for products on Amazon.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `query` | string | Search query (e.g., `laptop`) | Yes |
  | `tld` | string | Amazon top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `page` | number | Page number of search results | No |
  | `sort` | string | Sort parameter | No |
  | `category` | string | Department/category filter | No |
  | `language` | string | Language code for the response | No |

  #### Amazon Offers

  Get all offers (sellers) for a specific Amazon product.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `asin` | string | The Amazon Standard Identification Number | Yes |
  | `tld` | string | Amazon top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `condition` | string | Filter by item condition | No |
  | `filterNew` | boolean | Filter for new items | No |
  | `filterUsedGood` | boolean | Filter for used - good condition | No |
  | `filterUsedLikeNew` | boolean | Filter for used - like new condition | No |
  | `filterUsedVeryGood` | boolean | Filter for used - very good condition | No |
  | `filterUsedAcceptable` | boolean | Filter for used - acceptable condition | No |

  #### Amazon Review

  Get customer reviews for an Amazon product.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `asin` | string | The Amazon Standard Identification Number | Yes |
  | `tld` | string | Amazon top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `filterByStar` | string | Filter reviews by star rating | No |
  | `reviewerType` | string | Filter by reviewer type | No |
  | `pageNumber` | number | Page number of reviews | No |
  | `sortBy` | string | Sort order for reviews | No |

  #### Amazon Prices

  Get pricing information for multiple Amazon products at once.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `asins` | string | Comma-separated list of ASINs (max 8) | Yes |
  | `tld` | string | Amazon top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |

  </details>

  <details>
  <summary><strong>Google</strong></summary>

  #### Google Search

  Get Google search results.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `query` | string | Search query | Yes |
  | `tld` | string | Google top-level domain (e.g., `com`, `co.uk`) | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `dateRangeStart` | string | Start date for date-range filtering | No |
  | `dateRangeEnd` | string | End date for date-range filtering | No |
  | `timePeriod` | string | Predefined time period filter | No |
  | `includeHtml` | boolean | Whether to include raw HTML in the response | No |

  #### Google Jobs

  Get Google Jobs search results.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `query` | string | Job search query | Yes |
  | `tld` | string | Google top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |

  #### Google News

  Get Google News results.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `query` | string | News search query | Yes |
  | `tld` | string | Google top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `dateRangeStart` | string | Start date for date-range filtering | No |
  | `dateRangeEnd` | string | End date for date-range filtering | No |
  | `timePeriod` | string | Predefined time period filter | No |

  #### Google Shopping

  Get Google Shopping results.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `query` | string | Shopping search query | Yes |
  | `tld` | string | Google top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `includeHtml` | boolean | Whether to include raw HTML in the response | No |

  #### Google Maps Search

  Search for businesses and places on Google Maps.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `query` | string | Search query (e.g., `pizza near Times Square`) | Yes |
  | `tld` | string | Google top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `latitude` | string | Latitude for location-based search | No |
  | `longitude` | string | Longitude for location-based search | No |
  | `zoom` | number | Zoom level for the map search | No |
  | `includeHtml` | boolean | Whether to include raw HTML in the response | No |

  </details>

  <details>
  <summary><strong>eBay</strong></summary>

  #### eBay Search

  Search for items on eBay.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `query` | string | Search query | Yes |
  | `tld` | string | eBay top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `page` | number | Page number of search results | No |
  | `itemsPerPage` | number | Number of items per page | No |
  | `sellerId` | string | Filter by specific seller | No |
  | `condition` | string | Filter by item condition | No |
  | `buyingFormat` | string | Filter by buying format: `'buy_it_now'`, `'auction'`, `'accepts_offers'` | No |
  | `showOnly` | string | Additional filters: `'returns_accepted'`, `'authorized_seller'`, `'completed_items'`, `'sold_items'`, `'sale_items'`, `'listed_as_lots'`, `'search_in_description'`, `'benefits_charity'`, `'authenticity_guarantee'` | No |
  | `sortBy` | string | Sort order: `'ending_soonest'`, `'newly_listed'`, `'price_lowest'`, `'price_highest'`, `'distance_nearest'`, `'best_match'` | No |

  #### eBay Product

  Get detailed product information from eBay.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `productId` | string | The eBay product/item ID | Yes |
  | `tld` | string | eBay top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |

  </details>

  <details>
  <summary><strong>Walmart</strong></summary>

  #### Walmart Search

  Search for products on Walmart.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `query` | string | Search query | Yes |
  | `tld` | string | Walmart top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `page` | number | Page number of search results | No |

  #### Walmart Category

  Browse products by Walmart category.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `category` | string | Walmart category ID | Yes |
  | `tld` | string | Walmart top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `page` | number | Page number of results | No |

  #### Walmart Product

  Get detailed product information from Walmart.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `productId` | string | The Walmart product ID | Yes |
  | `tld` | string | Walmart top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |

  #### Walmart Review

  Get customer reviews for a Walmart product.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `productId` | string | The Walmart product ID | Yes |
  | `tld` | string | Walmart top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `page` | number | Page number of reviews | No |
  | `sort` | string | Sort order for reviews | No |
  | `ratings` | string | Filter by rating | No |
  | `verifiedPurchase` | boolean | Filter for verified purchases only | No |

  </details>

  <details>
  <summary><strong>Redfin</strong></summary>

  #### Redfin For Sale

  Get property listings for sale on Redfin.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `url` | string | Full Redfin URL for the search | Yes |
  | `tld` | string | Redfin top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `raw` | boolean | Whether to return raw data | No |

  #### Redfin For Rent

  Get rental property listings on Redfin.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `url` | string | Full Redfin URL for the search | Yes |
  | `tld` | string | Redfin top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |
  | `raw` | boolean | Whether to return raw data | No |

  #### Redfin Search

  Search for properties on Redfin.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `url` | string | Full Redfin URL for the search | Yes |
  | `tld` | string | Redfin top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |

  #### Redfin Agent

  Get real estate agent information from Redfin.

  | Parameter | Type | Description | Required |
  |-----------|------|-------------|----------|
  | `url` | string | Full Redfin agent URL | Yes |
  | `tld` | string | Redfin top-level domain | No |
  | `countryCode` | string | Two-letter country code for geo-targeting | No |

  </details>

  ## Documentation

  - [ScraperAPI Official Documentation](https://docs.scraperapi.com)

  ## Version History

  - **0.1.1**: Initial release with API resource support
  - **0.1.2**: Usage added to Documentation
  - **0.2.0**: Replace device_type options field with desktopDevice and mobileDevice boolean fields to support AI model auto-definition.
  - **1.0.0**: output_format and autoparse parameters supported.
  - **1.1.0**: Crawler resource: initiate crawler jobs, get job status, and cancel jobs.

  ## More ScraperAPI Integrations

  ### MCP Server

  ScraperAPI also provides an **MCP (Model Context Protocol) server** that enables AI models and agents to scrape websites.

  #### Hosted MCP Server

  ScraperAPI offers a hosted MCP server that you can use with n8n's [MCP Client Tool](https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolmcp/).

  **Configuration Steps:**

  1. Add an **MCP Client Tool** node to your workflow
  2. Configure the following settings:
     - **Endpoint**: `https://mcp.scraperapi.com/mcp`
     - **Server Transport**: `HTTP Streamable`
     - **Authentication**: `Bearer Auth`
     - **Credential for Bearer Auth**: Enter your ScraperAPI API key as a Bearer Token.
     - **Tools to include**: `All` (or select specific tools as needed)

  #### Self-Hosted MCP Server

  If you prefer to self-host the MCP server, you can find the implementation and setup instructions in the [scraperapi-mcp repository](https://github.com/scraperapi/scraperapi-mcp).

ai_documentation_summary:
  {"purpose":"This n8n community node enables scraping any website by handling proxies, browser automation, and CAPTCHAs, or extracting structured data from popular e-commerce and business sites.","capabilities":["Scrape single URLs with GET requests","Handle JavaScript rendering for dynamic content","Geo-target scraping by country","Simulate desktop or mobile device user agents","Extract structured data from Amazon, Google, eBay, Walmart, and Redfin","Run multi-page crawler jobs with webhook streaming","Support multiple output formats (HTML, markdown, text, CSV, JSON)","Use premium proxies and advanced bypass mechanisms"],"authentication":"API key required (entered in node credentials)","commonUseCases":["Extract product data from Amazon or eBay","Monitor competitor pricing","Scrape job listings from Google or Indeed","Gather real estate listings from Redfin","Automate data collection from dynamic websites"],"limitations":["Requires a paid ScraperAPI account and API key","Desktop and mobile device options are mutually exclusive","Premium and ultraPremium proxy options are mutually exclusive","CSV and JSON output formats only available for autoparse websites","Crawler jobs require a webhook endpoint for result streaming"],"relatedNodes":["HTTP Request","Webhook","Code","JSON","Split In Batches"]}


node_type: n8n-nodes-gainium.Gainium
package_name: n8n-nodes-gainium
display_name: Gainium
description: Operates with official Gainium API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: aressanch
author_github_url: https://github.com/Gainium/
npm_package_name: n8n-nodes-gainium
npm_version: 0.3.6
community_fetched_at: 2026-03-25T21:19:40.596Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "general",
      "options": [
        {
          "name": "General",
          "value": "general"
        },
        {
          "name": "Bots",
          "value": "bots"
        },
        {
          "name": "Deals",
          "value": "deals"
        },
        {
          "name": "User",
          "value": "user"
        }
      ],
      "required": true,
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get_user_grid_bots",
      "options": [
        {
          "name": "Get User Grid Bots",
          "value": "get_user_grid_bots",
          "action": "Get User Grid Bots",
          "description": "Get User Grid Bots"
        },
        {
          "name": "Get User Combo Bots",
          "value": "get_user_combo_bots",
          "action": "Get User Combo Bots",
          "description": "Get User Combo Bots"
        },
        {
          "name": "Get User DCA Bots",
          "value": "get_user_dca_bots",
          "action": "Get User DCA Bots",
          "description": "Get User DCA Bots"
        },
        {
          "name": "Update DCA Bot Settings",
          "value": "update_dca_bot_settings",
          "action": "Update DCA Bot Settings",
          "description": "Update DCA Bot Settings"
        },
        {
          "name": "Update Combo Bot Settings",
          "value": "update_combo_bot_settings",
          "action": "Update Combo Bot Settings",
          "description": "Update Combo Bot Settings"
        },
        {
          "name": "Change Bot Pairs",
          "value": "change_bot_pairs",
          "action": "Change Bot Pairs",
          "description": "Change Bot Pairs"
        },
        {
          "name": "Start Bot",
          "value": "start_bot",
          "action": "Start Bot",
          "description": "Start Bot"
        },
        {
          "name": "Stop Bot",
          "value": "stop_bot",
          "action": "Stop Bot",
          "description": "Stop Bot"
        },
        {
          "name": "Archive Bot",
          "value": "archive_bot",
          "action": "Archive Bot",
          "description": "Archive Bot"
        },
        {
          "name": "Restore Bot",
          "value": "restore_bot",
          "action": "Restore Bot",
          "description": "Restore Bot"
        },
        {
          "name": "Clone DCA Bot",
          "value": "clone_dca_bot",
          "action": "Clone DCA Bot",
          "description": "Clone DCA Bot"
        },
        {
          "name": "Clone Combo Bot",
          "value": "clone_combo_bot",
          "action": "Clone Combo Bot",
          "description": "Clone Combo Bot"
        }
      ],
      "required": true,
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "bots"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get_user_deals",
      "options": [
        {
          "name": "Get User Deals",
          "value": "get_user_deals",
          "action": "Get User Deals",
          "description": "Get User Deals"
        },
        {
          "name": "Update DCA Deal",
          "value": "update_dca_deal",
          "action": "Update DCA Deal",
          "description": "Update DCA Deal"
        },
        {
          "name": "Update Combo Deal",
          "value": "update_combo_deal",
          "action": "Update Combo Deal",
          "description": "Update Combo Deal"
        },
        {
          "name": "Add Funds To Deal",
          "value": "add_funds_to_deal",
          "action": "Add Funds To Deal",
          "description": "Add Funds To Deal"
        },
        {
          "name": "Reduce Funds From Deal",
          "value": "reduce_funds_from_deal",
          "action": "Reduce Funds From Deal",
          "description": "Reduce Funds From Deal"
        },
        {
          "name": "Start Deal",
          "value": "start_deal",
          "action": "Start Deal",
          "description": "Start Deal"
        },
        {
          "name": "Close Deal",
          "value": "close_deal",
          "action": "Close Deal",
          "description": "Close Deal"
        }
      ],
      "required": true,
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "deals"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get_user_exchanges",
      "options": [
        {
          "name": "Get User Exchanges",
          "value": "get_user_exchanges",
          "action": "Get User Exchanges",
          "description": "Get User Exchanges"
        },
        {
          "name": "Get User Balances",
          "value": "get_user_balances",
          "action": "Get User Balances",
          "description": "Get User Balances"
        }
      ],
      "required": true,
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "user"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get_supported_exchange",
      "options": [
        {
          "name": "Get Supported Exchanges",
          "value": "get_supported_exchange",
          "action": "Get Supported Exchanges",
          "description": "Get Supported Exchanges"
        },
        {
          "name": "Get Crypto Screener",
          "value": "get_crypto_screener",
          "action": "Get Crypto Screener",
          "description": "Get crypto screener data with filtering and pagination"
        }
      ],
      "required": true,
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "general"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Get User Grid Bots",
      "value": "get_user_grid_bots",
      "action": "Get User Grid Bots",
      "description": "Get User Grid Bots"
    },
    {
      "name": "Get User Combo Bots",
      "value": "get_user_combo_bots",
      "action": "Get User Combo Bots",
      "description": "Get User Combo Bots"
    },
    {
      "name": "Get User DCA Bots",
      "value": "get_user_dca_bots",
      "action": "Get User DCA Bots",
      "description": "Get User DCA Bots"
    },
    {
      "name": "Update DCA Bot Settings",
      "value": "update_dca_bot_settings",
      "action": "Update DCA Bot Settings",
      "description": "Update DCA Bot Settings"
    },
    {
      "name": "Update Combo Bot Settings",
      "value": "update_combo_bot_settings",
      "action": "Update Combo Bot Settings",
      "description": "Update Combo Bot Settings"
    },
    {
      "name": "Change Bot Pairs",
      "value": "change_bot_pairs",
      "action": "Change Bot Pairs",
      "description": "Change Bot Pairs"
    },
    {
      "name": "Start Bot",
      "value": "start_bot",
      "action": "Start Bot",
      "description": "Start Bot"
    },
    {
      "name": "Stop Bot",
      "value": "stop_bot",
      "action": "Stop Bot",
      "description": "Stop Bot"
    },
    {
      "name": "Archive Bot",
      "value": "archive_bot",
      "action": "Archive Bot",
      "description": "Archive Bot"
    },
    {
      "name": "Restore Bot",
      "value": "restore_bot",
      "action": "Restore Bot",
      "description": "Restore Bot"
    },
    {
      "name": "Clone DCA Bot",
      "value": "clone_dca_bot",
      "action": "Clone DCA Bot",
      "description": "Clone DCA Bot"
    },
    {
      "name": "Clone Combo Bot",
      "value": "clone_combo_bot",
      "action": "Clone Combo Bot",
      "description": "Clone Combo Bot"
    },
    {
      "name": "Get User Deals",
      "value": "get_user_deals",
      "action": "Get User Deals",
      "description": "Get User Deals"
    },
    {
      "name": "Update DCA Deal",
      "value": "update_dca_deal",
      "action": "Update DCA Deal",
      "description": "Update DCA Deal"
    },
    {
      "name": "Update Combo Deal",
      "value": "update_combo_deal",
      "action": "Update Combo Deal",
      "description": "Update Combo Deal"
    },
    {
      "name": "Add Funds To Deal",
      "value": "add_funds_to_deal",
      "action": "Add Funds To Deal",
      "description": "Add Funds To Deal"
    },
    {
      "name": "Reduce Funds From Deal",
      "value": "reduce_funds_from_deal",
      "action": "Reduce Funds From Deal",
      "description": "Reduce Funds From Deal"
    },
    {
      "name": "Start Deal",
      "value": "start_deal",
      "action": "Start Deal",
      "description": "Start Deal"
    },
    {
      "name": "Close Deal",
      "value": "close_deal",
      "action": "Close Deal",
      "description": "Close Deal"
    },
    {
      "name": "Get User Exchanges",
      "value": "get_user_exchanges",
      "action": "Get User Exchanges",
      "description": "Get User Exchanges"
    },
    {
      "name": "Get User Balances",
      "value": "get_user_balances",
      "action": "Get User Balances",
      "description": "Get User Balances"
    },
    {
      "name": "Get Supported Exchanges",
      "value": "get_supported_exchange",
      "action": "Get Supported Exchanges",
      "description": "Get Supported Exchanges"
    },
    {
      "name": "Get Crypto Screener",
      "value": "get_crypto_screener",
      "action": "Get Crypto Screener",
      "description": "Get crypto screener data with filtering and pagination"
    }
  ]

npm_readme:
  # n8n-nodes-gainium

  Integrate Gainium with n8n to manage bots, strategies, and deals directly in your workflows. This package lets you trigger actions, fetch data, and coordinate trading logic with the rest of your automation stack.

  ## ⚡ Gainium Overview

  [Gainium](https://gainium.io) is a platform for building and running automated trading strategies. Key features include:

  - Trading terminal
  - Grid, DCA, and Combo bots
  - Backtesting & paper trading
  - Market screener
  - Portfolio tracking
  - Risk tools & auto-compounding
  - Webhook-triggered actions

  ## 🤖 About n8n

  [n8n](https://n8n.io) is a workflow automation tool that connects APIs and services. Use these nodes to tie trading logic to alerts, signals, data sources, or messaging tools—no custom backend required.

  ---

  ## 📚 API Documentation

  📖 **Complete API Reference:** [https://api.gainium.io/api/docs/](https://api.gainium.io/api/docs/)

  > 💡 **Tip:** The interactive API documentation includes detailed endpoint descriptions, request/response examples, and a built-in testing interface to help you understand and implement each feature.

  ---

  ## 🚀 What You Can Do

  The nodes let you:

  - Trigger bots on signals or events
  - Monitor deals, positions, and PnL
  - Pause, resume, close, or adjust bots and deals
  - React to strategy alerts
  - Retrieve account and performance data
  - Combine with Telegram, Discord, TradingView, etc. for full workflows

  ---

  ## 🔧 Use Cases (Examples)

  - Rebalance: Fetch balances + external prices, adjust allocations via bot actions.
  - News / sentiment trigger: Pause or de-risk bots when negative sentiment spikes.
  - Scheduled profit taking: Cron step-down of position size or bot aggressiveness.
  - Volatility adaptation: Adjust grid/DCA parameters when volatility changes.
  - Arbitrage alerting: Compare prices across exchanges and notify or trigger a deal.
  - Chat control: Telegram command interface to pause, resume, or inspect deals.
  - Risk rules: Auto close or scale down deals at configured drawdown levels.
  - Template deployment: Create bots from stored presets (e.g. Airtable / Notion).
  - PnL logging: Push periodic performance snapshots to Sheets or Notion.

  ---

  ## 📦 Installation

  In your n8n instance or custom Docker setup:

      npm install n8n-nodes-gainium

  Then restart your n8n server.

  > This is a community node. You’ll need to enable community nodes in your `n8n` settings.

  ---

  ## 🔐 Authentication

  Set your Gainium API Key in the credentials section of the node. You can create an API key in your Gainium account settings.

  ---

  ## **🧩 Nodes Included**

  Each Gainium API endpoint has its own dedicated n8n node for maximum flexibility in your workflows.

  ### **Bots**

  - Get Grid Bots — /api/bots/grid
  - Get Combo Bots — /api/bots/combo
  - Get DCA Bots — /api/bots/dca
  - Update DCA Bot — /api/updateDCABot
  - Update Combo Bot — /api/updateComboBot
  - Change Bot Pairs — /api/changeBotPairs
  - Start Bot — /api/startBot
  - Clone DCA Bot — /api/cloneDCABot
  - Clone Combo Bot — /api/cloneComboBot
  - Restore Bot — /api/restoreBot
  - Stop Bot — /api/stopBot
  - Archive Bot — /api/archiveBot

  ### **Deals**

  - Get Deals — /api/deals
  - Update DCA Deal — /api/updateDCADel
  - Update Combo Deal — /api/updateComboDeal
  - Add Funds to Deal — /api/addFunds
  - Reduce Funds from Deal — /api/reduceFunds
  - Start Deal — /api/startDeal
  - Close Deal — /api/closeDeal/{dealId}

  ### **User**

  - Get User Exchanges — /api/user/exchanges
  - Get User Balances — /api/user/balances

  ### **General**

  - Get Supported Exchanges — /api/exchanges
  - Get Crypto Screener — /api/screener

  More coming soon.

  ---

  ## **🧠 Agent Tool**

  Provides a unified action/data interface for AI or rule-driven agents (OpenAI, Anthropic, LangChain, etc.) so they can inspect state and invoke supported bot or deal operations dynamically.

  ---

  ## 💡 Example Workflows

  1. TradingView signal → start bot via webhook
  2. De-risk on BTC dominance spike
  3. Profit alert when deal closes > X%
  4. Screener match → create / notify
  5. AI agent adjusts risk or pauses bots
  6. Natural language command interface (Telegram / Slack)
  7. Adaptive parameter tuning (volatility & volume)

  ---

  ## 🙋 Support

  Need help or a feature? Open an issue or visit the [community forum](https://community.gainium.io).

ai_documentation_summary:
  {"purpose":"This n8n node integrates with the Gainium trading platform to automate bot management, deal execution, and portfolio tracking within workflow automation.","capabilities":["Trigger bots on signals or events","Monitor deals, positions, and PnL","Pause, resume, close, or adjust bots and deals","React to strategy alerts","Retrieve account and performance data","Fetch grid, DCA, and combo bot configurations","Manage deal funds (add/reduce)","Access user exchanges and balances","Retrieve market screener data","Support AI agents via Agent Tool for dynamic bot/deal operations"],"authentication":"API Key required (configured in node credentials)","commonUseCases":["Rebalancing portfolio allocations via bot actions","Pausing or de-risking bots based on news/sentiment triggers","Scheduled profit taking and volatility adaptation","Arbitrage alerting and cross-exchange price comparison","Telegram/Slack command interfaces for bot control"],"limitations":["Community node requiring manual enabling in n8n settings","Some features (e.g., additional bot types) may not be fully implemented yet"],"relatedNodes":["n8n-nodes-github","n8n-nodes-telegram","n8n-nodes-discord","n8n-nodes-slack","n8n-nodes-webhook"]}


node_type: n8n-nodes-fluentc.fluentCTranslate
package_name: n8n-nodes-fluentc
display_name: FluentC Translate
description: Translate text or HTML using FluentC AI
category: Community
development_style: declarative
version: 1
properties_schema: []
operations: []
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: fluentc
author_github_url: https://github.com/FluentC/
npm_package_name: n8n-nodes-fluentc
npm_version: 1.2.7
community_fetched_at: 2026-03-25T21:19:40.597Z
ai_summary_generated_at: 2026-03-25 22:51:43
npm_readme:
  # FluentC N8N Plugin

  This N8N community plugin provides integration with the FluentC AI Translation API, enabling you to translate text and HTML content and detect languages within your N8N workflows.

  ## Features

  - **Translation**: Support for both real-time and batch translation modes
  - **Language Detection**: Detect the language of input content
  - **Language Retrieval**: Fetch lists of supported and source languages
  - **Dynamic Language Selection**: Dropdown menus populated with your API key's enabled languages
  - **Format Support**: Handle both text and HTML content
  - **Automatic Polling**: Batch jobs are automatically polled until completion
  - **Comprehensive Output**: Returns all metadata including token counts, detected languages, and model information
  - **Error Handling**: Graceful error handling with continue-on-fail support
  - **Language Management**: Direct link to manage enabled languages on your account

  ## Installation

  The installation method depends on your n8n setup. Choose the appropriate method below:

  ### Method 1: n8n GUI Installation (Recommended for self-hosted n8n)

  This is the easiest method for self-hosted n8n instances:

  1. **Open n8n Settings**: In your n8n instance, go to **Settings** > **Community Nodes**
  2. **Install Node**: Click **Install** 
  3. **Enter Package Name**: Enter `n8n-nodes-fluentc` in the "Enter npm package name" field
  4. **Accept Risk**: Check "I understand the risks of installing unverified code from a public source"
  5. **Install**: Click **Install**
  6. **Restart**: The nodes will be available immediately after installation

  ### Method 2: Verified Community Nodes (for n8n Cloud and recent versions)

  If this package gets verified by n8n (future goal):

  1. **Open Nodes Panel**: Go to the Canvas and open the nodes panel (click '+' or press Tab)
  2. **Search**: Search for "FluentC" 
  3. **Install from Community**: Look for the "More from the community" section
  4. **Install**: Click on the FluentC node and select **Install**

  ### Method 3: Manual Installation (for Docker/advanced setups)

  For Docker containers, queue mode, or when GUI installation isn't available:

  #### For Docker Containers:

  1. **Access Docker Shell**: 
     ```bash
     docker exec -it [your-n8n-container-name] sh
     ```

  2. **Create nodes directory** (if it doesn't exist):
     ```bash
     mkdir -p ~/.n8n/nodes
     cd ~/.n8n/nodes
     ```

  3. **Install the package**:
     ```bash
     npm install n8n-nodes-fluentc
     ```

  4. **Restart n8n container**

  #### For npm-based installations:

  1. **Navigate to n8n nodes directory**:
     ```bash
     cd ~/.n8n/nodes
     ```
     (Create the directory if it doesn't exist: `mkdir -p ~/.n8n/nodes`)

  2. **Install the package**:
     ```bash
     npm install n8n-nodes-fluentc
     ```

  3. **Restart n8n**

  ### Method 4: Custom Docker Build

  For permanent installation in a custom Docker image:

  ```dockerfile
  FROM n8nio/n8n:latest
  RUN cd ~/.n8n/ && mkdir -p nodes && cd nodes && npm install n8n-nodes-fluentc
  ```

  ## Verification

  After installation, verify the nodes are available:

  1. Create a new workflow
  2. Click the '+' button to add a node
  3. Search for "FluentC" in the node search
  4. You should see:
     - **FluentC Translate** (with package icon)
     - **FluentC Check Language** (with package icon)
     - **FluentC Languages** (with package icon)

  ## Setup

  ### 1. Create FluentC API Credentials

  1. In N8N, go to **Credentials** > **New**
  2. Search for "FluentC API" and select it
  3. Enter your FluentC API key (obtained from the FluentC sales website)
  4. Test and save the credential

  ### 2. Using the Nodes

  #### FluentC Translate Node

  This node handles text and HTML translation with support for both real-time and batch modes.

  **Parameters:**
  - **Mode**: Choose between "Real-time" (synchronous) or "Batch" (asynchronous)
  - **Input**: Text or HTML content to translate (max 100,000 bytes)
  - **Input Format**: Select "Text" or "HTML"
  - **Target Language**: Dropdown populated with your enabled target languages
  - **Source Language**: Dropdown with your enabled source languages (optional, includes "Auto-detect")
  - **Max Polling Attempts**: (Batch mode only) Maximum polling attempts for batch jobs (default: 30)

  **Language Management:**
  The language dropdowns are dynamically populated based on your API key's enabled languages. If you need additional languages, you'll see a link to visit www.fluentc.io to manage your language access.

  **Output:**
  The node returns all available data including:
  - `translation`: The translated content
  - `token_count`: Number of tokens used
  - `source_language_detected`: Detected source language
  - `model_used`: AI model used for translation
  - `metadata`: Additional metadata
  - `mode`: Translation mode used
  - `input_format`: Input format processed
  - `target_language`: Target language

  #### FluentC Check Language Node

  This node detects the language of input content.

  **Parameters:**
  - **Input**: Text or HTML content to analyze
  - **Input Format**: Select "Text" or "HTML"

  **Output:**
  - `detected_language`: Two-letter language code of detected language
  - `confidence`: Confidence score (0-1) for the detection
  - `input_format`: Input format processed
  - `input_length`: Length of analyzed content

  #### FluentC Languages Node

  This node fetches the lists of supported and source languages from your FluentC account.

  **Parameters:**
  This node has no parameters. It only requires a configured FluentC API credential.

  **Output:**
  - `supported_languages`: An array of objects, each with `code` and `name` of a language available for translation as a target.
  - `source_languages`: An array of objects, each with `code` and `name` of a language available as a source for translation.

  ## Usage Examples

  ### Basic Translation Workflow

  1. Add a **FluentC Translate** node to your workflow
  2. Configure it with:
     - Mode: "Real-time"
     - Input: "Hello, how are you today?"
     - Input Format: "Text"
     - Target Language: Select "Spanish (es)" from dropdown
  3. The output will include the Spanish translation and metadata

  ### Batch Translation for Large Content

  1. Use **FluentC Translate** node with:
     - Mode: "Batch"
     - Input: Your large HTML or text content
     - Target Language: Your desired language code
  2. The node will automatically poll until the translation is complete

  ### Language Detection Before Translation

  1. Add a **FluentC Check Language** node first
  2. Connect it to a **FluentC Translate** node
  3. Use the detected language as the source language parameter

  ### Retrieving Language Lists

  1. Add a **FluentC Languages** node to your workflow.
  2. Execute the node.
  3. The output will contain two arrays: `supported_languages` and `source_languages`, which you can then use in other parts of your workflow.

  ## Troubleshooting

  ### Nodes Not Appearing

  If nodes don't appear after installation:

  1. **Check Installation Method**: Ensure you used the correct method for your setup
  2. **Restart n8n**: Always restart n8n after manual installation
  3. **Check Directory**: For manual installs, verify the package is in `~/.n8n/nodes/`
  4. **Check Permissions**: Ensure n8n has read permissions for the nodes directory
  5. **Check Logs**: Look at n8n logs for any error messages during startup

  ### Permission Issues

  For Docker installations, you might need to ensure proper permissions:

  ```bash
  # Inside the container
  chown -R node:node ~/.n8n/nodes
  ```

  ### Queue Mode

  If you're running n8n in queue mode, you **must** use manual installation (Method 3).

  ## Error Handling

  The plugin includes comprehensive error handling:

  - **Content Size Validation**: Prevents requests exceeding 100,000 bytes
  - **Batch Timeout Protection**: Limits polling attempts to prevent infinite loops  
  - **API Error Handling**: Gracefully handles API errors and rate limits
  - **Continue on Fail**: Option to continue workflow execution even if translation fails

  ## API Rate Limits

  The plugin respects FluentC API rate limits and uses the recommended polling intervals returned by the batch API to avoid overwhelming the service.

  ## Development & Publishing

  This package follows n8n community node standards:

  - ✅ Package name starts with `n8n-nodes-`
  - ✅ Includes `n8n-community-node-package` keyword
  - ✅ No runtime dependencies (only devDependencies)
  - ✅ Proper n8n configuration in package.json
  - ✅ Built TypeScript with proper exports

  ## Support

  For issues related to this N8N plugin, please check:

  1. **FluentC API Documentation**: https://fluentc.ai/docs
  2. **N8N Community**: https://community.n8n.io
  3. **Plugin Repository**: https://github.com/FluentC/n8n-nodes-fluentc
  4. **n8n Community Nodes Documentation**: https://docs.n8n.io/integrations/community-nodes/

  ## License

  MIT License - see LICENSE file for details.

  ---

  **Note**: You need a valid FluentC API key to use this plugin. API keys are obtained through the FluentC sales website.

ai_documentation_summary:
  {"purpose":"The FluentC Translate node integrates with the FluentC AI Translation API to translate text and HTML content, detect languages, and manage language lists within n8n workflows.","capabilities":["Real-time (synchronous) translation","Batch translation with automatic polling","Text and HTML content translation","Language detection for input content","Supported and source language list retrieval","Dynamic language selection based on API credentials","Comprehensive metadata output including token counts and model information"],"authentication":"API key required (FluentC API key)","commonUseCases":["Multi-language content translation for websites or documents","Real-time chat translation","Batch processing of large content volumes","Language detection for incoming user input","Workflow automation for internationalization tasks"],"limitations":["Input content is limited to a maximum of 100,000 bytes","Batch mode requires manual configuration of max polling attempts","Language selection is restricted to languages enabled in the user's FluentC account"],"relatedNodes":["FluentC Check Language","FluentC Languages","n8n-nodes-fluentc.fluentCTranslate"]}


node_type: n8n-nodes-campaignkit.campaignKitTrigger
package_name: n8n-nodes-campaignkit
display_name: CampaignKit Trigger
description: Trigger workflows when CampaignKit events occur
category: Community
development_style: declarative
version: 1
properties_schema: []
operations: []
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: campaignkit
author_github_url: https://github.com/campaignkit-cc/
npm_package_name: n8n-nodes-campaignkit
npm_version: 1.0.3
community_fetched_at: 2026-03-25T21:19:40.598Z
ai_summary_generated_at: 2026-03-25 22:51:43
npm_readme:
  # n8n-nodes-campaignkit

  This is an n8n community node for [CampaignKit Email Validation](https://campaignkit.cc). It lets you validate email addresses and receive notifications when validation jobs complete in your n8n workflows.

  [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 Node Installation

  1. Go to **Settings** > **Community Nodes** in your n8n instance
  2. Select **Install**
  3. Enter `n8n-nodes-campaignkit` in the **Enter npm package name** field
  4. Click **Install**

  ### Manual Installation

  If you're self-hosting n8n, you can install the node manually:

  ```bash
  npm install n8n-nodes-campaignkit
  ```

  For Docker-based installations, add the package to your n8n Docker image or install it in your running container.

  ## Operations

  ### CampaignKit Node

  The main CampaignKit node supports the following operations:

  #### Validate Email
  Validates a single email address and returns detailed verification results.

  **Input:**
  - `email` (string, required): The email address to validate

  **Output:**
  ```json
  {
    "email": "user@example.com",
    "is_valid": true,
    "is_risky": false,
    "is_invalid": false,
    "is_disposable": false,
    "is_role_email": false,
    "is_catchall": false,
    "is_blacklisted": false,
    "is_free_email": true,
    "is_mx_found": true,
    "is_smtp_valid": true,
    "score": 10,
    "classifier": "valid",
    "domain": "example.com",
    "mx_record": "mail.example.com",
    "smtp_provider": "Google",
    "free_email_provider": "Gmail",
    "did_you_mean": ""
  }
  ```

  #### Validate Batch
  Validates multiple email addresses as a batch job.

  **Input:**
  - `emails` (string, required): Email addresses to validate (one per line)
  - `label` (string, optional): Label for the validation job

  **Output:**
  ```json
  {
    "job_id": 12345,
    "email_count": 100,
    "status": "pending",
    "label": "My Email List",
    "created_at": "2025-01-15T10:00:00Z"
  }
  ```

  #### Get Job Status
  Retrieves the status and results of a validation job.

  **Input:**
  - `jobId` (number, required): The ID of the validation job

  **Output:**
  ```json
  {
    "job_id": 12345,
    "account_id": 456,
    "label": "My Email List",
    "state": "done",
    "email_count": 100,
    "valid_count": 85,
    "invalid_count": 10,
    "risky_count": 5,
    "deliverable_count": 85,
    "undeliverable_count": 15,
    "credits_used": 100,
    "progress": 100,
    "created_at": "2025-01-15T10:00:00Z",
    "started_at": "2025-01-15T10:00:05Z",
    "finished_at": "2025-01-15T10:05:00Z"
  }
  ```

  ### CampaignKit Trigger

  The CampaignKit Trigger node responds to events from CampaignKit.

  #### Job Completed
  Triggers when a validation job is completed.

  **Output:**
  ```json
  {
    "event": "validation.job.completed",
    "job_id": 12345,
    "account_id": 456,
    "label": "My Email List",
    "state": "done",
    "email_count": 100,
    "deliverable_count": 85,
    "undeliverable_count": 15,
    "risky_count": 5,
    "credits_used": 100,
    "created_at": "2025-01-15T10:00:00Z",
    "finished_at": "2025-01-15T10:05:00Z"
  }
  ```

  ## Credentials

  To use this node, you need a CampaignKit API key.

  ### Getting Your API Key

  1. Sign up for a [CampaignKit account](https://app.campaignkit.cc)
  2. Navigate to **Settings** > **API Keys**
  3. Create a new API key or copy an existing one

  ### Configuring Credentials in n8n

  1. In your n8n workflow, add a CampaignKit node
  2. Click on **Credentials** > **Create New**
  3. Select **CampaignKit API**
  4. Enter your API key
  5. (Optional) Modify the Base URL if using a custom instance
  6. Click **Save**

  The credential will be automatically tested by making a request to `/v1/account/me`.

  ## Example Workflows

  ### Example 1: Validate Single Email

  ```
  Manual Trigger → CampaignKit (Validate Email) → Filter (is_valid = true) → Send Email
  ```

  1. Trigger workflow manually or via webhook
  2. Use **CampaignKit** node with **Validate Email** operation
  3. Filter results to only include valid emails
  4. Send confirmation email for valid addresses

  ### Example 2: Batch Validation with Notification

  ```
  Schedule Trigger → HTTP Request (Get Emails) → CampaignKit (Validate Batch) → Wait → CampaignKit (Get Job Status) → Send Notification
  ```

  1. Schedule workflow to run daily
  2. Fetch email list from your database/API
  3. Start batch validation with CampaignKit
  4. Wait for job to complete (or use webhook trigger)
  5. Get final job results
  6. Send summary notification

  ### Example 3: Webhook-Based Batch Validation

  ```
  CampaignKit Trigger (Job Completed) → Filter (deliverable_count > 0) → HTTP Request (Update Database) → Slack Notification
  ```

  1. **CampaignKit Trigger** listens for job completion
  2. Filter to only process jobs with deliverable emails
  3. Update your database with results
  4. Send Slack notification with summary

  ## Understanding Email Validation Scores

  CampaignKit assigns each email a score from 0-10:

  - **10**: Valid - All checks passed, email is deliverable
  - **9**: Valid but unverified - Syntax and MX checks passed, but SMTP verification failed
  - **2**: Risky - Email exists but may be a spam trap or temporary
  - **0**: Invalid - Email will bounce

  ### Key Output Fields

  - `is_valid`: Email is deliverable (score 9-10)
  - `is_invalid`: Email will bounce (score 0)
  - `is_risky`: Email is suspicious (score 2)
  - `is_disposable`: Temporary/disposable email service
  - `is_role_email`: Generic role address (info@, support@, etc.)
  - `is_catchall`: Domain accepts all email addresses
  - `is_blacklisted`: Email is on CampaignKit's blacklist
  - `did_you_mean`: Suggested correction for typos

  ## API Rate Limits

  CampaignKit API has the following limits:

  - **Authenticated requests**: Based on your account plan
  - **Batch validation**: Uses credits based on email count
  - **Webhook delivery**: Up to 5 retry attempts with exponential backoff

  Monitor your credit usage via the `/v1/account/billing/balance` endpoint or in your CampaignKit dashboard.

  ## Troubleshooting

  ### Credential Test Fails

  - Verify your API key is correct
  - Check that your CampaignKit account is active
  - Ensure the Base URL is correct (default: `https://api.campaignkit.cc`)

  ### Webhook Not Receiving Events

  - Ensure your n8n instance is accessible from the internet
  - Check that the workflow is activated
  - Verify webhook was created successfully in CampaignKit dashboard
  - Check n8n execution logs for incoming webhook requests

  ### Batch Job Stuck in "Pending"

  - Large batches may take time to process
  - Use the **Get Job Status** operation to monitor progress
  - Jobs process at approximately 100-500 emails/minute depending on validation type

  ### "Insufficient Credits" Error

  - Check your account credit balance
  - Purchase additional credits in your CampaignKit dashboard
  - Contact support@campaignkit.cc for billing assistance

  ## Development

  ### Building the Node

  ```bash
  npm install
  npm run build
  ```

  ### Local Testing

  1. Build the node
  2. Link it to your n8n installation:
     ```bash
     npm link
     cd ~/.n8n/nodes
     npm link n8n-nodes-campaignkit
     ```
  3. Restart n8n

  ### Project Structure

  ```
  n8n-nodes-campaignkit/
  ├── credentials/
  │   └── CampaignKitApi.credentials.ts
  ├── nodes/
  │   └── CampaignKit/
  │       ├── CampaignKit.node.ts
  │       └── CampaignKitTrigger.node.ts
  ├── types/
  │   └── CampaignKit.types.ts
  ├── package.json
  ├── tsconfig.json
  └── README.md
  ```

  ## Resources

  - [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
  - [CampaignKit API Documentation](https://campaignkit.cc/docs)
  - [CampaignKit Dashboard](https://app.campaignkit.cc)
  - [CampaignKit Support](mailto:support@campaignkit.cc)

  ## Compatibility

  - n8n version: 0.220.0 or higher
  - Node.js version: 18.x or higher

  ## License

  [MIT](LICENSE)

  ## Version History

  ### 0.1.0 (Initial Release)
  - ✅ Validate Email operation
  - ✅ Validate Batch operation
  - ✅ Get Job Status operation
  - ✅ Job Completed webhook trigger
  - ✅ API Key authentication
  - ✅ Comprehensive error handling

  ## Support

  For issues related to:
  - **This n8n node**: Open an issue on [GitHub](https://github.com/yourusername/CampaignKit/issues)
  - **CampaignKit API**: Contact support@campaignkit.cc
  - **n8n platform**: Visit [n8n community forum](https://community.n8n.io)

ai_documentation_summary:
  {"purpose":"A community n8n node that validates email addresses individually or in batches and triggers workflows when CampaignKit validation jobs complete.","capabilities":["Validate single email addresses with detailed verification results","Run batch email validation jobs","Retrieve status and results of validation jobs","Trigger workflows on job completion events","Provide email scoring (0-10) and risk classification","Detect disposable, role-based, catchall, and blacklisted emails","Suggest email corrections for typos"],"authentication":"API key required (CampaignKit API key)","commonUseCases":["Email list cleaning before sending marketing campaigns","Automated bounce management for email lists","Real-time email validation via webhook triggers","Scheduled daily validation of contact databases"],"limitations":["Requires separate CampaignKit account and API key","Batch validation uses credits based on email count","Limited to CampaignKit's API rate limits and plan tiers","Webhook delivery has up to 5 retry attempts with exponential backoff"],"relatedNodes":["n8n-nodes-campaignkit.campaignKit","n8n-nodes-campaignkit.campaignKitTrigger","n8n-nodes-standard.manualTrigger","n8n-nodes-standard.httpRequest","n8n-nodes-standard.filter"]}


node_type: n8n-nodes-invoice-api-xhub.invoiceXhub
package_name: n8n-nodes-invoice-api-xhub
display_name: invoice-api.xhub
description: Generate, parse, and validate e-invoices across 14 European countries with 18+ formats
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: pa201608
author_github_url: https://github.com/xhubio/
npm_package_name: n8n-nodes-invoice-api-xhub
npm_version: 1.0.6
community_fetched_at: 2026-03-25T21:19:40.598Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "operation",
      "type": "options",
      "default": "generate",
      "options": [
        {
          "name": "Generate",
          "value": "generate",
          "action": "Generate an e invoice document",
          "description": "Generate an e-invoice document (PDF, XRechnung, ZUGFeRD, etc.)"
        },
        {
          "name": "Get Formats",
          "value": "getFormats",
          "action": "Get supported formats",
          "description": "Get supported countries and formats"
        },
        {
          "name": "Parse",
          "value": "parse",
          "action": "Parse an e invoice document",
          "description": "Parse an e-invoice document and extract invoice data"
        },
        {
          "name": "Parse (Auto-Detect)",
          "value": "parseAutoDetect",
          "action": "Parse an e invoice with auto detection",
          "description": "Parse an e-invoice with automatic country and format detection"
        },
        {
          "name": "Validate",
          "value": "validate",
          "action": "Validate invoice data",
          "description": "Validate invoice data against country-specific rules"
        }
      ],
      "displayName": "Operation",
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Generate",
      "value": "generate",
      "action": "Generate an e invoice document",
      "description": "Generate an e-invoice document (PDF, XRechnung, ZUGFeRD, etc.)"
    },
    {
      "name": "Get Formats",
      "value": "getFormats",
      "action": "Get supported formats",
      "description": "Get supported countries and formats"
    },
    {
      "name": "Parse",
      "value": "parse",
      "action": "Parse an e invoice document",
      "description": "Parse an e-invoice document and extract invoice data"
    },
    {
      "name": "Parse (Auto-Detect)",
      "value": "parseAutoDetect",
      "action": "Parse an e invoice with auto detection",
      "description": "Parse an e-invoice with automatic country and format detection"
    },
    {
      "name": "Validate",
      "value": "validate",
      "action": "Validate invoice data",
      "description": "Validate invoice data against country-specific rules"
    }
  ]

npm_readme:
  # n8n-nodes-invoice-api-xhub

  [![npm version](https://badge.fury.io/js/n8n-nodes-invoice-api-xhub.svg)](https://badge.fury.io/js/n8n-nodes-invoice-api-xhub)
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

  Custom n8n Community Node for the invoice-api.xhub E-Invoice API. Generate, parse, and validate e-invoices for 14 European countries with 18+ formats. 

  https://service.invoice-api.xhub.io/api/v1/invoice/

  ## Features

  - **Generate**: Create e-invoices in various formats (XRechnung, ZUGFeRD, Factur-X, FatturaPA, etc.)
  - **Parse**: Extract invoice data from XML or PDF documents
  - **Parse Auto-Detect**: Parse invoices with automatic country and format detection
  - **Validate**: Validate invoice data against country-specific rules
  - **Get Formats**: Retrieve supported countries and formats

  ## Quick Start

  ```bash
  # Installation
  npm install n8n-nodes-invoice-api-xhub

  # Or via n8n Community Nodes UI
  ```

  1. Install the node via n8n Community Nodes
  2. Create credentials with your invoice-api.xhub API key
  3. Add the invoice-api.xhub node to your workflow
  4. Select the desired operation (Generate, Parse, Validate, Get Formats)

  ## Documentation

  | Document | Description |
  |----------|-------------|
  | [API Reference](docs/API-REFERENCE.md) | Complete API documentation |
  | [Multi-Agent Architecture](docs/MULTI-AGENT-ARCHITECTURE.md) | AI-powered multi-agent system |
  | [Tutorials](docs/tutorials/README.md) | Step-by-step guides |

  ### Tutorials

  | Tutorial | Level | Duration |
  |----------|-------|----------|
  | [Setup Credentials](docs/tutorials/01-getting-started/setup-credentials.md) | Beginner | 10 min |
  | [Your First Invoice](docs/tutorials/01-getting-started/first-invoice.md) | Beginner | 15 min |
  | [Understanding Agents](docs/tutorials/03-multi-agent-basics/understanding-agents.md) | Intermediate | 15 min |
  | [Coordinator Pattern](docs/tutorials/03-multi-agent-basics/coordinator-pattern.md) | Intermediate | 30 min |

  ## Installation

  ### In n8n (Recommended)

  Navigate to **Settings → Community Nodes** and search for `n8n-nodes-invoice-api-xhub`.

  Alternatively via CLI:

  ```bash
  npm install n8n-nodes-invoice-api-xhub
  ```

  ### For Development

  ```bash
  git clone https://github.com/xhubio/n8n-nodes-invoice-api-xhub.git
  cd n8n-nodes-invoice-api-xhub
  pnpm install
  pnpm build
  ```

  ## Credentials Setup

  ### Step 1: Get API Key

  1. Register at [invoice-api.xhub.io](https://invoice-api.xhub.io)
  2. Navigate to **Dashboard → API Keys**
  3. Create a new API key

  API keys have the following format:
  - **Production**: `sk_live_xxxxxxxxxxxxxxxxxxxx`
  - **Test/Sandbox**: `sk_test_xxxxxxxxxxxxxxxxxxxx`

  ### Step 2: Create Credentials in n8n

  1. Open n8n
  2. Navigate to **Credentials → New Credential**
  3. Search for "invoice-api.xhub API"
  4. Fill in the fields:
     - **API Key**: Your API key (sk_live_... or sk_test_...)
     - **Base URL**: `https://service.invoice-api.xhub.io` (default)

  ### Step 3: Test Connection

  The credential test automatically calls `/api/v1/invoice/formats` to verify the connection.

  ## Node Operations

  ### Generate - Create E-Invoice

  Creates an e-invoice document from JSON data.

  **Parameters:**

  | Parameter | Type | Required | Description |
  |-----------|------|----------|-------------|
  | `countryCode` | Dropdown | Yes | Target country (DE, AT, CH, etc.) |
  | `format` | Dropdown | Yes | Output format (xrechnung, zugferd, pdf, etc.) |
  | `invoiceData` | JSON | Yes | Invoice data as JSON object |

  **Options:**

  | Option | Type | Default | Description |
  |--------|------|---------|-------------|
  | `outputBinary` | Boolean | true | Output document as binary data |
  | `binaryPropertyName` | String | "data" | Name of the binary property |
  | `includeWarnings` | Boolean | true | Include validation warnings |
  | `formatOptions` | JSON | {} | Additional format-specific options |

  **Output:**

  ```json
  {
    "success": true,
    "format": "xrechnung",
    "filename": "invoice-INV-2025-001.xml",
    "mimeType": "application/xml",
    "hash": "sha256:abc123...",
    "warnings": []
  }
  ```

  ### Parse - Parse E-Invoice

  Extracts invoice data from a document.

  **Parameters:**

  | Parameter | Type | Required | Description |
  |-----------|------|----------|-------------|
  | `countryCode` | Dropdown | Yes | Expected country of the invoice |
  | `format` | Dropdown | Yes | Expected format |
  | `inputType` | Dropdown | Yes | Input type (binary/base64) |
  | `binaryPropertyName` | String | Yes* | Binary property with the document |
  | `base64Data` | String | Yes* | Base64-encoded document |

  *Depending on `inputType`

  **Options:**

  | Option | Type | Default | Description |
  |--------|------|---------|-------------|
  | `filename` | String | - | Filename hint for the parser |
  | `includeWarnings` | Boolean | true | Include warnings |

  **Output:**

  ```json
  {
    "success": true,
    "format": "xrechnung",
    "hash": "sha256:abc123...",
    "invoice": {
      "invoiceNumber": "INV-2025-001",
      "invoiceDate": "2025-01-15",
      "seller": { ... },
      "buyer": { ... },
      "lineItems": [ ... ],
      "total": 7497.00
    }
  }
  ```

  ### Parse Auto-Detect - Parse with Auto-Detection

  Parses an invoice document with automatic country and format detection.

  **Parameters:**

  | Parameter | Type | Required | Description |
  |-----------|------|----------|-------------|
  | `inputType` | Dropdown | Yes | Input type (binary/base64) |
  | `binaryPropertyName` | String | Yes* | Binary property with the document |
  | `base64Data` | String | Yes* | Base64-encoded document |

  *Depending on `inputType`

  **Options:**

  | Option | Type | Default | Description |
  |--------|------|---------|-------------|
  | `filename` | String | - | Filename hint for format detection |
  | `includeWarnings` | Boolean | true | Include warnings |

  **Output:**

  ```json
  {
    "success": true,
    "format": "xrechnung",
    "hash": "sha256:abc123...",
    "invoice": { ... },
    "detection": {
      "format": "xrechnung",
      "countryCode": "DE",
      "confidence": 0.95,
      "formatMethod": "xml-analysis",
      "countrySource": "vatId",
      "isAmbiguous": false
    },
    "detectedFormat": "xrechnung",
    "detectedCountry": "DE",
    "confidence": 0.95
  }
  ```

  ### Validate - Validate Invoice

  Validates invoice data against country-specific rules.

  **Parameters:**

  | Parameter | Type | Required | Description |
  |-----------|------|----------|-------------|
  | `countryCode` | Dropdown | Yes | Country for validation rules |
  | `invoiceData` | JSON | Yes | Invoice data to validate |

  **Options:**

  | Option | Type | Default | Description |
  |--------|------|---------|-------------|
  | `failOnErrors` | Boolean | false | Fail node on errors |
  | `failOnWarnings` | Boolean | false | Fail node on warnings |

  **Output:**

  ```json
  {
    "valid": true,
    "countryCode": "DE",
    "errors": [],
    "warnings": [
      {
        "code": "W001",
        "message": "Payment terms recommended",
        "field": "paymentTerms"
      }
    ],
    "errorCount": 0,
    "warningCount": 1
  }
  ```

  ### Get Formats - Retrieve Supported Formats

  Retrieves all supported countries and formats.

  **Parameters:**

  | Parameter | Type | Required | Description |
  |-----------|------|----------|-------------|
  | `scope` | Dropdown | Yes | "all" or "country" |
  | `countryCode` | Dropdown | Yes* | Country for specific formats |

  *Only when scope="country"

  **Output:**

  ```json
  {
    "success": true,
    "countries": [
      {
        "code": "DE",
        "name": "Germany",
        "formats": ["xrechnung", "zugferd", "pdf", "ubl", "cii"]
      }
    ]
  }
  ```

  ## Supported Countries (14)

  | Region | Countries | Main Formats |
  |--------|-----------|--------------|
  | **DACH** | Germany (DE), Austria (AT), Switzerland (CH) | XRechnung, ZUGFeRD, ebInterface, QR-Rechnung |
  | **EU West** | France (FR), Belgium (BE), Netherlands (NL) | Factur-X, UBL-BE, UBL-NL |
  | **EU South** | Italy (IT), Spain (ES), Portugal (PT) | FatturaPA, Facturae, SAF-T |
  | **EU East** | Poland (PL), Czech Republic (CZ), Hungary (HU), Romania (RO), Bulgaria (BG) | KSeF, ISDOC, NAV, e-Factura, UBL-BG |

  ## Supported Formats (18)

  ### DACH Region

  | Format | Country | Description | Output |
  |--------|---------|-------------|--------|
  | `xrechnung` | DE | German e-invoice standard (mandatory for B2G) | XML |
  | `zugferd` | DE/AT/CH | Hybrid format with embedded XML data | PDF |
  | `ebinterface` | AT | Austrian e-invoice standard | XML |
  | `qr-rechnung` | CH | Swiss QR invoice | PDF |

  ### EU West

  | Format | Country | Description | Output |
  |--------|---------|-------------|--------|
  | `facturx` | FR | French standard (based on ZUGFeRD) | PDF |
  | `ubl-be` | BE | Belgian UBL format | XML |
  | `ubl-nl` | NL | Dutch UBL format | XML |

  ### EU South

  | Format | Country | Description | Output |
  |--------|---------|-------------|--------|
  | `fatturapa` | IT | Italian e-invoice standard (mandatory) | XML |
  | `facturae` | ES | Spanish e-invoice standard | XML |
  | `saf-t` | PT | Portuguese SAF-T format | XML |

  ### EU East

  | Format | Country | Description | Output |
  |--------|---------|-------------|--------|
  | `ksef` | PL | Polish Krajowy System e-Faktur | XML |
  | `isdoc` | CZ | Czech e-invoice standard | XML |
  | `nav` | HU | Hungarian tax reporting system | XML |
  | `efactura` | RO | Romanian e-Factura system | XML |
  | `ubl-bg` | BG | Bulgarian UBL format | XML |

  ### Generic Formats

  | Format | Description | Output |
  |--------|-------------|--------|
  | `pdf` | Plain PDF without structured data | PDF |
  | `ubl` | Generic UBL (Universal Business Language) | XML |
  | `cii` | Cross Industry Invoice (UN/CEFACT) | XML |

  ## Invoice Data Schema

  ### Complete Example

  ```json
  {
    "invoiceNumber": "INV-2025-001",
    "invoiceDate": "2025-01-15",
    "dueDate": "2025-02-14",
    "seller": {
      "name": "ACME GmbH",
      "address": {
        "street": "Hauptstraße 1",
        "city": "Berlin",
        "postalCode": "10115",
        "country": "DE"
      },
      "vatId": "DE123456789",
      "email": "billing@acme.de",
      "phone": "+49 30 12345678",
      "bankAccount": {
        "iban": "DE89370400440532013000",
        "bic": "COBADEFFXXX",
        "bankName": "Commerzbank"
      }
    },
    "buyer": {
      "name": "Customer AG",
      "address": {
        "street": "Nebenstraße 2",
        "city": "München",
        "postalCode": "80331",
        "country": "DE"
      },
      "vatId": "DE987654321",
      "email": "purchasing@customer.de"
    },
    "lineItems": [
      {
        "position": 1,
        "description": "Consulting Services - Project Planning",
        "quantity": 10,
        "unit": "HUR",
        "unitPrice": 150.00,
        "vatRate": 19,
        "vatAmount": 285.00,
        "lineTotal": 1500.00
      },
      {
        "position": 2,
        "description": "Software Development",
        "quantity": 40,
        "unit": "HUR",
        "unitPrice": 120.00,
        "vatRate": 19,
        "vatAmount": 912.00,
        "lineTotal": 4800.00
      }
    ],
    "currency": "EUR",
    "subtotal": 6300.00,
    "totalVat": 1197.00,
    "total": 7497.00,
    "paymentTerms": "Payable within 30 days without deduction",
    "notes": "Thank you for your order!"
  }
  ```

  ### Required Fields

  | Field | Type | Description |
  |-------|------|-------------|
  | `invoiceNumber` | String | Unique invoice number |
  | `invoiceDate` | String (YYYY-MM-DD) | Invoice date |
  | `seller` | Object | Seller/Supplier |
  | `seller.name` | String | Company name |
  | `seller.address` | Object | Address with street, city, postalCode, country |
  | `seller.vatId` | String | VAT ID |
  | `buyer` | Object | Buyer/Recipient |
  | `buyer.name` | String | Company name |
  | `buyer.address` | Object | Address |
  | `lineItems` | Array | Invoice line items |
  | `lineItems[].description` | String | Description |
  | `lineItems[].quantity` | Number | Quantity |
  | `lineItems[].unitPrice` | Number | Unit price |
  | `lineItems[].vatRate` | Number | VAT rate in % |
  | `currency` | String | Currency code (EUR, CHF, etc.) |
  | `total` | Number | Total amount including VAT |

  ### Unit Codes (UN/ECE Rec. 20)

  | Code | Description |
  |------|-------------|
  | `C62` | Piece |
  | `HUR` | Hour |
  | `DAY` | Day |
  | `MON` | Month |
  | `KGM` | Kilogram |
  | `MTR` | Meter |
  | `LTR` | Liter |

  ## Examples

  ### Example 1: XRechnung for German B2G Customer

  ```javascript
  // Generate Node Configuration
  {
    "operation": "generate",
    "countryCode": "DE",
    "format": "xrechnung",
    "invoiceData": {
      "invoiceNumber": "XR-2025-001",
      "invoiceDate": "2025-01-15",
      "buyerReference": "04011000-12345-67",  // Leitweg-ID for B2G
      // ... additional data
    }
  }
  ```

  ### Example 2: ZUGFeRD PDF with Embedded Data

  ```javascript
  {
    "operation": "generate",
    "countryCode": "DE",
    "format": "zugferd",
    "invoiceData": { ... },
    "options": {
      "outputBinary": true,
      "binaryPropertyName": "invoice_pdf",
      "formatOptions": {
        "profile": "extended",  // basic, comfort, extended
        "pdfVersion": "PDF/A-3b"
      }
    }
  }
  ```

  ### Example 3: Parse and Validate Invoice

  ```javascript
  // Workflow: Parse → Validate → Process
  // 1. Parse Node
  {
    "operation": "parse",
    "countryCode": "DE",
    "format": "xrechnung",
    "inputType": "binary",
    "binaryPropertyName": "data"
  }

  // 2. Validate Node (connected to Parse)
  {
    "operation": "validate",
    "countryCode": "DE",
    "invoiceData": "={{ $json.invoice }}",
    "options": {
      "failOnErrors": true
    }
  }
  ```

  ## Multi-Agent Workflows

  This package is part of a larger multi-agent system for intelligent e-invoice processing. See [docs/MULTI-AGENT-ARCHITECTURE.md](docs/MULTI-AGENT-ARCHITECTURE.md) for details.

  ### Available Agents

  | Agent | Description |
  |-------|-------------|
  | **Coordinator Agent** | Central orchestration of all requests |
  | **Validation Agent** | Dual validation (API + AI) with confidence score |
  | **Generation Agent** | Intelligent invoice creation from text/JSON |
  | **Parsing Agent** | Document parsing with AI enrichment |
  | **Error Handler Agent** | Automatic error diagnosis and resolution |

  ### Key Features

  - **AI-Powered Processing**: Claude 3.5 for intelligent analysis
  - **Dual Validation**: API-based and AI-based verification
  - **Confidence Scoring**: 0-100% confidence with threshold-based escalation
  - **Human-in-the-Loop**: Slack notifications for low-confidence results
  - **Auto-Fix**: Automatic error correction attempts

  ## Project Structure

  ```
  n8n-nodes-invoice-api-xhub/
  ├── credentials/                    # API credential definitions
  │   └── InvoiceXhubApi.credentials.ts
  ├── nodes/InvoiceXhub/              # Node implementation
  │   ├── actions/                    # Operation handlers
  │   │   ├── generate.operation.ts
  │   │   ├── parse.operation.ts
  │   │   ├── parseAutoDetect.operation.ts
  │   │   ├── validate.operation.ts
  │   │   └── formats.operation.ts
  │   └── InvoiceXhub.node.ts         # Main node entry point
  ├── shared/                         # Shared utilities
  │   ├── GenericFunctions.ts         # API request helpers
  │   └── constants.ts                # Country/format mappings
  ├── docs/                           # Documentation
  │   ├── API-REFERENCE.md            # Complete API docs
  │   ├── MULTI-AGENT-ARCHITECTURE.md # Multi-agent system
  │   └── tutorials/                  # Step-by-step tutorials
  │       ├── README.md
  │       ├── 01-getting-started/
  │       ├── 03-multi-agent-basics/
  │       └── workflows/              # Importable workflow JSONs
  └── test/                           # Jest tests
  ```

  ## Troubleshooting

  ### Common Errors

  #### "API Key invalid"
  - Check that the API key was copied correctly
  - Ensure the key starts with `sk_live_` or `sk_test_`
  - Verify the Base URL

  #### "Validation error: VAT ID invalid"
  - VAT ID must match the country's format
  - DE: `DE` + 9 digits
  - AT: `ATU` + 8 digits
  - CH: `CHE` + 9 digits + "MWST"

  #### "Format not supported for country"
  - Not all formats are available for all countries
  - Check `COUNTRY_FORMATS` in the constants
  - Use "Get Formats" operation

  #### "Binary data not found"
  - Ensure the previous node outputs binary data
  - Check the `binaryPropertyName`

  ### Debug Mode

  Enable extended logs in n8n:

  ```bash
  export N8N_LOG_LEVEL=debug
  n8n start
  ```

  ## Development

  ### Build

  ```bash
  pnpm build
  ```

  ### Watch Mode

  ```bash
  pnpm dev
  ```

  ### Test

  ```bash
  pnpm test
  ```

  ### Lint

  ```bash
  pnpm lint
  pnpm lint:fix
  ```

  ### Local Installation in n8n

  ```bash
  # Build
  pnpm build

  # Link in n8n custom extensions folder
  cd ~/.n8n/custom
  npm link /path/to/n8n-nodes-invoice-xhub
  ```

  ## Changelog

  ### v1.3.0
  - Added Parse Auto-Detect operation (automatic country/format detection)
  - Updated API endpoints to `/api/v1/invoice/...` prefix
  - Improved error handling for country-specific format requests
  - Trailing-slash tolerance for base URL configuration

  ### v1.0.0
  - Initial Release
  - Generate, Parse, Validate, Get Formats operations
  - Support for 14 countries and 18 formats
  - Multi-agent workflow support
  - Comprehensive documentation and tutorials

  ## License

  MIT

  ## Links

  - [invoice-api.xhub Documentation](https://invoice-api.xhub.io/docs)
  - [invoice-api.xhub N8N Integration](https://invoice-api.xhub.io/docs/integrations/n8n)
  - [invoice-api.xhub API Console](https://console.invoice-api.xhub.io)
  - [n8n Community Nodes](https://docs.n8n.io/integrations/community-nodes/)
  - [invoice-api.xhub Homepage](https://invoice-api.xhub.io)
  - [xhub.io Homepage](https://xhub.io)
  - [GitHub Repository](https://github.com/xhubio/n8n-nodes-invoice-api-xhub)

  ## Support

  - **Documentation**: [docs.invoice-api.xhub.io](https://invoice-api.xhub.io/docs)
  - **GitHub Issues**: [github.com/xhubio/n8n-nodes-invoice-api-xhub/issues](https://github.com/xhubio/n8n-nodes-invoice-api-xhub/issues)
  - **Email**: support@xhub.io

ai_documentation_summary:
  {"purpose":"A community n8n node that generates, parses, and validates e-invoices across 14 European countries supporting 18+ formats.","capabilities":["Generate e-invoices from JSON data in various formats (XRechnung, ZUGFeRD, Factur-X, FatturaPA)","Parse e-invoices from XML or PDF documents","Auto-detect invoice country and format","Validate invoice data against country-specific rules","Retrieve supported countries and formats"],"authentication":"API key required (sk_live_ or sk_test_ prefix)","commonUseCases":["Automated invoice generation for European businesses","Invoice data extraction and processing workflows","Cross-border e-invoice validation","Automated document handling for accounting systems"],"limitations":["Limited to 14 European countries","Supports 18+ formats but specific format support may vary by country","Requires manual API key setup in n8n credentials"],"relatedNodes":["n8n-nodes-base-code","n8n-nodes-base-http-request","n8n-nodes-base-binary-file"]}


node_type: n8n-nodes-openmic.openMicAi
package_name: n8n-nodes-openmic
display_name: OpenMicAI
description: Interact with OpenMicAI API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-03-25 21:19:40
author_name: openmic
author_github_url: https://github.com/OpenMicAI/
npm_package_name: n8n-nodes-openmic
npm_version: 0.1.5
community_fetched_at: 2026-03-25T21:19:40.599Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "phoneCall",
      "options": [
        {
          "name": "Phone Call",
          "value": "phoneCall"
        },
        {
          "name": "Bot",
          "value": "bot"
        },
        {
          "name": "Call",
          "value": "call"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "create",
      "options": [
        {
          "name": "Create a Phone Call",
          "value": "create",
          "action": "Create a phone call"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "phoneCall"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Find Bot",
          "value": "get",
          "action": "Find a bot by ID"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many bots"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "bot"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Find Call",
          "value": "get",
          "action": "Find a call by ID"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many calls"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "call"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Create a Phone Call",
      "value": "create",
      "action": "Create a phone call"
    },
    {
      "name": "Find Bot",
      "value": "get",
      "action": "Find a bot by ID"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many bots"
    },
    {
      "name": "Find Call",
      "value": "get",
      "action": "Find a call by ID"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many calls"
    }
  ]

npm_readme:
  # n8n-nodes-openmic

  This is the official n8n node for OpenMic. It provides nodes to interact with the OpenMic API for creating and managing voice AI agents.

  [OpenMic](https://openmic.ai) provides APIs for creating conversational AI agents with natural-sounding voices.

  ## Installation

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

  ## Operations

  ### Phone Call

  - **Create a Phone Call** - Initiate a phone call between two numbers with optional agent override, customer ID, dynamic variables, and callback URL.

  ### Bot

  - **Find Bot** - Retrieve a specific bot by its Agent UID.
  - **Get Many** - List all bots with optional filters (name, created date range) and limit.

  ### Call

  - **Find Call** - Retrieve a specific call by its Call UID.
  - **Get Many** - List all calls with optional filters (customer ID, phone numbers, bot ID, date range, call status, call type) and limit.
  - **Watch Call (Webhook Trigger)** - Watch a call by its Bot UID and trigger a webhook when the call is completed.

  ## Resources

  - [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
  - [OpenMic API Documentation](https://docs.openmic.ai)

  ## Support

  For support with this integration, please contact OpenMic support at team@openmic.ai or visit our [documentation](https://docs.openmic.ai).

ai_documentation_summary:
  {"purpose":"This node enables interaction with the OpenMic API to create and manage voice AI agents, including initiating phone calls and managing bot/call data.","capabilities":["Create phone calls between two numbers","Override agent during phone calls","Pass customer IDs and dynamic variables to calls","Set callback URLs for call events","Retrieve specific bots by Agent UID","List all bots with filtering options","Retrieve specific calls by Call UID","List all calls with filtering options","Trigger webhooks when calls complete"],"authentication":"API key required","commonUseCases":["Automated customer support via voice calls","Scheduling appointments through voice agents","Monitoring call activity and status","Managing multiple voice AI agents"],"limitations":[],"relatedNodes":["n8n-nodes-webhook.webhook","n8n-nodes-http.request"]}


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-03-25 21:19:40
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-03-25T21:19:40.600Z
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"
    },
    {
      "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"
    },
    {
      "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"
    },
    {
      "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"
    }
  ]

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"]}


