# nodes

node_type: @reka-ai/n8n-nodes-reka.rekaVision
package_name: @reka-ai/n8n-nodes-reka
display_name: Reka Vision
description: Get data from Reka AI APIs
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: frank-reka
author_github_url: https://github.com/reka-ai/n8n-nodes-reka.git
npm_package_name: @reka-ai/n8n-nodes-reka
npm_version: 0.7.1
community_fetched_at: 2026-07-14T14:40:33.511Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "clip",
      "options": [
        {
          "name": "Clip",
          "value": "clip"
        },
        {
          "name": "Image Management",
          "value": "imageManagement"
        },
        {
          "name": "Prompt",
          "value": "prompt"
        },
        {
          "name": "Question & Answer",
          "value": "QA"
        },
        {
          "name": "Video Management",
          "value": "videoManagement"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "createClip",
      "options": [
        {
          "name": "CREATE CLIP",
          "value": "createClip",
          "action": "Create a clip",
          "routing": {
            "request": {
              "url": "/creator/reels",
              "method": "POST"
            }
          },
          "description": "Create a new clip job on Reka side"
        },
        {
          "name": "GET JOB STATUS",
          "value": "getJobtatus",
          "action": "Get clip status",
          "routing": {
            "request": {
              "method": "GET"
            }
          },
          "description": "Get the status of a clip job on Reka side"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "clip"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "listVideos",
      "options": [
        {
          "name": "DELETE VIDEO",
          "value": "deleteVideo",
          "action": "Delete video",
          "routing": {
            "request": {
              "method": "DELETE"
            }
          },
          "description": "Delete video from your Reka account library"
        },
        {
          "name": "GET VIDEO",
          "value": "getVideo",
          "action": "Get video",
          "routing": {
            "request": {
              "method": "GET"
            }
          },
          "description": "Get a video details from your Reka account library"
        },
        {
          "name": "LIST VIDEOS",
          "value": "listVideos",
          "action": "List videos",
          "routing": {
            "request": {
              "url": "/videos",
              "method": "GET"
            }
          },
          "description": "List videos in your Reka account library"
        },
        {
          "name": "UPLOAD VIDEO FILE",
          "value": "uploadVideoFile",
          "action": "Upload video file",
          "routing": {
            "request": {
              "url": "/videos/upload",
              "method": "POST"
            }
          },
          "description": "Upload a video to your Reka account library using a file as source"
        },
        {
          "name": "UPLOAD VIDEO FROM URL",
          "value": "uploadVideoFromUrl",
          "action": "Upload video by URL",
          "routing": {
            "request": {
              "url": "/videos/upload",
              "method": "POST"
            }
          },
          "description": "Upload a video to your Reka account library using a URL as source"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "videoManagement"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "listImages",
      "options": [
        {
          "name": "DELETE IMAGE",
          "value": "deleteImage",
          "action": "Delete image",
          "routing": {
            "request": {
              "method": "DELETE"
            }
          },
          "description": "Delete image from your Reka account library"
        },
        {
          "name": "GET IMAGE",
          "value": "getImage",
          "action": "Get image",
          "routing": {
            "request": {
              "method": "GET"
            }
          },
          "description": "Get an image details from your Reka account library"
        },
        {
          "name": "LIST IMAGES",
          "value": "listImages",
          "action": "List images",
          "routing": {
            "request": {
              "url": "/images",
              "method": "GET"
            }
          },
          "description": "List images in your Reka account library"
        },
        {
          "name": "UPLOAD IMAGE FILE",
          "value": "uploadImageFile",
          "action": "Upload image file",
          "routing": {
            "request": {
              "url": "/images/upload",
              "method": "POST"
            }
          },
          "description": "Upload an image to your Reka account library using a file as source"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "imageManagement"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "askVideoQuestion",
      "options": [
        {
          "name": "ASK QUESTION ABOUT IMAGE IN THE LIBRARY",
          "value": "askImageQuestion",
          "action": "Ask question about image in the library",
          "routing": {
            "request": {
              "method": "POST",
              "baseURL": "https://api.reka.ai/v1/chat"
            }
          },
          "description": "Ask a questions about a specific image in the library"
        },
        {
          "name": "ASK QUESTION ABOUT VIDEO IN THE LIBRARY",
          "value": "askVideoQuestion",
          "action": "Ask question about video in the library",
          "routing": {
            "request": {
              "url": "/qa/chat",
              "method": "POST"
            }
          },
          "description": "Ask a questions about a specific video in the library"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "QA"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "imagePrompt",
      "options": [
        {
          "name": "DETECT IN AN IMAGE",
          "value": "detectInImage",
          "action": "Detect in an image",
          "routing": {
            "send": {
              "type": "body",
              "value": "=user",
              "property": "messages[0].role"
            },
            "request": {
              "method": "POST",
              "baseURL": "https://api.reka.ai/v1/chat/completions"
            }
          },
          "description": "Detect objects or features in a specific image"
        },
        {
          "name": "PROMPT ABOUT AN IMAGE",
          "value": "imagePrompt",
          "action": "Prompt about an image",
          "routing": {
            "send": {
              "type": "body",
              "value": "=user",
              "property": "messages[0].role"
            },
            "request": {
              "method": "POST",
              "baseURL": "https://api.reka.ai/v1/chat/completions"
            }
          },
          "description": "Write a prompt about a specific image"
        },
        {
          "name": "PROMPT ABOUT A SMALL VIDEO",
          "value": "smallVideoPrompt",
          "action": "Prompt about a small video",
          "routing": {
            "send": {
              "type": "body",
              "value": "=user",
              "property": "messages[0].role"
            },
            "request": {
              "method": "POST",
              "baseURL": "https://api.reka.ai/v1/chat/completions"
            }
          },
          "description": "Write a prompt about a specific small video (max 10 seconds)"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "prompt"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "CREATE CLIP",
      "value": "createClip",
      "action": "Create a clip",
      "routing": {
        "request": {
          "url": "/creator/reels",
          "method": "POST"
        }
      },
      "description": "Create a new clip job on Reka side",
      "resource": "clip"
    },
    {
      "name": "GET JOB STATUS",
      "value": "getJobtatus",
      "action": "Get clip status",
      "routing": {
        "request": {
          "method": "GET"
        }
      },
      "description": "Get the status of a clip job on Reka side",
      "resource": "clip"
    },
    {
      "name": "DELETE VIDEO",
      "value": "deleteVideo",
      "action": "Delete video",
      "routing": {
        "request": {
          "method": "DELETE"
        }
      },
      "description": "Delete video from your Reka account library",
      "resource": "videoManagement"
    },
    {
      "name": "GET VIDEO",
      "value": "getVideo",
      "action": "Get video",
      "routing": {
        "request": {
          "method": "GET"
        }
      },
      "description": "Get a video details from your Reka account library",
      "resource": "videoManagement"
    },
    {
      "name": "LIST VIDEOS",
      "value": "listVideos",
      "action": "List videos",
      "routing": {
        "request": {
          "url": "/videos",
          "method": "GET"
        }
      },
      "description": "List videos in your Reka account library",
      "resource": "videoManagement"
    },
    {
      "name": "UPLOAD VIDEO FILE",
      "value": "uploadVideoFile",
      "action": "Upload video file",
      "routing": {
        "request": {
          "url": "/videos/upload",
          "method": "POST"
        }
      },
      "description": "Upload a video to your Reka account library using a file as source",
      "resource": "videoManagement"
    },
    {
      "name": "UPLOAD VIDEO FROM URL",
      "value": "uploadVideoFromUrl",
      "action": "Upload video by URL",
      "routing": {
        "request": {
          "url": "/videos/upload",
          "method": "POST"
        }
      },
      "description": "Upload a video to your Reka account library using a URL as source",
      "resource": "videoManagement"
    },
    {
      "name": "DELETE IMAGE",
      "value": "deleteImage",
      "action": "Delete image",
      "routing": {
        "request": {
          "method": "DELETE"
        }
      },
      "description": "Delete image from your Reka account library",
      "resource": "imageManagement"
    },
    {
      "name": "GET IMAGE",
      "value": "getImage",
      "action": "Get image",
      "routing": {
        "request": {
          "method": "GET"
        }
      },
      "description": "Get an image details from your Reka account library",
      "resource": "imageManagement"
    },
    {
      "name": "LIST IMAGES",
      "value": "listImages",
      "action": "List images",
      "routing": {
        "request": {
          "url": "/images",
          "method": "GET"
        }
      },
      "description": "List images in your Reka account library",
      "resource": "imageManagement"
    },
    {
      "name": "UPLOAD IMAGE FILE",
      "value": "uploadImageFile",
      "action": "Upload image file",
      "routing": {
        "request": {
          "url": "/images/upload",
          "method": "POST"
        }
      },
      "description": "Upload an image to your Reka account library using a file as source",
      "resource": "imageManagement"
    },
    {
      "name": "ASK QUESTION ABOUT IMAGE IN THE LIBRARY",
      "value": "askImageQuestion",
      "action": "Ask question about image in the library",
      "routing": {
        "request": {
          "method": "POST",
          "baseURL": "https://api.reka.ai/v1/chat"
        }
      },
      "description": "Ask a questions about a specific image in the library",
      "resource": "QA"
    },
    {
      "name": "ASK QUESTION ABOUT VIDEO IN THE LIBRARY",
      "value": "askVideoQuestion",
      "action": "Ask question about video in the library",
      "routing": {
        "request": {
          "url": "/qa/chat",
          "method": "POST"
        }
      },
      "description": "Ask a questions about a specific video in the library",
      "resource": "QA"
    },
    {
      "name": "DETECT IN AN IMAGE",
      "value": "detectInImage",
      "action": "Detect in an image",
      "routing": {
        "send": {
          "type": "body",
          "value": "=user",
          "property": "messages[0].role"
        },
        "request": {
          "method": "POST",
          "baseURL": "https://api.reka.ai/v1/chat/completions"
        }
      },
      "description": "Detect objects or features in a specific image",
      "resource": "prompt"
    },
    {
      "name": "PROMPT ABOUT AN IMAGE",
      "value": "imagePrompt",
      "action": "Prompt about an image",
      "routing": {
        "send": {
          "type": "body",
          "value": "=user",
          "property": "messages[0].role"
        },
        "request": {
          "method": "POST",
          "baseURL": "https://api.reka.ai/v1/chat/completions"
        }
      },
      "description": "Write a prompt about a specific image",
      "resource": "prompt"
    },
    {
      "name": "PROMPT ABOUT A SMALL VIDEO",
      "value": "smallVideoPrompt",
      "action": "Prompt about a small video",
      "routing": {
        "send": {
          "type": "body",
          "value": "=user",
          "property": "messages[0].role"
        },
        "request": {
          "method": "POST",
          "baseURL": "https://api.reka.ai/v1/chat/completions"
        }
      },
      "description": "Write a prompt about a specific small video (max 10 seconds)",
      "resource": "prompt"
    }
  ]

npm_readme:
  # n8n-nodes-reka

  [![version](https://img.shields.io/npm/v/n8n-nodes-text-manipulation.svg)](https://www.npmjs.org/package/n8n-nodes-text-manipulation)

  Official Reka community node that provides various capability with AI Vision. (soon Research, Speech, and Text)

  ## Operations

  ### Video Clipping

  - Automatically clip a video from a URL

  ### Vision

  - Prompt AI about images and small videos

  - Question and Answer (Q&A) from video

  - Question and Answer (Q&A) from image

  ### Speech

  - soon

  ### Research

  - soon

  ## Credentials

  Theses nodes requires an API Key from Reka, You can generate one by going to your [Dashboard](https://link.reka.ai/free).

  ## Installation

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

  ## Usage

  We are working on a more detailed documentation, meanwhile have a look to this [blog post](https://www.frankysnotes.com/2025/12/building-ai-powered-youtube-clipper.html) or [video](https://www.youtube.com/watch?v=6YV6K94m_FA) to learn How to Automate Clipping Using Reka's community node.

  ### Clip

  From a existing video (ex: YouTube) generate a vertical or horizontal clip using AI. Specific your instructions in a prompt (ex: Create an engaging short video highlighting the best moments) and voila! Optionally you can add burned captions, specify the minimum and maximum duration of your clip. Once ready you will get your video clip with a suggestion of title, description and tags.

  ### Prompt

  Ask any question about an image or a short video (up to ~10 seconds) and get an instant AI response.

  **Examples:**

  - Describe this image
  - Summarize this video

  You can also detect objects in images. Provide the name of the object you're looking for, and AI will return the bounding box coordinates if it's found.

  **Examples:**

  - eye
  - flower


  ### Q&A

  Ask any question about a image or a video and get the response quickly. The video and images are first uploaded using the media management actions, then you can ask all your questions. This support long videos.

  **Examples:** 

  - Describe this image
  - Summarize this video
  - What's the color of the car in this photo


  ## Resources

  - **[n8n Node Documentation](https://docs.n8n.io/integrations/creating-nodes/)** - Complete guide to building nodes
  - **[n8n Community Forum](https://community.n8n.io/)** - Get help and share your nodes
  - **[Reka Community Discord](https://link.reka.ai/discord)** - Get help and share your workflows
  - **[Reka API Documentaion](https://docs.reka.ai)** - Get help and share your workflows

  ## 🤝 Contributions

  If you find a bug or would like to add a feature, please read our [Code of Conduct](CODE_OF_CONDUCT.md) and the [Contributing Guide](CONTRIBUTING.md).

  Feel free to open an issue or submit a feedback PR and improvements are welcome.

  ## License

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

ai_documentation_summary:
  {"purpose":"An n8n community node that enables AI-powered video clipping and visual question answering using Reka AI APIs.","capabilities":["Automatically clip videos from URLs","Generate vertical or horizontal video clips from existing videos","Add burned captions to video clips","Specify minimum and maximum clip duration","Generate suggested titles, descriptions, and tags for clips","Perform Q&A on images","Perform Q&A on videos","Summarize images and videos"],"authentication":"API Key required","commonUseCases":["Automating YouTube video clipping for social media","Creating engaging short-form video content from long videos","Analyzing and extracting information from images","Summarizing video content for quick insights"],"limitations":["Speech and Research capabilities are not yet available (marked as 'soon')","Detailed usage documentation is still in progress"],"relatedNodes":["n8n HTTP Request","n8n Code Node","n8n Video Processing nodes"]}


node_type: n8n-nodes-layerre.layerre
package_name: n8n-nodes-layerre
display_name: Layerre
description: Create templates and variants from Canva designs with Layerre
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: layerre
author_github_url: https://github.com/layerre/
npm_package_name: n8n-nodes-layerre
npm_version: 1.0.3
community_fetched_at: 2026-07-14T14:40:33.512Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "template",
      "options": [
        {
          "name": "Template",
          "value": "template",
          "description": "Create and manage templates from Canva designs"
        },
        {
          "name": "Variant",
          "value": "variant",
          "description": "Create variants with layer overrides"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "create",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a template",
          "routing": {
            "request": {
              "url": "/template",
              "method": "POST"
            }
          },
          "description": "Create a new template from a Canva design URL"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a template",
          "routing": {
            "request": {
              "url": "=/template/{{$parameter.templateId}}",
              "method": "DELETE"
            }
          },
          "description": "Delete a template"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a template",
          "routing": {
            "request": {
              "url": "=/template/{{$parameter.templateId}}",
              "method": "GET"
            }
          },
          "description": "Retrieve a template by ID with all its layers"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many templates",
          "routing": {
            "request": {
              "url": "/templates",
              "method": "GET"
            }
          },
          "description": "Retrieve a list of templates for the current user"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "template"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "create",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a variant",
          "routing": {
            "send": {
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "=/template/{{$parameter.templateId}}/variant",
              "method": "POST"
            }
          },
          "description": "Create a new variant from a template with layer overrides"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a variant",
          "routing": {
            "request": {
              "url": "=/template/{{$parameter.templateId}}/variant/{{$parameter.variantId}}",
              "method": "DELETE"
            }
          },
          "description": "Delete a variant"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a variant",
          "routing": {
            "request": {
              "url": "=/template/{{$parameter.templateId}}/variant/{{$parameter.variantId}}",
              "method": "GET"
            }
          },
          "description": "Retrieve a variant by ID"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many variants",
          "routing": {
            "request": {
              "url": "=/template/{{$parameter.templateId}}/variants",
              "method": "GET"
            }
          },
          "description": "Retrieve a list of variants for a template"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "variant"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Create",
      "value": "create",
      "action": "Create a template",
      "routing": {
        "request": {
          "url": "/template",
          "method": "POST"
        }
      },
      "description": "Create a new template from a Canva design URL",
      "resource": "template"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a template",
      "routing": {
        "request": {
          "url": "=/template/{{$parameter.templateId}}",
          "method": "DELETE"
        }
      },
      "description": "Delete a template",
      "resource": "template"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a template",
      "routing": {
        "request": {
          "url": "=/template/{{$parameter.templateId}}",
          "method": "GET"
        }
      },
      "description": "Retrieve a template by ID with all its layers",
      "resource": "template"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many templates",
      "routing": {
        "request": {
          "url": "/templates",
          "method": "GET"
        }
      },
      "description": "Retrieve a list of templates for the current user",
      "resource": "template"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a variant",
      "routing": {
        "send": {
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "=/template/{{$parameter.templateId}}/variant",
          "method": "POST"
        }
      },
      "description": "Create a new variant from a template with layer overrides",
      "resource": "variant"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a variant",
      "routing": {
        "request": {
          "url": "=/template/{{$parameter.templateId}}/variant/{{$parameter.variantId}}",
          "method": "DELETE"
        }
      },
      "description": "Delete a variant",
      "resource": "variant"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a variant",
      "routing": {
        "request": {
          "url": "=/template/{{$parameter.templateId}}/variant/{{$parameter.variantId}}",
          "method": "GET"
        }
      },
      "description": "Retrieve a variant by ID",
      "resource": "variant"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many variants",
      "routing": {
        "request": {
          "url": "=/template/{{$parameter.templateId}}/variants",
          "method": "GET"
        }
      },
      "description": "Retrieve a list of variants for a template",
      "resource": "variant"
    }
  ]

npm_readme:
  # n8n-nodes-layerre

  This is an n8n community node for [Layerre](https://layerre.com) - a platform for creating templates and variants from Canva designs.

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

  [Installation](#installation)  
  [Operations](#operations)  
  [Credentials](#credentials)  
  [Usage](#usage)  
  [Contributing](#contributing)  
  [License](#license)

  ## Installation

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

  ```bash
  npm install n8n-nodes-layerre
  ```

  ## Operations

  ### Template

  - **Create** - Create a new template from a Canva design URL
  - **Get** - Get a template by ID with all its layers
  - **Get Many** - List all templates for the current user
  - **Delete** - Delete a template

  ### Variant

  - **Create** - Create a new variant with layer overrides (text, images, colors, positions, etc.)
  - **Get** - Get a variant by ID
  - **Get Many** - List all variants for a template
  - **Delete** - Delete a variant

  ## Credentials

  To use this node, you need a Layerre API key:

  1. Log in to your [Layerre account](https://layerre.com/app)
  2. Go to API Keys
  3. Create a new API key
  4. Copy the key (starts with `lyr_`)

  In n8n:
  1. Go to Credentials → Add Credential
  2. Search for "Layerre API"
  3. Paste your API key

  ## Usage

  ### Creating a Template from Canva

  1. Add the **Layerre** node to your workflow
  2. Select **Template** as the resource
  3. Select **Create** as the operation
  4. Paste your Canva share URL (e.g., `https://www.canva.com/design/...`)
  5. Execute the node

  The node will return the template with all extracted layers.

  ### Creating a Variant with Overrides

  1. Add the **Layerre** node to your workflow
  2. Select **Variant** as the resource
  3. Select **Create** as the operation
  4. Choose a template from the dropdown (dynamically loaded from your account)
  5. (Optional) Set variant dimensions (width/height) to override the template size
  6. Add layer overrides:
     - Select a layer from the dropdown (dynamically loaded from the selected template)
     - Configure override options:
       - **Text layers**: text content, color, font name, font size, font weight, italic, underline, text align, letter spacing, line spacing
       - **Image layers**: image URL, opacity, flip horizontal/vertical
       - **All layers**: position (x, y), size (width, height), rotation, color
  7. Execute the node

  The node will render the variant and return the image URL.

  ### Example: Bulk Image Generation

  ```
  [Spreadsheet] → [Loop Over Items] → [Layerre: Create Variant] → [Download Image]
  ```

  Use data from a spreadsheet to create personalized images at scale by mapping columns to layer overrides.

  ## Contributing

  Contributions are welcome! Here's how to get started:

  ### Development Setup

  1. Fork the repository on GitHub

  2. Clone your fork:
     ```bash
     git clone https://github.com/YOUR_USERNAME/n8n-nodes-layerre.git
     cd n8n-nodes-layerre
     ```

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

  4. Install n8n globally:
     ```bash
     npm install n8n -g
     ```

  5. Start development mode (runs n8n with the node loaded):
     ```bash
     npm run dev
     ```

  5. Make your changes and test in the n8n UI

  ### Building

  ```bash
  npm run build
  ```

  ### Linting

  ```bash
  npm run lint
  npm run lint:fix  # Auto-fix issues
  ```

  ### Submitting Changes

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

  ## Resources

  - [Layerre Documentation](https://layerre.com/docs)
  - [n8n Community Nodes](https://docs.n8n.io/integrations/community-nodes/)

  ## License

  [MIT](LICENSE)

ai_documentation_summary:
  {"purpose":"This n8n node automates the creation of Canva design templates and their variants using the Layerre platform, enabling bulk image generation with customizable text, images, and layout properties.","capabilities":["Create templates from Canva design URLs","Retrieve templates by ID or list all user templates","Delete templates","Create variants with layer overrides (text, images, colors, positions)","Retrieve variants by ID or list all variants for a template","Delete variants","Override text properties (content, color, font, size, weight, italics, alignment, spacing)","Override image properties (URL, opacity, flip)","Override all layer properties (position, size, rotation, color)","Support for bulk image generation via spreadsheet data"],"authentication":"API key required (Layerre API key starting with 'lyr_')","commonUseCases":["Bulk personalized image generation from Canva templates","A/B testing with design variants","Automated marketing asset creation with dynamic content","Template management and versioning"],"limitations":["Requires a paid Layerre account with API access","Canva design URL must be publicly accessible or shareable","Limited to Layerre-supported Canva design features"],"relatedNodes":["n8n-nodes-canva.canva","n8n-nodes-spreadsheet.spreadsheet","n8n-nodes-download-image.download-image","n8n-nodes-loop-over-items.loop-over-items"]}


node_type: n8n-nodes-weavely.weavelyTrigger
package_name: n8n-nodes-weavely
display_name: Weavely Trigger
description: Starts the workflow when a form submission is received from Weavely
category: Community
development_style: declarative
version: 1
properties_schema: []
operations: []
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: weavely
author_github_url: https://github.com/weavely/
npm_package_name: n8n-nodes-weavely
npm_version: 0.1.1
community_fetched_at: 2026-07-14T14:40:33.514Z
ai_summary_generated_at: 2026-03-25 22:51:43
npm_readme:
  # Weavely Trigger for n8n

  An n8n community node that enables workflow automation based on Weavely form submissions. Automatically process form responses and build powerful automations with your Weavely forms.

  ## Installation

  Install this node directly from n8n by following the [community nodes installation guide](https://docs.n8n.io/integrations/community-nodes/installation/gui-install/).

  ## Configuration

  ### Prerequisites
  You'll need a Weavely Personal Token to authenticate the integration. Generate one from [https://forms.weavely.ai/settings?tab=personal-tokens](https://forms.weavely.ai/settings?tab=personal-tokens).

  ### Setup Steps

  1. Add the **Weavely Trigger** node to your workflow
  2. Set up authentication:
     - Click "Create New Credential"
     - Enter your Weavely Personal Token
     - Test the connection
  3. Choose your team from the dropdown
  4. Select which form should trigger the workflow
  5. Activate your workflow

  When someone submits the selected form, your workflow will automatically run with the submission data.

  ## Output Data

  The trigger provides clean, structured data for each submission:

  - **Form metadata**: Form ID, response ID, submission timestamp
  - **Field answers**: Each answer includes both the value and human-readable field label
  - **Type information**: Understand what type of data each field contains

  All field IDs are automatically mapped to their corresponding question labels for easier workflow building.

  ## Developers

  For instructions on testing this node on a local n8n instance for development purposes, see [https://docs.n8n.io/integrations/creating-nodes/test/run-node-locally/](https://docs.n8n.io/integrations/creating-nodes/test/run-node-locally/)

  ## License

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

ai_documentation_summary:
  {"purpose":"This n8n community node automatically triggers a workflow when a form submission is received from Weavely, enabling seamless automation of form responses.","capabilities":["Monitors Weavely form submissions","Extracts form metadata (Form ID, response ID, submission timestamp)","Retrieves field answers with values and human-readable labels","Provides type information for each field","Maps field IDs to question labels automatically"],"authentication":"Personal Token (API Key)","commonUseCases":["Automate responses to customer feedback forms","Integrate form data into CRM systems","Trigger email notifications on form submission","Process survey data into spreadsheets or databases"],"limitations":["Community node (requires manual installation)","Requires valid Weavely Personal Token","Limited to Weavely form submissions only"],"relatedNodes":["Weavely Webhook","Weavely Form Builder","n8n HTTP Request","n8n Set"]}


node_type: n8n-nodes-neverbounce-email-verification.nbEmailVerification
package_name: n8n-nodes-neverbounce-email-verification
display_name: NeverBounce Email Verification
description: Verify email addresses using NeverBounce API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: omri.katz
author_github_url: https://github.com/NeverBounce/n8n-nodes-neverbounce.git
npm_package_name: n8n-nodes-neverbounce-email-verification
npm_version: 1.0.20
community_fetched_at: 2026-07-14T14:40:33.515Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "email",
      "options": [
        {
          "name": "Email",
          "value": "email"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "verify",
      "options": [
        {
          "name": "Verify",
          "value": "verify",
          "action": "Verify an email address",
          "description": "Verify an email address"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "email"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Verify",
      "value": "verify",
      "action": "Verify an email address",
      "description": "Verify an email address",
      "resource": "email"
    }
  ]

npm_readme:
  # NeverBounce Email Verification Node

  This node allows you to verify email addresses using the NeverBounce API, providing detailed validation results to improve email deliverability and reduce bounce rates in your workflows.

  ## Overview

  The NeverBounce Email Verification node connects to the NeverBounce API to validate email addresses. It checks if an email address is valid, invalid, disposable, a catch-all address, or if its status is unknown. The node provides detailed verification results including status codes, flags, and suggested corrections for potentially misspelled domains.

  ## Prerequisites

  - **NeverBounce API Key**: You need a valid API key from NeverBounce to use this node. You can obtain one by signing up at [NeverBounce](https://neverbounce.com/).

  ## Input Parameters

  ### Operation
  - **Verify Email**: Validates an email address using the NeverBounce API.

  ### API Endpoint
  - **API Endpoint**: The NeverBounce API endpoint to use. Default is `https://api.neverbounce.com'`.

  ### Email Field
  - **Email Field**: The name of the field in the input data that contains the email address to verify. Default is `email`.

  ### Additional Fields (Optional)
  - **Output Field Name**: The name of the field where verification results will be stored. Default is `verification_result`.
  - **Timeout**: Timeout in seconds for the API request. Default is 30 seconds.

  ## Output Fields

  The node adds the verification results to the output data in the specified output field (default: `verification_result`). The results include:

  - **valid**: Boolean indicating if the email is valid.
  - **status**: String representation of the verification status (`valid`, `invalid`, `disposable`, `catchall`, or `unknown`).
  - **status_code**: Numeric code representing the verification status:
    - `0`: Valid
    - `1`: Invalid
    - `2`: Disposable
    - `3`: Catchall
    - `4`: Unknown
  - **flags**: Array of flags providing additional information about the email address.
  - **suggested_correction**: Suggested correction for potentially misspelled domains (if available).
  - **raw_response**: The complete raw response from the NeverBounce API.

  Additionally, the node adds two metadata fields:
  - **email_verified**: Boolean set to `true` indicating the email has been verified.
  - **verification_timestamp**: ISO timestamp of when the verification was performed.

  ## Examples

  ### Basic Email Verification

  This example shows how to verify an email address and use the results in a workflow.

  1. **HTTP Request node**: Receives a webhook with an email to verify.
  2. **NeverBounce Email Verification node**: Verifies the email address.
     - Set "Email Field" to `email`
     - Leave other settings as default
  3. **IF node**: Routes the workflow based on verification results.
     - If `{{$json.verification_result.valid}} === true`, proceed with sending email
     - Otherwise, handle invalid email (e.g., notify user, log error)

  ### Bulk Email Verification with Custom Output Field

  This example shows how to verify multiple emails with a custom output field.

  1. **Spreadsheet node**: Reads a list of email addresses from a CSV file.
  2. **NeverBounce Email Verification node**: Verifies each email address.
     - Set "Email Field" to the column name containing emails
     - Set "Output Field Name" to `nb_verification`
  3. **Filter node**: Filters out invalid emails.
     - Set condition to `{{$json.nb_verification.valid}} === true`
  4. **Spreadsheet node**: Saves valid emails to a new spreadsheet.

  ## Additional Resources

  - [NeverBounce N8N Node Repository](https://github.com/NeverBounce/n8n-nodes-neverbounce)
  - [NeverBounce API Documentation](https://developers.neverbounce.com/docs)
  - [Email Deliverability Best Practices](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.nbemailverification/)

ai_documentation_summary:
  {"purpose":"Verifies email addresses using the NeverBounce API to validate validity, detect disposable or catch-all addresses, and provide status codes and suggested corrections.","capabilities":["Validate email address validity","Detect disposable email addresses","Identify catch-all addresses","Return status codes (0-4)","Provide flags for additional email information","Suggest corrections for misspelled domains","Return raw API response","Add metadata fields (email_verified, verification_timestamp)","Support custom email field names","Support custom output field names"],"authentication":"API key required","commonUseCases":["Pre-send email validation before sending campaigns","Filter invalid emails from lists","Clean email databases","Route workflows based on email validity","Bulk email verification from spreadsheets"],"limitations":["Requires a paid NeverBounce API key","Relies on NeverBounce API availability and rate limits","May not detect all types of invalid emails depending on API capabilities"],"relatedNodes":["n8n-nodes-base.if","n8n-nodes-base.httpRequest","n8n-nodes-base.spreadsheet","n8n-nodes-base.filter"]}


node_type: @drwally/n8n-nodes-integration.drwally
package_name: @drwally/n8n-nodes-integration
display_name: DrWally
description: DrWally send message node
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: drwally
author_github_url: https://github.com/sinova-dev/drwally-n8n-node.git
npm_package_name: @drwally/n8n-nodes-integration
npm_version: 1.0.6
community_fetched_at: 2026-07-14T14:40:33.516Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "message",
      "options": [
        {
          "name": "Message",
          "value": "message"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "sendPrivate",
      "options": [
        {
          "name": "Send Private Message",
          "value": "sendPrivate",
          "action": "Send a private message",
          "description": "Send a private message"
        },
        {
          "name": "Send Group Message",
          "value": "sendGroup",
          "action": "Send a group message",
          "description": "Send a group message"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "message"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Send Private Message",
      "value": "sendPrivate",
      "action": "Send a private message",
      "description": "Send a private message",
      "resource": "message"
    },
    {
      "name": "Send Group Message",
      "value": "sendGroup",
      "action": "Send a group message",
      "description": "Send a group message",
      "resource": "message"
    }
  ]

npm_readme:
  # n8n-nodes-DrWally

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

  DrWally API is service that allows users automate whatsapp messaging for business purposes.

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

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

  ## Installation

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

  ## Operations

  1. Send private message - send message to a single user
  2. Send group message - send message to a group

  ## Credentials

  For sending messages using DrWally node, you need to have account on [DrWally website](https://drwallyai.com/)

  1. Create account.
  2. Log in.
  3. In sidebar select WhatsApp for Business
  4. Connect your whatsapp account to the app
  5. Select Webhooks for WhatsApp for Business on sidebar
  6. Generate webhook for selected phone number
  7. Use Webhook URL and Secret Key, to create credentials in n8n

  ## Compatibility

  Minimum n8n version - 2.1.5

  ## Resources

  - [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  - [DrWally API](https://api.drwallyai.com/api-public)

ai_documentation_summary:
  {"purpose":"This n8n community node enables automation of WhatsApp messaging for business purposes by integrating with the DrWally API to send messages to individual users or groups.","capabilities":["Send private messages to a single WhatsApp user","Send group messages to multiple WhatsApp users","Automate WhatsApp communication workflows"],"authentication":"Webhook URL and Secret Key obtained from DrWally's WhatsApp for Business webhook settings","commonUseCases":["Automated customer support notifications","Broadcasting promotional messages to customer groups","Order status updates via WhatsApp","Appointment reminders"],"limitations":["Requires a separate DrWally account and WhatsApp for Business connection","Minimum n8n version 2.1.5 required","Limited to business messaging purposes only"],"relatedNodes":["Webhook node","HTTP Request node","WhatsApp nodes"]}


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

updated_at: 2026-07-14 14:40:33
author_name: development_keephub
author_github_url: https://github.com/RetailInTouch/
npm_package_name: n8n-nodes-keephub
npm_version: 1.8.1
community_fetched_at: 2026-07-14T14:40:33.517Z
ai_summary_generated_at: 2026-03-25 22:47:20
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "content",
      "options": [
        {
          "name": "Content",
          "value": "content"
        },
        {
          "name": "Form Submission",
          "value": "formSubmission"
        },
        {
          "name": "Orgchart",
          "value": "orgchart"
        },
        {
          "name": "Storage",
          "value": "storage"
        },
        {
          "name": "Task Template",
          "value": "task"
        },
        {
          "name": "User",
          "value": "user"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "create",
      "options": [
        {
          "name": "Approve Content",
          "value": "approveContent",
          "action": "Approve content",
          "description": "Approve content pending approval"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create content",
          "description": "Create new content"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete content",
          "description": "Delete content by ID"
        },
        {
          "name": "Find by Content Pool",
          "value": "findByContentPool",
          "action": "Find content by content pool",
          "description": "Find contents by content pool"
        },
        {
          "name": "Find by Group",
          "value": "findByGroup",
          "action": "Find content by group",
          "description": "Find contents by group"
        },
        {
          "name": "Find by Orgunit",
          "value": "findByOrgunit",
          "action": "Find content by orgunit",
          "description": "Find contents by organization unit"
        },
        {
          "name": "Get by ID",
          "value": "getById",
          "action": "Get content by ID",
          "description": "Get content by ID"
        },
        {
          "name": "Reject Content",
          "value": "rejectContent",
          "action": "Reject content",
          "description": "Reject content pending approval"
        },
        {
          "name": "Update by ID",
          "value": "updateById",
          "action": "Update content by ID",
          "description": "Update content by ID"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "content"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getFormSubmission",
      "options": [
        {
          "name": "Calculate Response Duration",
          "value": "calculateResponseDuration",
          "action": "Calculate response duration for a form submission",
          "description": "Time from form creation to submission"
        },
        {
          "name": "Find by Form",
          "value": "findByForm",
          "action": "Find form submissions by form",
          "description": "Retrieve all submissions for a given form"
        },
        {
          "name": "Get",
          "value": "getFormSubmission",
          "action": "Get a form submission",
          "description": "Fetch full form submission data"
        },
        {
          "name": "Get Submission Orgunits",
          "value": "getSubmissionOrgunits",
          "action": "Get submission orgunits for a form submission",
          "description": "Fetch orgunit hierarchy"
        },
        {
          "name": "Get Submitter Details",
          "value": "getSubmitterDetails",
          "action": "Get submitter details for a form submission",
          "description": "Fetch full user data of the submitter"
        },
        {
          "name": "Update Submission Orgunits",
          "value": "updateSubmissionOrgunits",
          "action": "Update submission orgunits for a form submission",
          "description": "Change submission orgunit to limit visibility"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "formSubmission"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getById",
      "options": [
        {
          "name": "Get Ancestors",
          "value": "getAncestors",
          "action": "Get ancestors of an orgchart node",
          "description": "Get all ancestors of an orgchart node by ID"
        },
        {
          "name": "Get by External Ref",
          "value": "getByExternalRef",
          "action": "Get an orgchart node by external reference",
          "description": "Get a single orgchart node by external reference"
        },
        {
          "name": "Get by ID",
          "value": "getById",
          "action": "Get an orgchart node",
          "description": "Get a single orgchart node by ID"
        },
        {
          "name": "Get Children",
          "value": "getChildren",
          "action": "Get children of an orgchart node",
          "description": "Get all children/descendants of an orgchart node by ID"
        },
        {
          "name": "Get Parent",
          "value": "getParent",
          "action": "Get parent of an orgchart node",
          "description": "Get the parent of an orgchart node by ID"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "orgchart"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getSignedUrl",
      "options": [
        {
          "name": "Get Signed URL",
          "value": "getSignedUrl",
          "action": "Get a signed URL for a stored file",
          "description": "Generate a pre-signed URL for secure access to a stored file"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "storage"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "createTask",
      "options": [
        {
          "name": "Approve Task",
          "value": "approveTask",
          "action": "Approve a task",
          "description": "Approve a pending task"
        },
        {
          "name": "Create Task Template",
          "value": "createTask",
          "action": "Create a task template",
          "description": "Create a new task template"
        },
        {
          "name": "Delete Task Template",
          "value": "deleteTask",
          "action": "Delete a task template",
          "description": "Delete a task template by ID"
        },
        {
          "name": "Get Task",
          "value": "getTaskInstance",
          "action": "Get a task by ID",
          "description": "Fetch a task instance by its ID"
        },
        {
          "name": "Get Task Template by ID",
          "value": "getTask",
          "action": "Get a task template by ID",
          "description": "Fetch a task template by its ID"
        },
        {
          "name": "Get Task Template by Task",
          "value": "getTemplateByTask",
          "action": "Get the task template for a given task",
          "description": "Fetch the task template that a specific task was created from"
        },
        {
          "name": "Get Task Template Progress",
          "value": "getTaskProgress",
          "action": "Get progress for a task template",
          "description": "Get completion progress for a task template"
        },
        {
          "name": "Get Task Template Status Counts",
          "value": "getTaskStatusCounts",
          "action": "Get status counts for a task template",
          "description": "Get status distribution counts for a task template"
        },
        {
          "name": "Get Task Templates by Orgunit",
          "value": "getTaskByOrgunit",
          "action": "Get task templates by orgunit",
          "description": "Find all task templates for a specific organization unit"
        },
        {
          "name": "Reject Task",
          "value": "rejectTask",
          "action": "Reject a task",
          "description": "Reject a pending task with a reason"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "task"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getById",
      "options": [
        {
          "name": "Get by ID",
          "value": "getById",
          "action": "Get a user by ID",
          "description": "Retrieve a user by their unique ID"
        },
        {
          "name": "Find by Login Name",
          "value": "findByLoginName",
          "action": "Find a user by login name",
          "description": "Retrieve a user by their Login Name"
        },
        {
          "name": "Find by Group",
          "value": "findByGroup",
          "action": "Find a user by group",
          "description": "Find all users in a specific group"
        },
        {
          "name": "Find by Orgunit",
          "value": "findByOrgunit",
          "action": "Find a user by orgunit",
          "description": "Find all users in a specific organization unit"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "user"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Approve Content",
      "value": "approveContent",
      "action": "Approve content",
      "description": "Approve content pending approval",
      "resource": "content"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create content",
      "description": "Create new content",
      "resource": "content"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete content",
      "description": "Delete content by ID",
      "resource": "content"
    },
    {
      "name": "Find by Content Pool",
      "value": "findByContentPool",
      "action": "Find content by content pool",
      "description": "Find contents by content pool",
      "resource": "content"
    },
    {
      "name": "Find by Group",
      "value": "findByGroup",
      "action": "Find content by group",
      "description": "Find contents by group",
      "resource": "content"
    },
    {
      "name": "Find by Orgunit",
      "value": "findByOrgunit",
      "action": "Find content by orgunit",
      "description": "Find contents by organization unit",
      "resource": "content"
    },
    {
      "name": "Get by ID",
      "value": "getById",
      "action": "Get content by ID",
      "description": "Get content by ID",
      "resource": "content"
    },
    {
      "name": "Reject Content",
      "value": "rejectContent",
      "action": "Reject content",
      "description": "Reject content pending approval",
      "resource": "content"
    },
    {
      "name": "Update by ID",
      "value": "updateById",
      "action": "Update content by ID",
      "description": "Update content by ID",
      "resource": "content"
    },
    {
      "name": "Calculate Response Duration",
      "value": "calculateResponseDuration",
      "action": "Calculate response duration for a form submission",
      "description": "Time from form creation to submission",
      "resource": "formSubmission"
    },
    {
      "name": "Find by Form",
      "value": "findByForm",
      "action": "Find form submissions by form",
      "description": "Retrieve all submissions for a given form",
      "resource": "formSubmission"
    },
    {
      "name": "Get",
      "value": "getFormSubmission",
      "action": "Get a form submission",
      "description": "Fetch full form submission data",
      "resource": "formSubmission"
    },
    {
      "name": "Get Submission Orgunits",
      "value": "getSubmissionOrgunits",
      "action": "Get submission orgunits for a form submission",
      "description": "Fetch orgunit hierarchy",
      "resource": "formSubmission"
    },
    {
      "name": "Get Submitter Details",
      "value": "getSubmitterDetails",
      "action": "Get submitter details for a form submission",
      "description": "Fetch full user data of the submitter",
      "resource": "formSubmission"
    },
    {
      "name": "Update Submission Orgunits",
      "value": "updateSubmissionOrgunits",
      "action": "Update submission orgunits for a form submission",
      "description": "Change submission orgunit to limit visibility",
      "resource": "formSubmission"
    },
    {
      "name": "Get Ancestors",
      "value": "getAncestors",
      "action": "Get ancestors of an orgchart node",
      "description": "Get all ancestors of an orgchart node by ID",
      "resource": "orgchart"
    },
    {
      "name": "Get by External Ref",
      "value": "getByExternalRef",
      "action": "Get an orgchart node by external reference",
      "description": "Get a single orgchart node by external reference",
      "resource": "orgchart"
    },
    {
      "name": "Get by ID",
      "value": "getById",
      "action": "Get an orgchart node",
      "description": "Get a single orgchart node by ID",
      "resource": "orgchart"
    },
    {
      "name": "Get Children",
      "value": "getChildren",
      "action": "Get children of an orgchart node",
      "description": "Get all children/descendants of an orgchart node by ID",
      "resource": "orgchart"
    },
    {
      "name": "Get Parent",
      "value": "getParent",
      "action": "Get parent of an orgchart node",
      "description": "Get the parent of an orgchart node by ID",
      "resource": "orgchart"
    },
    {
      "name": "Get Signed URL",
      "value": "getSignedUrl",
      "action": "Get a signed URL for a stored file",
      "description": "Generate a pre-signed URL for secure access to a stored file",
      "resource": "storage"
    },
    {
      "name": "Approve Task",
      "value": "approveTask",
      "action": "Approve a task",
      "description": "Approve a pending task",
      "resource": "task"
    },
    {
      "name": "Create Task Template",
      "value": "createTask",
      "action": "Create a task template",
      "description": "Create a new task template",
      "resource": "task"
    },
    {
      "name": "Delete Task Template",
      "value": "deleteTask",
      "action": "Delete a task template",
      "description": "Delete a task template by ID",
      "resource": "task"
    },
    {
      "name": "Get Task",
      "value": "getTaskInstance",
      "action": "Get a task by ID",
      "description": "Fetch a task instance by its ID",
      "resource": "task"
    },
    {
      "name": "Get Task Template by ID",
      "value": "getTask",
      "action": "Get a task template by ID",
      "description": "Fetch a task template by its ID",
      "resource": "task"
    },
    {
      "name": "Get Task Template by Task",
      "value": "getTemplateByTask",
      "action": "Get the task template for a given task",
      "description": "Fetch the task template that a specific task was created from",
      "resource": "task"
    },
    {
      "name": "Get Task Template Progress",
      "value": "getTaskProgress",
      "action": "Get progress for a task template",
      "description": "Get completion progress for a task template",
      "resource": "task"
    },
    {
      "name": "Get Task Template Status Counts",
      "value": "getTaskStatusCounts",
      "action": "Get status counts for a task template",
      "description": "Get status distribution counts for a task template",
      "resource": "task"
    },
    {
      "name": "Get Task Templates by Orgunit",
      "value": "getTaskByOrgunit",
      "action": "Get task templates by orgunit",
      "description": "Find all task templates for a specific organization unit",
      "resource": "task"
    },
    {
      "name": "Reject Task",
      "value": "rejectTask",
      "action": "Reject a task",
      "description": "Reject a pending task with a reason",
      "resource": "task"
    },
    {
      "name": "Get by ID",
      "value": "getById",
      "action": "Get a user by ID",
      "description": "Retrieve a user by their unique ID",
      "resource": "user"
    },
    {
      "name": "Find by Login Name",
      "value": "findByLoginName",
      "action": "Find a user by login name",
      "description": "Retrieve a user by their Login Name",
      "resource": "user"
    },
    {
      "name": "Find by Group",
      "value": "findByGroup",
      "action": "Find a user by group",
      "description": "Find all users in a specific group",
      "resource": "user"
    },
    {
      "name": "Find by Orgunit",
      "value": "findByOrgunit",
      "action": "Find a user by orgunit",
      "description": "Find all users in a specific organization unit",
      "resource": "user"
    }
  ]

npm_readme:
  # 🚀 n8n-nodes-keephub

  <div align="center">
    <img src="https://github.com/RetailInTouch/n8n-nodes-keephub/blob/master/assets/Keephub%20Banner.png?raw=true" alt="Keephub Banner" width="50%" height="auto">
  </div>



  <div align="center">

  [![npm version](https://img.shields.io/npm/v/n8n-nodes-keephub.svg?style=flat-square)](https://www.npmjs.com/package/n8n-nodes-keephub)
  [![npm downloads](https://img.shields.io/npm/dm/n8n-nodes-keephub.svg?style=flat-square)](https://www.npmjs.com/package/n8n-nodes-keephub)
  [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](./LICENSE.md)
  [![Maintenance](https://img.shields.io/badge/maintained%3F-yes-green.svg?style=flat-square)](https://github.com/RetailInTouch/n8n-nodes-keephub)

  **Seamlessly integrate Keephub with your n8n workflows** 🎯

  Unlock the power of employee engagement and communication automation with this comprehensive n8n community node for Keephub.

  [🔧 Installation](#-installation) • [📚 Documentation](#-operations) • [🚀 Quick Start](#-quick-start) • [📝 Changelog](#-version-history) • [🤝 Contributing](#-contributing)

  </div>

  ---

  ## 📖 About

  This is a professional **n8n community node** that enables you to harness the full power of **Keephub** within your workflow automation.

  **Keephub** is an enterprise-grade employee engagement platform for managing:

  - 👥 User management and organizational structures
  - 📰 Content creation and distribution
  - ✅ Task management and templates
  - 📋 Dynamic form submissions and responses

  **n8n** is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform that puts automation in the hands of technical and business users.

  ---

  ## 🔧 Installation

  ### 📦 Community Nodes Method (Recommended)

  1. Open your n8n instance
  2. Navigate to **Settings** ⚙️ → **Community Nodes**
  3. Click **Install a community node**
  4. Enter: `n8n-nodes-keephub`
  5. Click **Install**
  6. ✅ Done! The node is ready to use

  ### 🛠️ Manual Installation

  **For Local n8n:**

  ```bash
  cd ~/.n8n/nodes
  npm install n8n-nodes-keephub
  ```

  **For Docker:**

  ```bash
  docker exec -it <n8n-container> sh
  cd /home/node/.n8n/nodes
  npm install n8n-nodes-keephub
  # Restart your container
  ```

  **For Node.js n8n:**

  ```bash
  npm install -g n8n-nodes-keephub
  ```

  > [!TIP]
  > Restart your n8n instance and the Keephub node will appear in your palette! 🎨

  ---

  ## 🚀 Quick Start

  ### 1️⃣ Set Up Credentials

  Keephub supports **two separate credential types**. Choose the one that matches your setup:

  **Option A — Bearer Token** (recommended for API integrations):
  1. In n8n, go to **Credentials** 🔐
  2. Create **New** → Search for **Keephub Bearer API**
  3. Fill in:
     - **Client URL**: `https://yourcompany.keephub.io`
     - **Bearer Token**: Your API token
     - **Language** (optional): Default is `en`
  4. Test & Save ✔️

  **Option B — Login Credentials** (username & password):
  1. In n8n, go to **Credentials** 🔐
  2. Create **New** → Search for **Keephub Login API**
  3. Fill in:
     - **Client URL**: `https://yourcompany.keephub.io`
     - **Login Name**: Your username
     - **Password**: Your password
     - **Auth Endpoint** (optional): Custom auth endpoint path
     - **Language** (optional): Default is `en`
  4. Test & Save ✔️

  ### 2️⃣ Add the Node to Your Workflow

  1. Click **+** to add a node
  2. Search for **Keephub**
  3. Select your **Authentication** method (Bearer Token or Login Credentials)
  4. Select your resource and operation
  5. Configure parameters
  6. Run! 🏃

  ### 3️⃣ Example: Get User Info

  ```
  Keephub Node Configuration:
  ├── Resource: User
  ├── Operation: Find by Login Name
  └── Login Name: john.doe

  Output:
  {
    "id": "63bd885034d0466d11073575",
    "loginName": "john.doe",
    "firstName": "John",
    "lastName": "Doe",
    "email": "john.doe@company.com"
  }
  ```

  ---

  ## 📚 Operations

  ### 👥 **User Operations**

  | Operation                 | Description                              |
  | ------------------------- | ---------------------------------------- |
  | 🆔 **Get by ID**          | Retrieve a user by their unique ID       |
  | 🔍 **Find by Login Name** | Search users by login name               |
  | 👨‍💼 **Find by Group**      | Fetch all users in a specific group      |
  | 🏢 **Find by Orgunit**    | Retrieve users from an organization unit |

  **Example:**

  ```javascript
  // Get all users in a group
  {
    "resource": "user",
    "operation": "findByGroup",
    "groupId": "group_12345"
  }
  ```

  ---

  ### 📰 **Content Operations**

  | Operation                   | Description                                            |
  | --------------------------- | ------------------------------------------------------ |
  | ✅ **Approve Content**      | Approve content that is pending approval               |
  | ✨ **Create**               | Create new content (news, forms, manuals, etc.)        |
  | 🗑️ **Delete**               | Remove content                                         |
  | 📁 **Find by Content Pool** | Filter content by pool with optional sorting           |
  | 🏷️ **Find by Group**        | Get content assigned to groups with optional sorting   |
  | 🏢 **Find by Orgunit**      | Retrieve content by organization with optional sorting |
  | 📖 **Get by ID**            | Retrieve specific content                              |
  | ❌ **Reject Content**       | Reject content that is pending approval                |
  | ✏️ **Update by ID**         | Modify existing content                                |

  **Example - Create Content:**

  ```json
  {
  	"resource": "content",
  	"operation": "create",
  	"defineContentInput": "json",
  	"contentBody": {
  		"originLanguage": "en",
  		"contentType": "news",
  		"contentPool": "POOL_ID",
  		"title": { "en": "🎉 Company Announcement" },
  		"message": { "en": "<p>Great news everyone!</p>" },
  		"orgchartSelection": { "include": ["root0001"], "exclude": [] }
  	}
  }
  ```

  **Example - Find Content by Orgunit with Filtering:**

  ```json
  {
  	"resource": "content",
  	"operation": "findByOrgunit",
  	"orgunitId": "root0077",
  	"options": {
  		"limit": 50,
  		"skip": 0,
  		"sortBy": "createdAt",
  		"sortOrder": 1
  	}
  }
  ```

  **Content Filtering Parameters:**

  - **Orgunit ID** (required): The organization unit ID

  **Options** (all optional):

  - **Limit**: Maximum number of results (default: 50)
  - **Skip**: Number of results to skip (pagination)
  - **Sort Field**: Field to sort by (e.g., `createdAt`, `updatedAt`)
  - **Sort Order**: `1` for ascending, `-1` for descending

  ---

  ### ✅ **Task Template Operations**

  | Operation                                  | Description                                                            |
  | ------------------------------------------ | ---------------------------------------------------------------------- |
  | ✅ **Approve Task**                     | Approve a pending task, with an optional comment              |
  | ➕ **Create Task Template**              | Create a new task template                                             |
  | 🗑️ **Delete Task Template**              | Remove a task template                                                 |
  | 📋 **Get Task**                          | Fetch a task instance by its ID                                        |
  | 📋 **Get Task Template by ID**           | Retrieve a task template by its own ID                                 |
  | 🔍 **Get Task Template by Task**         | Fetch the template a specific task instance was created from           |
  | 🔍 **Get Task Templates by Orgunit**     | Fetch task templates by organization unit with filtering & pagination  |
  | 📊 **Get Task Template Progress**        | Check task template progress                                           |
  | 📈 **Get Task Template Status Counts**   | View task template completion statistics                               |
  | ❌ **Reject Task**                      | Reject a pending task with a required reason                           |

  **Example - Create Task with JSON Body:**

  ```json
  {
  	"resource": "task",
  	"operation": "createTask",
  	"defineTaskInput": "json",
  	"taskJsonBody": {
  		"title": { "en": "Q4 Performance Review" },
  		"template": {
  			"form": {
  				"fields": [
  					{ "name": "rating", "type": "number" },
  					{ "name": "feedback", "type": "text" }
  				]
  			}
  		}
  	}
  }
  ```

  **Example - Create Task with Additional Fields:**

  ```json
  {
  	"resource": "task",
  	"operation": "createTask",
  	"defineTaskInput": "fields",
  	"taskTitle": "Q4 Performance Review",
  	"taskType": "form",
  	"additionalFields": {
  		"taskMessage": "Please complete your review by end of quarter.",
  		"taskNotification": true
  	}
  }
  ```

  **Example - Get Tasks by Orgunit with Filtering:**

  ```json
  {
  	"resource": "task",
  	"operation": "getTaskByOrgunit",
  	"orgunitId": "root0077",
  	"options": {
  		"limit": 50,
  		"skip": 0,
  		"sortBy": "template.dueDate",
  		"sortOrder": 1,
  		"startDateGte": "2025-11-01T00:00:00Z",
  		"startDateLte": "2025-11-30T23:59:59Z"
  	}
  }
  ```

  **Task Template Create Parameters:**

  - **Task Title** (required): Name of the task template
  - **Task Type** (required): `form`, `confirmation`, or `read`

  **Additional Fields** (optional):

  - **Message**: Custom message body for the task template
  - **Send Notification**: Whether to notify assigned users (`true`/`false`)

  **Task Template Get by Orgunit Parameters:**

  - **Orgunit ID** (required): The organization unit ID to filter task templates

  **Options** (all optional):

  - **Limit**: Maximum number of results (default: 50)
  - **Skip**: Number of results to skip (pagination)
  - **Sort Field**: Field to sort by (e.g., `template.dueDate`)
  - **Sort Order**: `1` for ascending, `-1` for descending
  - **Start Date After**: Filter tasks after this date
  - **Start Date Before**: Filter tasks before this date

  ---

  ### 📋 **Form Submission Operations**

  | Operation                          | Description                                       |
  | ---------------------------------- | ------------------------------------------------- |
  | 🔍 **Find by Form**               | Retrieve all submissions for a form with filtering |
  | 📥 **Get**                         | Fetch complete form submission data               |
  | 👤 **Get Submitter Details**       | Retrieve full user profile of submitter           |
  | 🏢 **Get Submission Orgunits**     | View orgunit hierarchy                            |
  | 📍 **Update Submission Orgunits**  | Change visibility by orgunit                      |
  | ⏱️ **Calculate Response Duration** | Time from creation to submission                  |

  **Example - Find Submissions by Form:**

  ```json
  {
  	"resource": "formSubmission",
  	"operation": "findByForm",
  	"contentRef": "699848533ab62d9d50409890",
  	"options": {
  		"limit": 50,
  		"skip": 0,
  		"sortBy": "updatedAt",
  		"sortOrder": -1
  	}
  }
  ```

  **Find by Form Parameters:**

  - **Form Content ID** (required): The content ID of the form (24-character MongoDB ObjectID)

  **Options** (all optional):

  - **Limit**: Maximum number of results (default: 50)
  - **Skip**: Number of results to skip (pagination)
  - **Sort Field**: Field to sort by (default: `updatedAt`)
  - **Sort Order**: `-1` for descending (newest first), `1` for ascending

  **Example - Calculate Response Time:**

  ```javascript
  {
    "resource": "formSubmission",
    "operation": "calculateResponseDuration",
    "formSubmissionId": "form_67890"
  }

  // Returns:
  {
    "duration": {
      "days": 2,
      "hours": 5,
      "minutes": 30,
      "totalSeconds": 183930
    }
  }
  ```

  ---

  ### 🏢 **Orgchart Operations**

  | Operation                   | Description                                        |
  | --------------------------- | -------------------------------------------------- |
  | 🌳 **Get Ancestors**        | Get all ancestors in the org hierarchy             |
  | 🔗 **Get by External Ref**  | Retrieve an orgchart node by its externalRef value |
  | 🆔 **Get by ID**            | Retrieve an orgchart node by ID                    |
  | 👶 **Get Children**         | Retrieve all children/descendants                  |
  | ⬆️ **Get Parent**            | Fetch the parent node of an orgchart node          |

  **Example - Get Children with Depth Limit:**

  ```json
  {
  	"resource": "orgchart",
  	"operation": "getChildren",
  	"orgunitId": "node123",
  	"additionalFields": {
  		"limit": 100
  	}
  }
  ```

  **Example - Get Ancestors with Depth Limit:**

  ```json
  {
  	"resource": "orgchart",
  	"operation": "getAncestors",
  	"orgunitId": "node123",
  	"additionalFields": {
  		"depthLimit": 3
  	}
  }
  ```

  **Optional Additional Fields:**

  - **Get Children** → `limit`: Maximum number of child nodes to return (default: 0 = unlimited)
  - **Get Ancestors** → `depthLimit`: How many levels up to traverse (default: 0 = unlimited)

  ---

  ### 🗄️ **Storage Operations**

  | Operation           | Description                                               |
  | ------------------- | --------------------------------------------------------- |
  | 🔗 **Get Signed URL** | Generate a pre-signed URL for secure access to a stored file |

  **Use case:** Retrieve a signed CloudFront URL for a file attached to a task, form answer, or content item, then pass it directly to an AI vision model for analysis.

  **Required fields:**

  | Field | Description |
  | ----- | ----------- |
  | **Origin** | The entity type that owns the file (e.g. `Task Form Answer`, `Content Attachment`) |
  | **Origin ID** | The `_id` of the owning entity — use `{{ $json._id }}` from a preceding Keephub node |
  | **Storage ID** | The S3 path of the file — use `{{ $json.answers[N].value.value }}` for upload form answers |
  | **Force Download** | Optional. Sets `Content-Disposition: attachment` to force a file download instead of inline display |

  **Example:**

  ```json
  {
  	"resource": "storage",
  	"operation": "getSignedUrl",
  	"origin": "taskFormAnswer",
  	"originId": "699974e8f6c386b2e3e93cbc",
  	"storageId": "live/image/dev/contents/abc123/photo.jpg"
  }
  ```

  **Output:**

  ```json
  {
  	"url": "https://d2qp115j4w1ptn.cloudfront.net/live/image/...?Expires=...&Signature=..."
  }
  ```

  > ⚠️ The returned URL is time-limited and contains embedded credentials. Treat it as a secret and use it immediately in your workflow — do not log or store it.

  ---

  ## 🔐 Credentials Setup

  Keephub uses **two separate credential types** — select the matching authentication method from the **Authentication** dropdown on the Keephub node.

  ### 🔑 Keephub Bearer API

  ```
  ✓ Most secure for API integrations
  ✓ Use existing API tokens from Keephub
  ✓ Perfect for server-to-server communication
  ✓ Uses IAuthenticateGeneric — token sent automatically in headers
  ```

  | Field          | Description                          |
  | -------------- | ------------------------------------ |
  | **Client URL** | Your Keephub instance base URL       |
  | **Bearer Token** | API bearer token                   |
  | **Language**   | Language code (default: `en`)        |

  ### 🔒 Keephub Login API

  ```
  ✓ Automatic token generation from username/password
  ✓ Simple to set up
  ✓ Token refreshed dynamically per request
  ✓ Credentials securely stored and encrypted in n8n
  ```

  | Field              | Description                                     |
  | ------------------ | ----------------------------------------------- |
  | **Client URL**     | Your Keephub instance base URL                  |
  | **Login Name**     | Username for authentication                     |
  | **Password**       | Password for authentication                     |
  | **Auth Endpoint**  | Custom auth path (default: `/authentication`)   |
  | **Language**       | Language code (default: `en`)                   |

  **All credentials are encrypted** 🔒 and never exposed in logs or workflows.

  ---

  ## 💡 Real-World Examples

  ### 📧 Example 1: Auto-Create Tasks from Email

  ```
  Gmail Trigger
    ↓
  Extract email data
    ↓
  Keephub: Create Task
    ↓
  Send confirmation email
  ```

  ### 📊 Example 2: Sync Users to Slack

  ```
  Keephub: Get all users in a group
    ↓
  Filter active users
    ↓
  Slack: Create channels per active users in group
  ```

  ### 📋 Example 3: Form Response Automation

  ```
  Schedule Trigger (daily)
    ↓
  Keephub: Get Form Submission
    ↓
  Keephub: Get Submitter Details
    ↓
  Keephub: Calculate Response Duration
    ↓
  Store in database
  ```

  ---

  ## ⚙️ Node Configuration

  ### Authentication

  The Keephub node provides an **Authentication** dropdown at the top of the configuration panel:

  - **Bearer Token** → uses the **Keephub Bearer API** credential
  - **Login Credentials** → uses the **Keephub Login API** credential

  ### Input Data

  - All parameters support dynamic expressions with `{{ }}`
  - Use previous node outputs: `{{ $node["Previous Node"].json.field }}`
  - Access environment variables: `{{ $env.MY_VAR }}`

  ### Output Format

  ```javascript
  {
    "pairedItem": { "item": 0 },
    "json": {
      // API response data
    }
  }
  ```

  ### Error Handling

  Enable "Continue on Error" to handle failures gracefully in your workflow.

  ---

  ## 📦 Requirements

  | Requirement | Version  |
  | ----------- | -------- |
  | **n8n**     | v1.0.0+  |
  | **Node.js** | 18.17.0+ |
  | **npm**     | 8.0.0+   |

  ---

  ## 🐛 Troubleshooting

  ### ❌ "Authentication failed"

  - ✅ Verify your Keephub instance URL
  - ✅ Check API credentials are correct
  - ✅ Ensure credentials have required permissions

  ### ❌ "Unknown operation"

  - ✅ Verify resource and operation combination exist
  - ✅ Check node version is latest
  - ✅ Try refreshing the node palette

  ### ❌ "Connection timeout"

  - ✅ Check network connectivity
  - ✅ Verify firewall allows outbound HTTPS
  - ✅ Check Keephub instance is accessible

  ---

  ## 📚 Documentation

  - 📖 [n8n Documentation](https://docs.n8n.io/)
  - 🔗 [Keephub API Docs](https://dev.api.keephub.io/api-docs/)
  - 💬 [n8n Community Forum](https://community.n8n.io/)

  ---

  ## 🏗️ Project Structure

  ```
  n8n-nodes-keephub/
  ├── nodes/
  │   └── Keephub/
  │       ├── Keephub.node.ts           # Main node class
  │       ├── descriptions/             # Field definitions
  │       │   ├── UserDescription.ts
  │       │   ├── ContentDescription.ts
  │       │   ├── TaskDescription.ts
  │       │   ├── FormSubmissionDescription.ts
  │       │   ├── OrgchartDescription.ts
  │       │   └── StorageDescription.ts
  │       ├── actions/                  # Operation implementations
  │       │   ├── user/
  │       │   ├── content/
  │       │   ├── task/
  │       │   ├── formSubmission/
  │       │   ├── orgchart/
  │       │   └── storage/
  │       └── utils/
  │           └── helpers.ts
  ├── credentials/
  │   ├── KeephubBearerApi.credentials.ts
  │   └── KeephubLoginApi.credentials.ts
  ├── package.json
  └── README.md
  ```

  ---

  ## 🚀 Development

  ### Build

  ```bash
  npm run build
  ```

  ### Verify (lint + build)

  ```bash
  npm run test
  ```

  ### Lint

  ```bash
  npm run lint
  ```

  ---

  ## 📝 Version History

  ### v1.0.0 (2025-01-09) 🎉

  - ✨ Initial release
  - 👥 User management operations
  - 📰 Content creation & management
  - ✅ Task template operations
  - 📋 Form submission handling
  - 🔐 Secure API authentication

  ### v1.1.0 (2025-11-10) 📦

  - 📊 Added Orgchart operations (Get, Parent, Ancestors, Children)
  - 🧹 Fixed console.log in updateById operation
  - 🔧 Code cleanup and optimizations

  ### v1.2.0 (2025-11-12) 🆕

  - 🔍 Added Get By Orgunit task operation
  - 📅 Date range filtering support for tasks (Start Date Before/After)
  - 📰 Enhanced Content filtering

  ### v1.2.1 (2025-11-20)

  - 📖 README updates and documentation improvements

  ### v1.2.2 (2025-11-20)

  - 🧹 Build process improvements (added dist folder cleanup script)

  ### v1.3.0 (2025-12-04)

  - 🔍 Added Get by External Ref operation to Orgchart resource for querying nodes by external reference

  ### v1.3.4 (2025-12-08)

  - 🔧 Added externalRef of orgunit to the output of the Get submission orgunits node
  - 🧹 Fixed typo that was causing README rendering issues

  ### v1.4.0 (2026-02-09) 🔐

  - 🔑 **Split credentials** into two separate types: **Keephub Bearer API** and **Keephub Login API**
  - 🛡️ Added `IAuthenticateGeneric` to Bearer credential for automatic header injection
  - 🔀 Added **Authentication** selector dropdown to the node UI (Bearer Token / Login Credentials)
  - 🔌 Updated `inputs`/`outputs` to use `NodeConnectionTypes.Main` (n8n best practice)
  - 📦 Moved non-required fields into **Additional Fields** / **Options** collections:
    - Content: Limit, Skip, Sort Field, Sort Order → Options
    - Orgchart: Depth Limit, Result Limit → Additional Fields
    - Task: Message, Send Notification → Additional Fields; Limit → Options
  - 🧹 Updated helpers to dynamically resolve credentials based on authentication selector
  - 📖 Updated README documentation to reflect all changes

  ### v1.4.3 (2026-02-19)

  - 🖼️ Changed Image assets to better match n8n's dark theme


  ### v1.4.4 (2026-02-19)

  - 🐛 Empty content/task searches now return `[]` instead of throwing error (`findByContentPool`, `findByGroup`, `findByOrgunit`, `task/getByOrgunit`)
  - 🐛 Orgchart node IDs now properly URL-encoded (`getById`, `getParent`, `getAncestors`)
  - 🐛 Added null check for submitter ID in form submissions (`getSubmitterDetails`)
  - 🐛 Fixed double indentation in content delete operation
  - 🐛 Fixed paginated response unwrapping in user `findByLoginName`, `findByGroup`, `findByOrgunit` — now correctly returns individual user items instead of the raw envelope
  - 📋 Added codex metadata with expanded aliases for better node discoverability
  - 🔍 Expanded package keywords from 4 to 19 for better discoverability
  - 🖼️ Optimized icon SVG viewBox to show full logo without cutoff
  - 🖼️ Restored white ellipse border for visibility in dark mode
  - 🔐 Improved credential test behavior for both auth methods:
  	- Bearer credential test now handles both client URL and API subdomain URL formats (strips `.api.` for compatibility)
  	- Login credential test now performs real auth validation via `POST` to the configured auth endpoint (uses string manipulation compatible with n8n's expression sandbox)
  	- Both use protocol normalization to handle URLs without `https://` prefix
  - 🧩 Renamed Login credential field from **Token Endpoint** to **Auth Endpoint** for clarity (runtime keeps backward compatibility with legacy `tokenEndpoint`)
  - 📚 Fixed metadata/docs pointers:
  	- Updated node credential documentation anchor link
  	- Fixed README MIT badge link target
  	- Updated clientUrl field descriptions to clarify "Do not use the API URL"
  - 📦 Added `prepublishOnly` guard (`npm run test`) and aligned package Node engine requirement to `>=18.17.0`
  - 🔗 Enhanced URL transformation robustness:
  	- Client URL now auto-normalizes `.api.` subdomain (handles both formats transparently)
  	- Protocol normalization adds `https://` if missing

  ### v1.5.0 (2026-02-20) 📋

  - 🔍 Added **Find by Form** operation to Form Submission resource — retrieve all submissions for a given form with pagination & sorting
  - 📦 Supports Limit, Skip, Sort Field, Sort Order options (consistent with Content and Task list operations)
  - 🔗 Uses `$sort[_id]=1` tiebreaker for stable pagination across pages

  ### v1.6.0 (2026-02-22)

  - ✅ Added **Approve Task** operation — approve a pending task with an optional comment
  - ❌ Added **Reject Task** operation — reject a pending task with a required reason
  - ✅ Added **Approve Content** operation — approve content pending approval
  - ❌ Added **Reject Content** operation — reject content pending approval

  ### v1.8.0 (2026-02-23)

  - 🗄️ Added **Storage** resource with **Get Signed URL** operation — generates pre-signed CloudFront URLs for secure file access from tasks, form answers, and content
  - Supports all 11 origin types with inline field hints for common expressions; optional Force Download flag
  - 🏷️ Renamed **Task** resource display label to **Task Template** for clarity across all template-managing operations (Create, Delete, Get by ID, Get by Orgunit, Get Progress, Get Status Counts)
  - **Approve Task** and **Reject Task** intentionally kept as-is — they operate on task instances, not templates; no breaking changes
  - 🔍 Added **Get Task Template by Task** operation — resolves a task template from a task instance ID via `/tasks/{id}` → `/tasktemplates/{templateRef}`
  - 📋 Added **Get Task** operation — fetches a task instance directly by its ID from `/tasks/{id}`

  ### v1.8.1 (2026-02-23)

  - 🐛 Fixed missing `description` property on six Task Template operations (Create, Delete, Get by ID, Progress, Status Counts, By Orgunit) — these were not visible on the [n8n integrations page](https://n8n.io/integrations/keephub/)

  ---

  ## 🤝 Contributing

  Contributions are welcome! 🙌

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

  ### Development Setup

  ```bash
  git clone https://github.com/RetailInTouch/n8n-nodes-keephub.git
  cd n8n-nodes-keephub
  npm install
  npm run build
  ```

  ---

  ## 📄 License

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

  ---

  ## 🙏 Support

  Found a bug? Have a feature request?

  - 🐛 [Open an Issue](https://github.com/RetailInTouch/n8n-nodes-keephub/issues)
  - 💬 [Start a Discussion](https://github.com/RetailInTouch/n8n-nodes-keephub/discussions)

  ---

  ## ⭐ Show Your Support

  If you find this node useful, please consider:

  - ⭐ Starring this repository
  - 🐦 Sharing it on social media
  - 📢 Recommending it to the community

  ---

  <div align="center">

  **Made with ❤️ for the automation community**

  [Visit n8n](https://n8n.io) • [View on npm](https://www.npmjs.com/package/n8n-nodes-keephub) • [GitHub Repo](https://github.com/RetailInTouch/n8n-nodes-keephub)

  </div>

ai_documentation_summary:
  {"purpose":"This n8n community node enables automation of Keephub API operations for employee engagement, including user management, content creation, and task handling.","capabilities":["Retrieve users by ID, login name, group, or organization unit","Create, update, delete, and approve/reject content","Filter content by content pool, group, or organization unit","Manage organizational structures and user groups","Support for both Bearer Token and Login Credentials authentication"],"authentication":"API Key (Bearer Token) or Basic Auth (Username/Password)","commonUseCases":["Automated employee onboarding and user provisioning","Internal communication and announcement distribution","Content approval workflows for company news and manuals","Organizational structure updates and group management"],"limitations":["Requires manual installation via npm for local/Node.js n8n instances","Requires separate credential setup for Bearer Token vs Login methods","Some advanced operations may require specific Keephub platform permissions not detailed in README"],"relatedNodes":["n8n-nodes-keephub.keephub","n8n-nodes-keephub.keephub-user","n8n-nodes-keephub.keephub-content"]}


node_type: n8n-nodes-pagepixels-screenshots.pagePixelsScreenshots
package_name: n8n-nodes-pagepixels-screenshots
display_name: PagePixels Screenshots
description: Capture website screenshots using the PagePixels API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: pagepixels
author_github_url: https://github.com/PagePixels/
npm_package_name: n8n-nodes-pagepixels-screenshots
npm_version: 1.0.0
community_fetched_at: 2026-07-14T14:40:33.518Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "screenshot",
      "options": [
        {
          "name": "Screenshot",
          "value": "screenshot"
        },
        {
          "name": "Metadata",
          "value": "metadata"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "quickSnap",
      "options": [
        {
          "name": "AI Analysis Screenshot",
          "value": "aiAnalysisScreenshot",
          "action": "Take a screenshot and analyze it with AI",
          "routing": {
            "request": {
              "qs": {
                "analyze_image_with_ai": true
              },
              "url": "/snap",
              "method": "POST"
            }
          },
          "description": "Capture a web page screenshot and run AI analysis with a custom prompt"
        },
        {
          "name": "Quick Snap",
          "value": "quickSnap",
          "action": "Take a screenshot of a web page",
          "routing": {
            "request": {
              "url": "/snap",
              "method": "POST"
            }
          },
          "description": "Capture an instant screenshot from a URL"
        },
        {
          "name": "Real Location Screenshot",
          "value": "realLocationScreenshot",
          "action": "Capture a screenshot from a real geographic location",
          "routing": {
            "request": {
              "url": "/snap",
              "method": "POST"
            }
          },
          "description": "Capture screenshots using residential IPs within a chosen location"
        },
        {
          "name": "Snap HTML",
          "value": "snapHtml",
          "action": "Take a screenshot of custom HTML",
          "routing": {
            "request": {
              "url": "/snap_html",
              "method": "POST"
            }
          },
          "description": "Render supplied HTML and capture it as an image"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "screenshot"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "listRealLocations",
      "options": [
        {
          "name": "List Real Locations",
          "value": "listRealLocations",
          "action": "List real locations",
          "routing": {
            "request": {
              "url": "/real_locations",
              "method": "GET"
            }
          },
          "description": "Retrieve the available residential proxy locations"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "metadata"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "AI Analysis Screenshot",
      "value": "aiAnalysisScreenshot",
      "action": "Take a screenshot and analyze it with AI",
      "routing": {
        "request": {
          "qs": {
            "analyze_image_with_ai": true
          },
          "url": "/snap",
          "method": "POST"
        }
      },
      "description": "Capture a web page screenshot and run AI analysis with a custom prompt",
      "resource": "screenshot"
    },
    {
      "name": "Quick Snap",
      "value": "quickSnap",
      "action": "Take a screenshot of a web page",
      "routing": {
        "request": {
          "url": "/snap",
          "method": "POST"
        }
      },
      "description": "Capture an instant screenshot from a URL",
      "resource": "screenshot"
    },
    {
      "name": "Real Location Screenshot",
      "value": "realLocationScreenshot",
      "action": "Capture a screenshot from a real geographic location",
      "routing": {
        "request": {
          "url": "/snap",
          "method": "POST"
        }
      },
      "description": "Capture screenshots using residential IPs within a chosen location",
      "resource": "screenshot"
    },
    {
      "name": "Snap HTML",
      "value": "snapHtml",
      "action": "Take a screenshot of custom HTML",
      "routing": {
        "request": {
          "url": "/snap_html",
          "method": "POST"
        }
      },
      "description": "Render supplied HTML and capture it as an image",
      "resource": "screenshot"
    },
    {
      "name": "List Real Locations",
      "value": "listRealLocations",
      "action": "List real locations",
      "routing": {
        "request": {
          "url": "/real_locations",
          "method": "GET"
        }
      },
      "description": "Retrieve the available residential proxy locations",
      "resource": "metadata"
    }
  ]

npm_readme:
  # n8n-nodes-pagepixels-screenshots

  This n8n community node lets you trigger the PagePixels Screenshots API directly from your workflows. Capture high-fidelity website screenshots, render custom HTML, request real location captures (from countries and states around the world), and pair each image with AI-powered analysis, no custom HTTP node required.

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

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

  ## Installation

  Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation, then install this package with:

  ```bash
  npm install n8n-nodes-pagepixels-screenshots
  ```

  Restart n8n and the node will appear in the editor.

  ## Operations

  All operations return the raw PagePixels response, including the screenshot asset URLs, embed links, job IDs, and AI metadata when available.

  - **Quick Snap** – Capture an instant screenshot from any public URL.
  - **Snap HTML** – Render supplied HTML (with optional CSS/JS injection) and capture the output.
  - **Real Location Screenshot** – Capture a page through PagePixels’ residential proxy network (countries, regions, and major cities).
  - **AI Analysis Screenshot** – Take a screenshot and run an AI visual analysis using your custom prompt.

  Each operation exposes the full set of optional PagePixels parameters — viewport size, wait conditions, multi-step actions, ad/cookie blocking, geolocation overrides, headers, cookies, caching, and more.

  ## Credentials

  Configure one of the two supported authentication strategies in n8n.

  ### API key

  1. Sign in to [PagePixels](https://pagepixels.com) and create an API key in the dashboard.
  2. Add the **PagePixels API** credential in n8n and paste the key.
  3. Leave “Use Bearer Prefix” enabled unless your key must be sent in plain text.
  4. (Optional) Provide extra headers in the node if you need to override the default `Authorization: Bearer` header.

  ### OAuth2

  1. Register an OAuth app by contacting support for production credentials.
  2. In n8n, create a **PagePixels OAuth2 API** credential.
  3. Use `https://pagepixels.com/oauth/authorize` as the authorization URL and `https://pagepixels.com/oauth/token` as the access token URL (defaults are pre-filled).
  4. Enter your client ID and client secret, then complete the OAuth handshake when prompted in a workflow.

  ## Compatibility

  Compatible with n8n v1.60.0 or later.

  ## Usage

  1. Drop the **PagePixels Screenshots** node into your workflow and select the desired operation.
  2. Choose the authentication method and credential.
  3. Provide the required fields (`URL`, `HTML Content`, `AI Prompt`, or `Proxy Location`, depending on the operation).
  4. Expand **Options** to set advanced rendering controls (multi-step actions, viewport, selectors, blocking flags, etc.).
  5. Execute the workflow to receive the screenshot metadata, including direct download links and analysis output.

  Tip: Chain the output into an **HTTP Request**, **Webhook**, or **Drive** node to archive images, notify teams, or trigger downstream automations.

  ## Resources

  - [PagePixels Screenshots API documentation](https://pagepixels.com/app/screenshots-api-documentation)
  - [PagePixels support](https://pagepixels.com/support)
  - [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)

ai_documentation_summary:
  {"purpose":"Captures high-fidelity website screenshots, renders custom HTML, performs real-world location captures, and generates AI-powered visual analysis directly from n8n workflows.","capabilities":["Quick Snap: Capture screenshots from public URLs","Snap HTML: Render custom HTML with optional CSS/JS injection","Real Location Screenshot: Capture pages via residential proxy network from specific countries/regions","AI Analysis Screenshot: Generate screenshots with custom AI visual analysis prompts","Custom viewport and size control","Multi-step actions and selectors","Ad and cookie blocking","Geolocation overrides","Custom headers and cookies","Caching support"],"authentication":"API key or OAuth2","commonUseCases":["Automated website monitoring and archiving","Product page visual analysis for e-commerce","Competitor landscape tracking with location-specific views","Generating visual reports with AI insights","Automated content rendering and testing"],"limitations":["Community node (not core n8n)","Requires external API key or OAuth2 setup","Requires n8n v1.60.0 or later","Real location captures require residential proxy network access"],"relatedNodes":["HTTP Request","Webhook","Google Drive","File Node"]}


node_type: n8n-nodes-reachinbox.reachInbox
package_name: n8n-nodes-reachinbox
display_name: ReachInbox
description: Interact with ReachInbox leads
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: lokesh1403
author_github_url: https://github.com/dsouzaalan/n8n-reachinbox.git
npm_package_name: n8n-nodes-reachinbox
npm_version: 0.1.12
community_fetched_at: 2026-07-14T14:40:33.519Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "lead",
      "options": [
        {
          "name": "Campaign",
          "value": "campaign",
          "description": "Interact with campaigns"
        },
        {
          "name": "Lead",
          "value": "lead",
          "description": "Interact with leads"
        },
        {
          "name": "Leads List",
          "value": "leadsList",
          "description": "Interact with leads lists"
        },
        {
          "name": "Blocklist",
          "value": "blocklist",
          "description": "Interact with blocklists"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "createCampaign",
      "options": [
        {
          "name": "Add Email Sequences to a Campaign",
          "value": "addSequences",
          "action": "Add sequences to campaign"
        },
        {
          "name": "Add Sending Email Accounts to a Campaign",
          "value": "addEmailToCampaign",
          "action": "Add email to campaign"
        },
        {
          "name": "Create a New Campaign",
          "value": "createCampaign",
          "action": "Create a new campaign"
        },
        {
          "name": "Get the Current Status of a Campaign",
          "value": "getCampaignStatus",
          "action": "Get campaign status"
        },
        {
          "name": "Pause a Campaign",
          "value": "pauseCampaign",
          "action": "Pause a campaign"
        },
        {
          "name": "Start a Campaign",
          "value": "startCampaign",
          "action": "Start a campaign"
        },
        {
          "name": "Update Campaign Details",
          "value": "updateCampaignDetails",
          "action": "Update campaign details"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "campaign"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "add",
      "options": [
        {
          "name": "Add a Lead to a Campaign",
          "value": "add",
          "action": "Add a lead to a campaign"
        },
        {
          "name": "Update a Specific Lead's Data",
          "value": "update",
          "action": "Update a specific lead data"
        },
        {
          "name": "Delete a Lead From a Campaign",
          "value": "delete",
          "action": "Delete a lead from a campaign"
        },
        {
          "name": "Pause or Resume Leads in a Campaign",
          "value": "changeLeadsState",
          "action": "Pause or resume leads"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "lead"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "addLeadToList",
      "options": [
        {
          "name": "Add a Lead to a Leads List",
          "value": "addLeadToList",
          "action": "Add a lead to a leads list"
        },
        {
          "name": "Remove a Lead From a Leads List",
          "value": "removeLeadFromList",
          "action": "Remove a lead from a leads list"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "leadsList"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "addToBlocklist",
      "options": [
        {
          "name": "Add Emails, Domains or Keywords to the Blocklist",
          "value": "addToBlocklist",
          "action": "Add to blocklist"
        },
        {
          "name": "Remove Emails, Domains or Keywords From the Blocklist",
          "value": "removeFromBlocklist",
          "action": "Remove from blocklist"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "blocklist"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Add Email Sequences to a Campaign",
      "value": "addSequences",
      "action": "Add sequences to campaign",
      "resource": "campaign"
    },
    {
      "name": "Add Sending Email Accounts to a Campaign",
      "value": "addEmailToCampaign",
      "action": "Add email to campaign",
      "resource": "campaign"
    },
    {
      "name": "Create a New Campaign",
      "value": "createCampaign",
      "action": "Create a new campaign",
      "resource": "campaign"
    },
    {
      "name": "Get the Current Status of a Campaign",
      "value": "getCampaignStatus",
      "action": "Get campaign status",
      "resource": "campaign"
    },
    {
      "name": "Pause a Campaign",
      "value": "pauseCampaign",
      "action": "Pause a campaign",
      "resource": "campaign"
    },
    {
      "name": "Start a Campaign",
      "value": "startCampaign",
      "action": "Start a campaign",
      "resource": "campaign"
    },
    {
      "name": "Update Campaign Details",
      "value": "updateCampaignDetails",
      "action": "Update campaign details",
      "resource": "campaign"
    },
    {
      "name": "Add a Lead to a Campaign",
      "value": "add",
      "action": "Add a lead to a campaign",
      "resource": "lead"
    },
    {
      "name": "Update a Specific Lead's Data",
      "value": "update",
      "action": "Update a specific lead data",
      "resource": "lead"
    },
    {
      "name": "Delete a Lead From a Campaign",
      "value": "delete",
      "action": "Delete a lead from a campaign",
      "resource": "lead"
    },
    {
      "name": "Pause or Resume Leads in a Campaign",
      "value": "changeLeadsState",
      "action": "Pause or resume leads",
      "resource": "lead"
    },
    {
      "name": "Add a Lead to a Leads List",
      "value": "addLeadToList",
      "action": "Add a lead to a leads list",
      "resource": "leadsList"
    },
    {
      "name": "Remove a Lead From a Leads List",
      "value": "removeLeadFromList",
      "action": "Remove a lead from a leads list",
      "resource": "leadsList"
    },
    {
      "name": "Add Emails, Domains or Keywords to the Blocklist",
      "value": "addToBlocklist",
      "action": "Add to blocklist",
      "resource": "blocklist"
    },
    {
      "name": "Remove Emails, Domains or Keywords From the Blocklist",
      "value": "removeFromBlocklist",
      "action": "Remove from blocklist",
      "resource": "blocklist"
    }
  ]

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

  # n8n-nodes-reachinbox

  This custom node allows users to connect [ReachInbox.ai](https://reachinbox.ai) with n8n, enabling workflows to be triggered by email events (e.g. email sent, reply received) and perform powerful actions like adding leads, updating their statuses, starting campaigns, and more.

  n8n is a fair-code licensed workflow automation platform.

  ---

  ## 📦 Installation

  Follow the official [n8n custom nodes installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) to install this package.

  ```bash
  npm install n8n-nodes-reachinbox
  ```

  Or for local development:

  ```bash
  git clone https://github.com/dsouzaalan/n8n-nodes-reachinbox.git
  cd n8n-reachinbox
  npm install
  npm run build
  npm link
  ```

  Then link it in your local `~/.n8n/custom/` folder:

  ```bash
  cd ~/.n8n/custom
  npm init -y
  npm link n8n-nodes-reachinbox
  ```

  ---

  ## 🤖 ReachInbox Setup

  To use this integration, you’ll need an **API Key** from ReachInbox:

  1. Log in to [ReachInbox.ai](https://app.reachinbox.ai).
  2. Go to **Settings** > **Integrations** > **n8n**.
  3. Copy your API Key and Base URL (typically `https://api.reachinbox.ai`).

  ---

  ## 🔧 Credentials

  This node requires the following credentials:

  * **API Key**: Your ReachInbox API key.
  * **Base URL**: API base URL (`https://api.reachinbox.ai`).

  Set these up once in the **Credentials** section in n8n.

  ---

  ## ⚡ Operations

  ### 🟢 Triggers

  You can use this node to trigger workflows on events like:

  * Email Sent
  * Email Opened
  * Email Link Clicked
  * Reply Received
  * Email Bounced
  * Lead Interested
  * Lead Not Interested
  * Campaign Completed
  * Or trigger on **All Events**

  These are dynamically fetched from ReachInbox’s API.
  After setting up, a webhook URL will be generated which you must add in ReachInbox → **Integrations** → **Webhooks**.

  ### 🛠️ Actions

  * **Add Lead to Campaign**
  * **Update Lead to Campaign**
  * **Remove Lead to Campaign**

  All actions require selecting a campaign and passing necessary data (email, first name, last name, etc.), which can be mapped from previous steps.

  ---

  ## ✅ Usage

  1. **Install the node** as described in the installation section.
  2. **Drag the ReachInbox Trigger or Action node** into your workflow.
  3. **Set up your credentials** by entering your API key and base URL.
  4. **Choose a trigger event** or action.
  5. **Map the required fields**, test the workflow, and activate.

  You can now fully automate your cold outreach operations.

  ---

  ## 🧪 Testing Locally

  To run locally:

  ```bash
  n8n
  ```

  Then open [http://localhost:5678](http://localhost:5678) and search for **ReachInbox** in the node list.

  ---

  ## 📎 Compatibility

  * Tested on **n8n v1.75.2**
  * Node.js **v20+**

  ---

  ## 🕓 Version History

  | Version | Description                                          |
  | ------- | ---------------------------------------------------- |
  | 0.1.0   | Initial release with full trigger and action support |

  ---

  ## 🧩 Resources

  * [ReachInbox API Docs](https://docs.reachinbox.ai)
  * [n8n Community Nodes Guide](https://docs.n8n.io/integrations/community-nodes/)

ai_documentation_summary:
  {"purpose":"This n8n node enables automation of ReachInbox workflows by triggering on email events (sent, opened, clicked, replied, bounced) and performing actions like adding or updating leads in campaigns.","capabilities":["Trigger on Email Sent","Trigger on Email Opened","Trigger on Email Link Clicked","Trigger on Reply Received","Trigger on Email Bounced","Trigger on Lead Interested","Trigger on Lead Not Interested","Trigger on Campaign Completed","Trigger on All Events","Add Lead to Campaign"],"authentication":"API Key and Base URL required","commonUseCases":["Automate cold outreach follow-ups based on email engagement","Segment leads into campaigns based on email interactions","Update lead status automatically after email events","Monitor campaign performance and lead interest"],"limitations":["Requires manual setup of webhook URL in ReachInbox integrations","Limited to specific campaign actions (add, update, remove)","Requires ReachInbox API access"],"relatedNodes":["n8n-nodes-base.webhook","n8n-nodes-base.set","n8n-nodes-base.if"]}


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

updated_at: 2026-07-14 14:40:33
author_name: actaport.integration
author_github_url: https://github.com/actaport/n8n-nodes-actaport.git
npm_package_name: @actaport/n8n-nodes-actaport
npm_version: 0.3.0
community_fetched_at: 2026-07-14T14:40:33.520Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "contact",
      "options": [
        {
          "name": "Additional Information Category",
          "value": "additionalInformationCategory"
        },
        {
          "name": "Case File",
          "value": "caseFile"
        },
        {
          "name": "Collision",
          "value": "collision"
        },
        {
          "name": "Contact",
          "value": "contact"
        },
        {
          "name": "Deadline",
          "value": "deadline"
        },
        {
          "name": "Department",
          "value": "department"
        },
        {
          "name": "Document",
          "value": "document"
        },
        {
          "name": "Document Template",
          "value": "documentTemplate"
        },
        {
          "name": "Expense",
          "value": "expense"
        },
        {
          "name": "Folder",
          "value": "folder"
        },
        {
          "name": "Invoice",
          "value": "invoice"
        },
        {
          "name": "Note",
          "value": "note"
        },
        {
          "name": "Resubmission",
          "value": "resubmission"
        },
        {
          "name": "RVG Fee",
          "value": "rvg"
        },
        {
          "name": "Task",
          "value": "task"
        },
        {
          "name": "Third Party Cost",
          "value": "thirdPartyCost"
        },
        {
          "name": "User",
          "value": "user"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get",
          "value": "get",
          "action": "Get additional information category",
          "routing": {
            "request": {
              "url": "=/v1/zusatzinformation/kategorien/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          }
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get additional information categories",
          "routing": {
            "request": {
              "url": "=/v1/zusatzinformation/kategorien",
              "method": "GET"
            }
          }
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "additionalInformationCategory"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get case files",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
              },
              "url": "=/v1/akten",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 50 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Retrieve case files"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get case file",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}",
              "method": "GET"
            }
          },
          "description": "Retrieve case file"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create case file",
          "routing": {
            "send": {
              "preSend": []
            },
            "request": {
              "url": "=/v1/akten",
              "method": "POST"
            }
          },
          "description": "Create new case file"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update case file",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}",
              "method": "PUT"
            }
          },
          "description": "Update case"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "caseFile"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Get",
          "value": "get",
          "action": "Get collision check",
          "routing": {
            "request": {
              "url": "=/v1/kollisionspruefung",
              "method": "GET"
            }
          },
          "description": "Check for collisions"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "collision"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "get",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a contact",
          "routing": {
            "request": {
              "url": "=/v1/kontakte",
              "method": "POST"
            }
          },
          "description": "Creates a new contact with the given data"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get a contact",
          "routing": {
            "request": {
              "url": "=/v1/kontakte/{{$parameter.id}}",
              "method": "GET"
            }
          },
          "description": "Retrieves the metadata of a contact"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many contacts",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
              },
              "url": "=/v1/kontakte",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 100 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Retrieves multiple contacts"
        },
        {
          "name": "Search",
          "value": "search",
          "action": "Search contacts",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 20 : $parameter['size'] }}"
              },
              "url": "=/v1/kontakte/suche",
              "method": "GET"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 20 }}",
                      "suchbegriff": "={{ $request.qs.suchbegriff }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Search contacts with a search term. The search will be performed on certain fields of the contact."
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a contact",
          "routing": {
            "request": {
              "url": "=/v1/kontakte/{{$parameter.id}}",
              "method": "PUT"
            }
          },
          "description": "Updates the data of a contact"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "contact"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create deadline",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/fristen",
              "method": "POST"
            }
          },
          "description": "Create a new deadline"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get deadline",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/fristen/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          },
          "description": "Retrieve a single deadline"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get deadlines",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
              },
              "url": "=/v1/fristen",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 100 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Retrieve many deadlines"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update deadline",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/fristen/{{$parameter[\"id\"]}}",
              "method": "PUT"
            }
          },
          "description": "Update an existing deadline"
        },
        {
          "name": "Update Status",
          "value": "updateStatus",
          "action": "Update deadline status",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/fristen/{{$parameter[\"id\"]}}/status/{{$parameter[\"status\"]}}",
              "method": "PUT"
            }
          },
          "description": "Change the status of a deadline"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "deadline"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get",
          "value": "get",
          "action": "Get department",
          "routing": {
            "request": {
              "url": "=/v1/info/kanzlei/dezernate/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          },
          "description": "Retrieves a department by ID"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get departments",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
              },
              "url": "=/v1/info/kanzlei/dezernate",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 100 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Retrieves many departments"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "department"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create document",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/dokumente/neu",
              "method": "POST"
            }
          },
          "description": "Create a new document with template if supported. If no template is specified, a blank document will be created."
        },
        {
          "name": "Download",
          "value": "download",
          "action": "Download document",
          "routing": {
            "output": {
              "postReceive": [
                null
              ]
            },
            "request": {
              "url": "=/v1/documents/{{$parameter[\"id\"]}}",
              "method": "GET",
              "encoding": "arraybuffer",
              "returnFullResponse": true
            }
          },
          "description": "Download a single document"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get document",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/dokumente/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          },
          "description": "Get a single document"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get documents",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
              },
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/dokumente/uebersicht",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 100 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Retrieve many documents"
        },
        {
          "name": "Search",
          "value": "search",
          "action": "Search documents",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 20 : $parameter['size'] }}"
              },
              "url": "=/v1/documents/search",
              "method": "GET"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 20 }}",
                      "aktennummer": "={{ $request.qs?.aktennummer }}",
                      "suchbegriff": "={{ $request.qs.suchbegriff }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Search documents with a search term. The search will be performed on the document attributes and content."
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update document",
          "routing": {
            "request": {
              "url": "=/v1/documents/{{$parameter[\"id\"]}}/metadata",
              "method": "PUT"
            }
          },
          "description": "Update a single document"
        },
        {
          "name": "Upload",
          "value": "upload",
          "action": "Upload document",
          "routing": {
            "send": {
              "preSend": [
                null
              ]
            },
            "request": {
              "url": "=/v1/akten/{{$parameter.laufendeNummer}}/{{$parameter.bezugsJahr}}/dokumente",
              "method": "POST",
              "headers": {
                "Content-Type": "multipart/form-data"
              }
            }
          },
          "description": "Upload a document to a case"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "document"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get",
          "value": "get",
          "action": "Get document template",
          "routing": {
            "request": {
              "url": "=/v1/vorlagen/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          },
          "description": "Get a single document template"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get document templates",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
              },
              "url": "=/v1/vorlagen",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 50 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Retrieve many document templates"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "documentTemplate"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create expense",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/eigene-auslagen",
              "method": "POST"
            }
          }
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete expense",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/eigene-auslagen/{{$parameter[\"id\"]}}",
              "method": "DELETE"
            }
          }
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get expense",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/eigene-auslagen/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          }
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get expenses",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
              },
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/eigene-auslagen",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 50 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          }
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update expense",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/eigene-auslagen/{{$parameter[\"id\"]}}",
              "method": "PUT"
            }
          }
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "expense"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create folder",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/ordner",
              "method": "POST"
            }
          },
          "description": "Create a new folder in a case file"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get folder",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/ordner/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          },
          "description": "Get a single folder"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get folders",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
              },
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/ordner",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 50 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Retrieve folder structure of a case file"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "folder"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get invoices",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
              },
              "url": "=/v1/rechnungen",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 50 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Retrieve many invoices"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get invoice",
          "routing": {
            "request": {
              "url": "=/v1/rechnungen/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          },
          "description": "Retrieve a single invoice"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "invoice"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create note",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/notizen",
              "method": "POST"
            }
          },
          "description": "Create a new note"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get note",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/notizen/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          },
          "description": "Retrieve a single note"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get notes",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
              },
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/notizen",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 100 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Retrieve many notes"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update note",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/notizen/{{$parameter[\"id\"]}}",
              "method": "PUT"
            }
          },
          "description": "Update an existing note"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "note"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create resubmission",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/wiedervorlagen",
              "method": "POST"
            }
          },
          "description": "Create a new resubmission"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get resubmission",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/wiedervorlagen/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          },
          "description": "Retrieve a single resubmission"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get resubmissions",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
              },
              "url": "=/v1/wiedervorlagen",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 100 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Retrieve many resubmissions"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update resubmission",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/wiedervorlagen/{{$parameter[\"id\"]}}",
              "method": "PUT"
            }
          },
          "description": "Update an existing resubmission"
        },
        {
          "name": "Update Status",
          "value": "updateStatus",
          "action": "Update resubmission status",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/wiedervorlagen/{{$parameter[\"id\"]}}/status/{{$parameter[\"status\"]}}",
              "method": "PUT"
            }
          },
          "description": "Change the status of a resubmission"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "resubmission"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create rvg fee",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/rvg",
              "method": "POST"
            }
          }
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete rvg fee",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/rvg/{{$parameter[\"id\"]}}",
              "method": "DELETE"
            }
          }
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get rvg fee",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/rvg/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          }
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get rvg fees",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
              },
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/rvg",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 100 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          }
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update rvg fee",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/rvg/{{$parameter[\"id\"]}}",
              "method": "PUT"
            }
          }
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "rvg"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create task",
          "routing": {
            "request": {
              "url": "=/v1/aufgaben",
              "method": "POST"
            }
          },
          "description": "Create a new task"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get task",
          "routing": {
            "request": {
              "url": "=/v1/aufgaben/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          },
          "description": "Retrieve a single task"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get tasks",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
              },
              "url": "=/v1/aufgaben",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 100 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Retrieve many tasks"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update task",
          "routing": {
            "request": {
              "url": "=/v1/aufgaben/{{$parameter[\"id\"]}}",
              "method": "PUT"
            }
          },
          "description": "Update an existing task"
        },
        {
          "name": "Update Status",
          "value": "updateStatus",
          "action": "Update task status",
          "routing": {
            "request": {
              "url": "=/v1/aufgaben/{{$parameter[\"id\"]}}/status/{{$parameter[\"status\"]}}",
              "method": "PUT"
            }
          },
          "description": "Change the status of a task"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "task"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getAll",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create third party cost",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/fremde-auslagen",
              "method": "POST"
            }
          }
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete third party cost",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/fremde-auslagen/{{$parameter[\"id\"]}}",
              "method": "DELETE"
            }
          }
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get third party cost",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/fremde-auslagen/{{$parameter[\"id\"]}}",
              "method": "GET"
            }
          }
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get third party costs",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
              },
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/fremde-auslagen",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 100 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          }
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update third party cost",
          "routing": {
            "request": {
              "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/fremde-auslagen/{{$parameter[\"id\"]}}",
              "method": "PUT"
            }
          }
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "thirdPartyCost"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getCurrent",
      "options": [
        {
          "name": "Get",
          "value": "get",
          "action": "Get a user",
          "routing": {
            "request": {
              "url": "=/v1/benutzer/{{$parameter.userId}}",
              "method": "GET"
            }
          },
          "description": "Get the data of a single user"
        },
        {
          "name": "Get Me",
          "value": "getCurrent",
          "action": "Get me",
          "routing": {
            "request": {
              "url": "=/v1/info/me",
              "method": "GET"
            }
          },
          "description": "Get the data of the current authenticated user"
        },
        {
          "name": "Get Many",
          "value": "getAll",
          "action": "Get many users",
          "routing": {
            "send": {
              "paginate": "={{ $parameter['returnAll'] }}"
            },
            "output": {
              "postReceive": [
                {
                  "type": "rootProperty",
                  "properties": {
                    "property": "content"
                  }
                }
              ]
            },
            "request": {
              "qs": {
                "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
              },
              "url": "=/v1/benutzer",
              "method": "GET",
              "arrayFormat": "repeat"
            },
            "operations": {
              "pagination": {
                "type": "generic",
                "properties": {
                  "request": {
                    "qs": {
                      "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                      "size": "={{ $response.body?.size ?? 100 }}",
                      "sort": "={{ $request.qs?.sort }}",
                      "filter": "={{ $request.qs?.filter }}"
                    }
                  },
                  "continue": "={{ !$response.body?.last }}"
                }
              }
            }
          },
          "description": "Get the data of multiple users"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "user"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Get",
      "value": "get",
      "action": "Get additional information category",
      "routing": {
        "request": {
          "url": "=/v1/zusatzinformation/kategorien/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "resource": "additionalInformationCategory"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get additional information categories",
      "routing": {
        "request": {
          "url": "=/v1/zusatzinformation/kategorien",
          "method": "GET"
        }
      },
      "resource": "additionalInformationCategory"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get case files",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
          },
          "url": "=/v1/akten",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 50 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Retrieve case files",
      "resource": "caseFile"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get case file",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}",
          "method": "GET"
        }
      },
      "description": "Retrieve case file",
      "resource": "caseFile"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create case file",
      "routing": {
        "send": {
          "preSend": []
        },
        "request": {
          "url": "=/v1/akten",
          "method": "POST"
        }
      },
      "description": "Create new case file",
      "resource": "caseFile"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update case file",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}",
          "method": "PUT"
        }
      },
      "description": "Update case",
      "resource": "caseFile"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get collision check",
      "routing": {
        "request": {
          "url": "=/v1/kollisionspruefung",
          "method": "GET"
        }
      },
      "description": "Check for collisions",
      "resource": "collision"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a contact",
      "routing": {
        "request": {
          "url": "=/v1/kontakte",
          "method": "POST"
        }
      },
      "description": "Creates a new contact with the given data",
      "resource": "contact"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a contact",
      "routing": {
        "request": {
          "url": "=/v1/kontakte/{{$parameter.id}}",
          "method": "GET"
        }
      },
      "description": "Retrieves the metadata of a contact",
      "resource": "contact"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many contacts",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
          },
          "url": "=/v1/kontakte",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 100 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Retrieves multiple contacts",
      "resource": "contact"
    },
    {
      "name": "Search",
      "value": "search",
      "action": "Search contacts",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 20 : $parameter['size'] }}"
          },
          "url": "=/v1/kontakte/suche",
          "method": "GET"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 20 }}",
                  "suchbegriff": "={{ $request.qs.suchbegriff }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Search contacts with a search term. The search will be performed on certain fields of the contact.",
      "resource": "contact"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a contact",
      "routing": {
        "request": {
          "url": "=/v1/kontakte/{{$parameter.id}}",
          "method": "PUT"
        }
      },
      "description": "Updates the data of a contact",
      "resource": "contact"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create deadline",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/fristen",
          "method": "POST"
        }
      },
      "description": "Create a new deadline",
      "resource": "deadline"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get deadline",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/fristen/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "description": "Retrieve a single deadline",
      "resource": "deadline"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get deadlines",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
          },
          "url": "=/v1/fristen",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 100 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Retrieve many deadlines",
      "resource": "deadline"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update deadline",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/fristen/{{$parameter[\"id\"]}}",
          "method": "PUT"
        }
      },
      "description": "Update an existing deadline",
      "resource": "deadline"
    },
    {
      "name": "Update Status",
      "value": "updateStatus",
      "action": "Update deadline status",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/fristen/{{$parameter[\"id\"]}}/status/{{$parameter[\"status\"]}}",
          "method": "PUT"
        }
      },
      "description": "Change the status of a deadline",
      "resource": "deadline"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get department",
      "routing": {
        "request": {
          "url": "=/v1/info/kanzlei/dezernate/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "description": "Retrieves a department by ID",
      "resource": "department"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get departments",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
          },
          "url": "=/v1/info/kanzlei/dezernate",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 100 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Retrieves many departments",
      "resource": "department"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create document",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/dokumente/neu",
          "method": "POST"
        }
      },
      "description": "Create a new document with template if supported. If no template is specified, a blank document will be created.",
      "resource": "document"
    },
    {
      "name": "Download",
      "value": "download",
      "action": "Download document",
      "routing": {
        "output": {
          "postReceive": [
            null
          ]
        },
        "request": {
          "url": "=/v1/documents/{{$parameter[\"id\"]}}",
          "method": "GET",
          "encoding": "arraybuffer",
          "returnFullResponse": true
        }
      },
      "description": "Download a single document",
      "resource": "document"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get document",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/dokumente/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "description": "Get a single document",
      "resource": "document"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get documents",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
          },
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/dokumente/uebersicht",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 100 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Retrieve many documents",
      "resource": "document"
    },
    {
      "name": "Search",
      "value": "search",
      "action": "Search documents",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 20 : $parameter['size'] }}"
          },
          "url": "=/v1/documents/search",
          "method": "GET"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 20 }}",
                  "aktennummer": "={{ $request.qs?.aktennummer }}",
                  "suchbegriff": "={{ $request.qs.suchbegriff }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Search documents with a search term. The search will be performed on the document attributes and content.",
      "resource": "document"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update document",
      "routing": {
        "request": {
          "url": "=/v1/documents/{{$parameter[\"id\"]}}/metadata",
          "method": "PUT"
        }
      },
      "description": "Update a single document",
      "resource": "document"
    },
    {
      "name": "Upload",
      "value": "upload",
      "action": "Upload document",
      "routing": {
        "send": {
          "preSend": [
            null
          ]
        },
        "request": {
          "url": "=/v1/akten/{{$parameter.laufendeNummer}}/{{$parameter.bezugsJahr}}/dokumente",
          "method": "POST",
          "headers": {
            "Content-Type": "multipart/form-data"
          }
        }
      },
      "description": "Upload a document to a case",
      "resource": "document"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get document template",
      "routing": {
        "request": {
          "url": "=/v1/vorlagen/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "description": "Get a single document template",
      "resource": "documentTemplate"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get document templates",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
          },
          "url": "=/v1/vorlagen",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 50 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Retrieve many document templates",
      "resource": "documentTemplate"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create expense",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/eigene-auslagen",
          "method": "POST"
        }
      },
      "resource": "expense"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete expense",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/eigene-auslagen/{{$parameter[\"id\"]}}",
          "method": "DELETE"
        }
      },
      "resource": "expense"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get expense",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/eigene-auslagen/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "resource": "expense"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get expenses",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
          },
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/eigene-auslagen",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 50 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "resource": "expense"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update expense",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/eigene-auslagen/{{$parameter[\"id\"]}}",
          "method": "PUT"
        }
      },
      "resource": "expense"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create folder",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/ordner",
          "method": "POST"
        }
      },
      "description": "Create a new folder in a case file",
      "resource": "folder"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get folder",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/ordner/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "description": "Get a single folder",
      "resource": "folder"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get folders",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
          },
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/ordner",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 50 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Retrieve folder structure of a case file",
      "resource": "folder"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get invoices",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 50 : $parameter['size'] }}"
          },
          "url": "=/v1/rechnungen",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 50 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Retrieve many invoices",
      "resource": "invoice"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get invoice",
      "routing": {
        "request": {
          "url": "=/v1/rechnungen/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "description": "Retrieve a single invoice",
      "resource": "invoice"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create note",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/notizen",
          "method": "POST"
        }
      },
      "description": "Create a new note",
      "resource": "note"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get note",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/notizen/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "description": "Retrieve a single note",
      "resource": "note"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get notes",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
          },
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/notizen",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 100 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Retrieve many notes",
      "resource": "note"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update note",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/notizen/{{$parameter[\"id\"]}}",
          "method": "PUT"
        }
      },
      "description": "Update an existing note",
      "resource": "note"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create resubmission",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/wiedervorlagen",
          "method": "POST"
        }
      },
      "description": "Create a new resubmission",
      "resource": "resubmission"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get resubmission",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/wiedervorlagen/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "description": "Retrieve a single resubmission",
      "resource": "resubmission"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get resubmissions",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
          },
          "url": "=/v1/wiedervorlagen",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 100 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Retrieve many resubmissions",
      "resource": "resubmission"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update resubmission",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/wiedervorlagen/{{$parameter[\"id\"]}}",
          "method": "PUT"
        }
      },
      "description": "Update an existing resubmission",
      "resource": "resubmission"
    },
    {
      "name": "Update Status",
      "value": "updateStatus",
      "action": "Update resubmission status",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/wiedervorlagen/{{$parameter[\"id\"]}}/status/{{$parameter[\"status\"]}}",
          "method": "PUT"
        }
      },
      "description": "Change the status of a resubmission",
      "resource": "resubmission"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create rvg fee",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/rvg",
          "method": "POST"
        }
      },
      "resource": "rvg"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete rvg fee",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/rvg/{{$parameter[\"id\"]}}",
          "method": "DELETE"
        }
      },
      "resource": "rvg"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get rvg fee",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/rvg/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "resource": "rvg"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get rvg fees",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
          },
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/rvg",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 100 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "resource": "rvg"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update rvg fee",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/rvg/{{$parameter[\"id\"]}}",
          "method": "PUT"
        }
      },
      "resource": "rvg"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create task",
      "routing": {
        "request": {
          "url": "=/v1/aufgaben",
          "method": "POST"
        }
      },
      "description": "Create a new task",
      "resource": "task"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get task",
      "routing": {
        "request": {
          "url": "=/v1/aufgaben/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "description": "Retrieve a single task",
      "resource": "task"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get tasks",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
          },
          "url": "=/v1/aufgaben",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 100 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Retrieve many tasks",
      "resource": "task"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update task",
      "routing": {
        "request": {
          "url": "=/v1/aufgaben/{{$parameter[\"id\"]}}",
          "method": "PUT"
        }
      },
      "description": "Update an existing task",
      "resource": "task"
    },
    {
      "name": "Update Status",
      "value": "updateStatus",
      "action": "Update task status",
      "routing": {
        "request": {
          "url": "=/v1/aufgaben/{{$parameter[\"id\"]}}/status/{{$parameter[\"status\"]}}",
          "method": "PUT"
        }
      },
      "description": "Change the status of a task",
      "resource": "task"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create third party cost",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/fremde-auslagen",
          "method": "POST"
        }
      },
      "resource": "thirdPartyCost"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete third party cost",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/fremde-auslagen/{{$parameter[\"id\"]}}",
          "method": "DELETE"
        }
      },
      "resource": "thirdPartyCost"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get third party cost",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/fremde-auslagen/{{$parameter[\"id\"]}}",
          "method": "GET"
        }
      },
      "resource": "thirdPartyCost"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get third party costs",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
          },
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/fremde-auslagen",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 100 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "resource": "thirdPartyCost"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update third party cost",
      "routing": {
        "request": {
          "url": "=/v1/akten/{{$parameter[\"laufendeNummer\"]}}/{{$parameter[\"bezugsJahr\"]}}/verguetungspositionen/fremde-auslagen/{{$parameter[\"id\"]}}",
          "method": "PUT"
        }
      },
      "resource": "thirdPartyCost"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get a user",
      "routing": {
        "request": {
          "url": "=/v1/benutzer/{{$parameter.userId}}",
          "method": "GET"
        }
      },
      "description": "Get the data of a single user",
      "resource": "user"
    },
    {
      "name": "Get Me",
      "value": "getCurrent",
      "action": "Get me",
      "routing": {
        "request": {
          "url": "=/v1/info/me",
          "method": "GET"
        }
      },
      "description": "Get the data of the current authenticated user",
      "resource": "user"
    },
    {
      "name": "Get Many",
      "value": "getAll",
      "action": "Get many users",
      "routing": {
        "send": {
          "paginate": "={{ $parameter['returnAll'] }}"
        },
        "output": {
          "postReceive": [
            {
              "type": "rootProperty",
              "properties": {
                "property": "content"
              }
            }
          ]
        },
        "request": {
          "qs": {
            "size": "={{ $parameter['returnAll'] ? 100 : $parameter['size'] }}"
          },
          "url": "=/v1/benutzer",
          "method": "GET",
          "arrayFormat": "repeat"
        },
        "operations": {
          "pagination": {
            "type": "generic",
            "properties": {
              "request": {
                "qs": {
                  "page": "={{ ($response.body?.number ?? -1) + 1 }}",
                  "size": "={{ $response.body?.size ?? 100 }}",
                  "sort": "={{ $request.qs?.sort }}",
                  "filter": "={{ $request.qs?.filter }}"
                }
              },
              "continue": "={{ !$response.body?.last }}"
            }
          }
        }
      },
      "description": "Get the data of multiple users",
      "resource": "user"
    }
  ]

npm_readme:
  # n8n-nodes-actaport

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

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

  Actaport is a cloud-based legal management platform that helps law firms streamline their daily work.

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

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

  ## Installation

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

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

  After installing the node, you can use it like any other node. n8n displays the node in search results in the **Nodes**
  panel.

  ## Operations

  | Resource                              | Supported Operations                          |
  |---------------------------------------|-----------------------------------------------|
  | **Additional Information Categories** | get, getAll                                   |
  | **Case File**                         | get, getAll, create, update                   |
  | **Client Fee**                        | get, update                                   |
  | **Collision Check**                   | get                                           |
  | **Contact**                           | get, getAll, create, update, search           |
  | **Deadline**                          | get, getAll, create, update, updateStatus     |
  | **Department**                        | get, getAll                                   |
  | **Document**                          | get, getAll, update, download, create, search |
  | **Document Template**                 | get, getAll                                   |
  | **Expense**                           | get, getAll, create, update, delete           |
  | **Folder**                            | get, getAll, create                           |
  | **Invoice**                           | get, getAll                                   |
  | **Note**                              | get, getAll, create, update                   |
  | **Resubmission**                      | get, getAll, create, update, updateStatus     |
  | **RVG Fee**                           | get, getAll, create, update, delete           |
  | **Task**                              | get, getAll, create, update, updateStatus     |
  | **Third Party Cost**                  | get, getAll, create, update, delete           |
  | **User**                              | get, getMe, getAll                            |

  ## Credentials

  ### Prerequisites
  1. An active Actaport account. If you don't have one, sign up at [Actaport](https://www.actaport.de/).
  2. API access enabled for your Actaport account. Contact [Actaport support](https://actaport.zendesk.com/hc/de/requests/new) if you need assistance with this step.

  ### Setting up Actaport credentials in n8n
  1. In n8n, locate the **Credentials** section and click on **Create credential**.
  2. Select **Actaport OAuth2 API** from the list.
  3. Fill in your realm once prompted and click on **Connect my account**.
  4. Once redirected to the Actaport login page, enter your Actaport credentials to authorize n8n.
  5. After successful authentication, n8n will store the access token securely for future API requests

  ![img.png](assets/add_credentials.png)
  ![img_1.png](assets/login_prompt.png)
  ![img_2.png](assets/successful_login.png)
  ## Compatibility

  * n8n Nodes API version: 1
  * Node.js: >= 20.15


  ## Resources

  * [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  * [Actaport Homepage](https://www.actaport.de/)
  * [Actaport Developer Documentation](https://api.actaport.de/api-docs)
  * [Actaport REST API Reference](https://api.actaport.de/api-docs/rest-api/reference/public)
  * [Actaport Technical Support](https://www.actaport.de/kontakt/api-anfrage)

  # Version history
  * 0.1.0 - Initial release of the n8n Actaport node, supporting multiple resources and operations (see [Operations
    section](#operations) for details).

ai_documentation_summary:
  {"purpose":"This n8n community node enables workflow automation by interacting with the Actaport cloud-based legal management platform API.","capabilities":["Retrieve additional information categories","Manage case files (get, create, update)","Perform collision checks","Search and manage contacts","Handle deadlines (get, create, update, update status)","Manage departments","Retrieve and download documents","Manage document templates","Process expenses (create, update, delete)","Manage folders"],"authentication":"OAuth2","commonUseCases":["Automating legal case management workflows","Syncing contact information between systems","Processing deadline updates and status changes","Downloading and managing legal documents","Tracking expenses and third-party costs"],"limitations":["Requires an active Actaport account with API access enabled","Node.js version 20.15 or higher is required","Requires manual setup of credentials in n8n before use"],"relatedNodes":["n8n HTTP Request node","n8n Webhook node","n8n Code node"]}


node_type: n8n-nodes-resend.resend
package_name: n8n-nodes-resend
display_name: Resend
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: zenorocha
author_github_url: https://github.com/resend/
npm_package_name: n8n-nodes-resend
npm_version: 2.5.3
community_fetched_at: 2026-07-14T14:40:33.521Z
ai_summary_generated_at: 2026-03-25 22:47:20
description:
  Send emails, manage contacts, create broadcasts, handle templates, domains, segments, topics, and webhooks using the Resend email platform

properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "email",
      "options": [
        {
          "name": "Broadcast",
          "value": "broadcasts",
          "description": "Create, send, update, or delete email broadcasts to reach multiple contacts at once"
        },
        {
          "name": "Contact",
          "value": "contacts",
          "description": "Add, update, delete, or list contacts (email subscribers) and manage their segment and topic memberships"
        },
        {
          "name": "Contact Property",
          "value": "contactProperties",
          "description": "Create, update, delete, or list custom contact properties (metadata fields) for storing additional contact information"
        },
        {
          "name": "Domain",
          "value": "domains",
          "description": "Add, verify, update, or delete sending domains for email authentication and deliverability"
        },
        {
          "name": "Email",
          "value": "email",
          "description": "Send single or batch emails, retrieve sent emails, cancel scheduled emails, or manage email attachments"
        },
        {
          "name": "Event",
          "value": "events",
          "description": "Create, send, retrieve, update, or delete events for triggering workflows (private alpha)"
        },
        {
          "name": "Log",
          "value": "logs",
          "description": "List and retrieve API request logs including request bodies, response bodies, and status codes"
        },
        {
          "name": "Receiving Email",
          "value": "receivingEmails",
          "description": "List and retrieve received emails and their attachments from inbound email processing"
        },
        {
          "name": "Segment",
          "value": "segments",
          "description": "Create, update, delete, or list contact segments for grouping contacts based on criteria"
        },
        {
          "name": "Template",
          "value": "templates",
          "description": "Create, update, publish, duplicate, or delete reusable email templates with dynamic variables"
        },
        {
          "name": "Topic",
          "value": "topics",
          "description": "Create, update, delete, or list subscription topics for managing email preferences"
        },
        {
          "name": "Webhook",
          "value": "webhooks",
          "description": "Create, update, delete, or list webhooks for receiving real-time notifications about email events"
        },
        {
          "name": "Workflow",
          "value": "workflows",
          "description": "Create, update, delete, or list automated email workflows and monitor runs (private alpha)"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "send",
      "options": [
        {
          "name": "Cancel",
          "value": "cancel",
          "action": "Cancel a scheduled email",
          "description": "Cancel a previously scheduled email by its email ID before it gets sent"
        },
        {
          "name": "Get Attachment",
          "value": "getAttachment",
          "action": "Get attachment from email",
          "description": "Download a specific attachment from a sent email using the email ID and attachment ID"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all sent emails",
          "description": "Get a list of all previously sent emails with their status, subject, and recipients"
        },
        {
          "name": "List Attachments",
          "value": "listAttachments",
          "action": "List email attachments",
          "description": "Get all attachments from a sent email by providing the email ID"
        },
        {
          "name": "Retrieve",
          "value": "retrieve",
          "action": "Get email details",
          "description": "Get details of a specific sent email including subject, recipients, status, and timestamps"
        },
        {
          "name": "Send",
          "value": "send",
          "action": "Send a new email",
          "description": "Send a new email to one or more recipients with HTML or plain text content, attachments, and optional scheduling"
        },
        {
          "name": "Send and Wait for Response",
          "value": "sendAndWait",
          "action": "Send email and wait for recipient response",
          "description": "Send an email with response buttons and pause workflow execution until the recipient clicks a response"
        },
        {
          "name": "Send Batch",
          "value": "sendBatch",
          "action": "Send multiple emails in batch",
          "description": "Send up to 100 emails in a single API call for bulk email delivery with individual customization"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update scheduled email time",
          "description": "Reschedule a scheduled email by updating its scheduled_at time before it is sent"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "email"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create an email template",
          "description": "Create a new reusable email template with HTML content and dynamic variables for personalization"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a template",
          "description": "Permanently delete an email template by its template ID"
        },
        {
          "name": "Duplicate",
          "value": "duplicate",
          "action": "Duplicate a template",
          "description": "Create a copy of an existing template with a new name for easy customization"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get template details",
          "description": "Retrieve a template's details including HTML content, variables, and publish status by template ID"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all templates",
          "description": "Get all email templates with their names, IDs, and publish status"
        },
        {
          "name": "Publish",
          "value": "publish",
          "action": "Publish a template",
          "description": "Publish a template to make it available for sending emails. Required before a template can be used in email operations."
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a template",
          "description": "Update a template's name, subject, HTML content, or variables"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "templates"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Add a new sending domain",
          "description": "Add a new sending domain for email authentication. Returns DNS records that must be configured for verification."
        },
        {
          "name": "Create Tracking Domain",
          "value": "createTrackingDomain",
          "action": "Create a tracking domain",
          "description": "Create a custom domain for click and open tracking (private alpha)"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a domain",
          "description": "Remove a sending domain from your account. Emails can no longer be sent from this domain after deletion."
        },
        {
          "name": "Delete Tracking Domain",
          "value": "deleteTrackingDomain",
          "action": "Delete a tracking domain",
          "description": "Remove an existing click tracking domain (private alpha)"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get domain details",
          "description": "Retrieve details of a domain including DNS records, verification status, and configuration"
        },
        {
          "name": "Get Tracking Domain",
          "value": "getTrackingDomain",
          "action": "Get tracking domain details",
          "description": "Retrieve a single click tracking domain with its DNS records and verification status (private alpha)"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all domains",
          "description": "Get all sending domains with their verification status and DNS record requirements"
        },
        {
          "name": "List Tracking Domains",
          "value": "listTrackingDomains",
          "action": "List tracking domains",
          "description": "List all tracking domains for a given domain (private alpha)"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update domain settings",
          "description": "Update domain settings such as click tracking, open tracking, or TLS requirements"
        },
        {
          "name": "Verify",
          "value": "verify",
          "action": "Verify domain DNS records",
          "description": "Trigger verification of DNS records for a domain. Use after configuring DNS to check if domain is ready for sending."
        },
        {
          "name": "Verify Tracking Domain",
          "value": "verifyTrackingDomain",
          "action": "Verify a tracking domain",
          "description": "Trigger DNS verification for a tracking domain (private alpha)"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "domains"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a broadcast campaign",
          "description": "Create a new email broadcast campaign with subject, content, and target audience for bulk email sending"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a broadcast",
          "description": "Permanently delete a broadcast campaign by its broadcast ID"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get broadcast details",
          "description": "Retrieve details of a specific broadcast including status, subject, audience, and send statistics"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all broadcasts",
          "description": "Get a list of all broadcast campaigns with their status, subject lines, and creation dates"
        },
        {
          "name": "Send",
          "value": "send",
          "action": "Send a broadcast",
          "description": "Trigger the sending of a broadcast campaign to its target audience immediately or at a scheduled time"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a broadcast",
          "description": "Update a broadcast's subject, content, audience, or schedule before it is sent"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "broadcasts"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a contact segment",
          "description": "Create a new contact segment for grouping contacts based on criteria or manual assignment"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a segment",
          "description": "Permanently delete a segment by its segment ID. Contacts in the segment are not deleted."
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get segment details",
          "description": "Retrieve details of a specific segment including its name, contact count, and creation date"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all segments",
          "description": "Get all segments in the account with their names, IDs, and contact counts"
        },
        {
          "name": "List Contacts",
          "value": "listContacts",
          "action": "List segment contacts",
          "description": "Get all contacts belonging to a specific segment"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "segments"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a subscription topic",
          "description": "Create a new subscription topic for organizing email preferences and managing unsubscribe options"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a topic",
          "description": "Permanently delete a subscription topic by its topic ID"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get topic details",
          "description": "Retrieve details of a specific topic including its name, description, and subscriber count"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all topics",
          "description": "Get all subscription topics with their names, IDs, and subscriber counts"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a topic",
          "description": "Update a topic's name or description"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "topics"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Add to Segment",
          "value": "addToSegment",
          "action": "Add contact to a segment",
          "description": "Add an existing contact to a segment by providing the contact ID and segment ID"
        },
        {
          "name": "Create",
          "value": "create",
          "action": "Create a new contact",
          "description": "Create a new contact with email address, optional name, custom properties, and segment/topic assignments"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a contact",
          "description": "Permanently delete a contact by their contact ID"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get contact details",
          "description": "Retrieve a contact's details including email, name, properties, and subscription status by contact ID"
        },
        {
          "name": "Get Topics",
          "value": "getTopics",
          "action": "Get contact topic subscriptions",
          "description": "Get all topic subscriptions (opt-in/opt-out status) for a specific contact"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all contacts",
          "description": "Get a list of all contacts in the account with their email addresses and basic information"
        },
        {
          "name": "List Segments",
          "value": "listSegments",
          "action": "List contact segments",
          "description": "Get all segments that a specific contact belongs to"
        },
        {
          "name": "Remove From Segment",
          "value": "removeFromSegment",
          "action": "Remove contact from segment",
          "description": "Remove a contact from a segment by providing the contact ID and segment ID"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update contact details",
          "description": "Update a contact's information including name, email, properties, or unsubscribe status"
        },
        {
          "name": "Update Topics",
          "value": "updateTopics",
          "action": "Update contact topic subscriptions",
          "description": "Update a contact's topic subscription preferences (opt-in or opt-out of specific topics)"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "contacts"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a contact property",
          "description": "Create a new custom contact property (metadata field) for storing additional information about contacts"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a contact property",
          "description": "Permanently delete a contact property. This removes the property from all contacts."
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get contact property details",
          "description": "Retrieve details of a specific contact property including its name, type, and configuration"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all contact properties",
          "description": "Get all custom contact properties defined for storing contact metadata"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a contact property",
          "description": "Update a contact property's name or configuration"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "contactProperties"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a webhook endpoint",
          "description": "Create a new webhook endpoint to receive real-time notifications for email events like delivered, opened, clicked, bounced"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a webhook",
          "description": "Delete a webhook endpoint. Events will no longer be sent to this URL."
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get webhook details",
          "description": "Retrieve details of a webhook including its URL, subscribed events, and status"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all webhooks",
          "description": "Get all configured webhooks with their URLs, event subscriptions, and status"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a webhook",
          "description": "Update a webhook's URL or the events it is subscribed to"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "webhooks"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Get",
          "value": "get",
          "action": "Get received email details",
          "description": "Retrieve a specific received email including sender, subject, body, and headers by email ID"
        },
        {
          "name": "Get Attachment",
          "value": "getAttachment",
          "action": "Download attachment from received email",
          "description": "Download a specific attachment from a received email by email ID and attachment ID"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all received emails",
          "description": "Get all emails received through inbound email processing with sender, subject, and timestamps"
        },
        {
          "name": "List Attachments",
          "value": "listAttachments",
          "action": "List attachments from received email",
          "description": "Get all attachments from a received email including filenames, sizes, and content types"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "receivingEmails"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a workflow",
          "description": "Create a new workflow to automate email sequences"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete a workflow",
          "description": "Remove an existing workflow"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get workflow details",
          "description": "Retrieve a single workflow with its steps and edges"
        },
        {
          "name": "Get Run",
          "value": "getRun",
          "action": "Get a workflow run",
          "description": "Retrieve a single workflow run"
        },
        {
          "name": "Get Run Step",
          "value": "getRunStep",
          "action": "Get a workflow run step",
          "description": "Retrieve a single workflow run step"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all workflows",
          "description": "Retrieve a list of workflows"
        },
        {
          "name": "List Run Steps",
          "value": "listRunSteps",
          "action": "List workflow run steps",
          "description": "Retrieve a list of workflow run steps"
        },
        {
          "name": "List Runs",
          "value": "listRuns",
          "action": "List workflow runs",
          "description": "Retrieve a list of workflow runs"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update a workflow",
          "description": "Update an existing workflow (enable or disable)"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "workflows"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create an event",
          "description": "Create a new event that can be used to trigger workflows"
        },
        {
          "name": "Delete",
          "value": "delete",
          "action": "Delete an event",
          "description": "Remove an existing event by ID or name"
        },
        {
          "name": "Get",
          "value": "get",
          "action": "Get event details",
          "description": "Retrieve a single event by ID or name"
        },
        {
          "name": "List",
          "value": "list",
          "action": "List all events",
          "description": "Retrieve a list of events"
        },
        {
          "name": "Send",
          "value": "send",
          "action": "Send an event",
          "description": "Send a named event to trigger matching workflows"
        },
        {
          "name": "Update",
          "value": "update",
          "action": "Update an event",
          "description": "Update an existing event schema"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "events"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "List",
          "value": "list",
          "action": "List all logs",
          "description": "Get all API request logs for the account including request and response details"
        },
        {
          "name": "Retrieve",
          "value": "retrieve",
          "action": "Retrieve a log entry",
          "description": "Retrieve details of a specific log entry including the request body, response body, and response status"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "logs"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Cancel",
      "value": "cancel",
      "action": "Cancel a scheduled email",
      "description": "Cancel a previously scheduled email by its email ID before it gets sent",
      "resource": "email"
    },
    {
      "name": "Get Attachment",
      "value": "getAttachment",
      "action": "Get attachment from email",
      "description": "Download a specific attachment from a sent email using the email ID and attachment ID",
      "resource": "email"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all sent emails",
      "description": "Get a list of all previously sent emails with their status, subject, and recipients",
      "resource": "email"
    },
    {
      "name": "List Attachments",
      "value": "listAttachments",
      "action": "List email attachments",
      "description": "Get all attachments from a sent email by providing the email ID",
      "resource": "email"
    },
    {
      "name": "Retrieve",
      "value": "retrieve",
      "action": "Get email details",
      "description": "Get details of a specific sent email including subject, recipients, status, and timestamps",
      "resource": "email"
    },
    {
      "name": "Send",
      "value": "send",
      "action": "Send a new email",
      "description": "Send a new email to one or more recipients with HTML or plain text content, attachments, and optional scheduling",
      "resource": "email"
    },
    {
      "name": "Send and Wait for Response",
      "value": "sendAndWait",
      "action": "Send email and wait for recipient response",
      "description": "Send an email with response buttons and pause workflow execution until the recipient clicks a response",
      "resource": "email"
    },
    {
      "name": "Send Batch",
      "value": "sendBatch",
      "action": "Send multiple emails in batch",
      "description": "Send up to 100 emails in a single API call for bulk email delivery with individual customization",
      "resource": "email"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update scheduled email time",
      "description": "Reschedule a scheduled email by updating its scheduled_at time before it is sent",
      "resource": "email"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create an email template",
      "description": "Create a new reusable email template with HTML content and dynamic variables for personalization",
      "resource": "templates"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a template",
      "description": "Permanently delete an email template by its template ID",
      "resource": "templates"
    },
    {
      "name": "Duplicate",
      "value": "duplicate",
      "action": "Duplicate a template",
      "description": "Create a copy of an existing template with a new name for easy customization",
      "resource": "templates"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get template details",
      "description": "Retrieve a template's details including HTML content, variables, and publish status by template ID",
      "resource": "templates"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all templates",
      "description": "Get all email templates with their names, IDs, and publish status",
      "resource": "templates"
    },
    {
      "name": "Publish",
      "value": "publish",
      "action": "Publish a template",
      "description": "Publish a template to make it available for sending emails. Required before a template can be used in email operations.",
      "resource": "templates"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a template",
      "description": "Update a template's name, subject, HTML content, or variables",
      "resource": "templates"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Add a new sending domain",
      "description": "Add a new sending domain for email authentication. Returns DNS records that must be configured for verification.",
      "resource": "domains"
    },
    {
      "name": "Create Tracking Domain",
      "value": "createTrackingDomain",
      "action": "Create a tracking domain",
      "description": "Create a custom domain for click and open tracking (private alpha)",
      "resource": "domains"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a domain",
      "description": "Remove a sending domain from your account. Emails can no longer be sent from this domain after deletion.",
      "resource": "domains"
    },
    {
      "name": "Delete Tracking Domain",
      "value": "deleteTrackingDomain",
      "action": "Delete a tracking domain",
      "description": "Remove an existing click tracking domain (private alpha)",
      "resource": "domains"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get domain details",
      "description": "Retrieve details of a domain including DNS records, verification status, and configuration",
      "resource": "domains"
    },
    {
      "name": "Get Tracking Domain",
      "value": "getTrackingDomain",
      "action": "Get tracking domain details",
      "description": "Retrieve a single click tracking domain with its DNS records and verification status (private alpha)",
      "resource": "domains"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all domains",
      "description": "Get all sending domains with their verification status and DNS record requirements",
      "resource": "domains"
    },
    {
      "name": "List Tracking Domains",
      "value": "listTrackingDomains",
      "action": "List tracking domains",
      "description": "List all tracking domains for a given domain (private alpha)",
      "resource": "domains"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update domain settings",
      "description": "Update domain settings such as click tracking, open tracking, or TLS requirements",
      "resource": "domains"
    },
    {
      "name": "Verify",
      "value": "verify",
      "action": "Verify domain DNS records",
      "description": "Trigger verification of DNS records for a domain. Use after configuring DNS to check if domain is ready for sending.",
      "resource": "domains"
    },
    {
      "name": "Verify Tracking Domain",
      "value": "verifyTrackingDomain",
      "action": "Verify a tracking domain",
      "description": "Trigger DNS verification for a tracking domain (private alpha)",
      "resource": "domains"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a broadcast campaign",
      "description": "Create a new email broadcast campaign with subject, content, and target audience for bulk email sending",
      "resource": "broadcasts"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a broadcast",
      "description": "Permanently delete a broadcast campaign by its broadcast ID",
      "resource": "broadcasts"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get broadcast details",
      "description": "Retrieve details of a specific broadcast including status, subject, audience, and send statistics",
      "resource": "broadcasts"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all broadcasts",
      "description": "Get a list of all broadcast campaigns with their status, subject lines, and creation dates",
      "resource": "broadcasts"
    },
    {
      "name": "Send",
      "value": "send",
      "action": "Send a broadcast",
      "description": "Trigger the sending of a broadcast campaign to its target audience immediately or at a scheduled time",
      "resource": "broadcasts"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a broadcast",
      "description": "Update a broadcast's subject, content, audience, or schedule before it is sent",
      "resource": "broadcasts"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a contact segment",
      "description": "Create a new contact segment for grouping contacts based on criteria or manual assignment",
      "resource": "segments"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a segment",
      "description": "Permanently delete a segment by its segment ID. Contacts in the segment are not deleted.",
      "resource": "segments"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get segment details",
      "description": "Retrieve details of a specific segment including its name, contact count, and creation date",
      "resource": "segments"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all segments",
      "description": "Get all segments in the account with their names, IDs, and contact counts",
      "resource": "segments"
    },
    {
      "name": "List Contacts",
      "value": "listContacts",
      "action": "List segment contacts",
      "description": "Get all contacts belonging to a specific segment",
      "resource": "segments"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a subscription topic",
      "description": "Create a new subscription topic for organizing email preferences and managing unsubscribe options",
      "resource": "topics"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a topic",
      "description": "Permanently delete a subscription topic by its topic ID",
      "resource": "topics"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get topic details",
      "description": "Retrieve details of a specific topic including its name, description, and subscriber count",
      "resource": "topics"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all topics",
      "description": "Get all subscription topics with their names, IDs, and subscriber counts",
      "resource": "topics"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a topic",
      "description": "Update a topic's name or description",
      "resource": "topics"
    },
    {
      "name": "Add to Segment",
      "value": "addToSegment",
      "action": "Add contact to a segment",
      "description": "Add an existing contact to a segment by providing the contact ID and segment ID",
      "resource": "contacts"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a new contact",
      "description": "Create a new contact with email address, optional name, custom properties, and segment/topic assignments",
      "resource": "contacts"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a contact",
      "description": "Permanently delete a contact by their contact ID",
      "resource": "contacts"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get contact details",
      "description": "Retrieve a contact's details including email, name, properties, and subscription status by contact ID",
      "resource": "contacts"
    },
    {
      "name": "Get Topics",
      "value": "getTopics",
      "action": "Get contact topic subscriptions",
      "description": "Get all topic subscriptions (opt-in/opt-out status) for a specific contact",
      "resource": "contacts"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all contacts",
      "description": "Get a list of all contacts in the account with their email addresses and basic information",
      "resource": "contacts"
    },
    {
      "name": "List Segments",
      "value": "listSegments",
      "action": "List contact segments",
      "description": "Get all segments that a specific contact belongs to",
      "resource": "contacts"
    },
    {
      "name": "Remove From Segment",
      "value": "removeFromSegment",
      "action": "Remove contact from segment",
      "description": "Remove a contact from a segment by providing the contact ID and segment ID",
      "resource": "contacts"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update contact details",
      "description": "Update a contact's information including name, email, properties, or unsubscribe status",
      "resource": "contacts"
    },
    {
      "name": "Update Topics",
      "value": "updateTopics",
      "action": "Update contact topic subscriptions",
      "description": "Update a contact's topic subscription preferences (opt-in or opt-out of specific topics)",
      "resource": "contacts"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a contact property",
      "description": "Create a new custom contact property (metadata field) for storing additional information about contacts",
      "resource": "contactProperties"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a contact property",
      "description": "Permanently delete a contact property. This removes the property from all contacts.",
      "resource": "contactProperties"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get contact property details",
      "description": "Retrieve details of a specific contact property including its name, type, and configuration",
      "resource": "contactProperties"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all contact properties",
      "description": "Get all custom contact properties defined for storing contact metadata",
      "resource": "contactProperties"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a contact property",
      "description": "Update a contact property's name or configuration",
      "resource": "contactProperties"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a webhook endpoint",
      "description": "Create a new webhook endpoint to receive real-time notifications for email events like delivered, opened, clicked, bounced",
      "resource": "webhooks"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a webhook",
      "description": "Delete a webhook endpoint. Events will no longer be sent to this URL.",
      "resource": "webhooks"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get webhook details",
      "description": "Retrieve details of a webhook including its URL, subscribed events, and status",
      "resource": "webhooks"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all webhooks",
      "description": "Get all configured webhooks with their URLs, event subscriptions, and status",
      "resource": "webhooks"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a webhook",
      "description": "Update a webhook's URL or the events it is subscribed to",
      "resource": "webhooks"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get received email details",
      "description": "Retrieve a specific received email including sender, subject, body, and headers by email ID",
      "resource": "receivingEmails"
    },
    {
      "name": "Get Attachment",
      "value": "getAttachment",
      "action": "Download attachment from received email",
      "description": "Download a specific attachment from a received email by email ID and attachment ID",
      "resource": "receivingEmails"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all received emails",
      "description": "Get all emails received through inbound email processing with sender, subject, and timestamps",
      "resource": "receivingEmails"
    },
    {
      "name": "List Attachments",
      "value": "listAttachments",
      "action": "List attachments from received email",
      "description": "Get all attachments from a received email including filenames, sizes, and content types",
      "resource": "receivingEmails"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create a workflow",
      "description": "Create a new workflow to automate email sequences",
      "resource": "workflows"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete a workflow",
      "description": "Remove an existing workflow",
      "resource": "workflows"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get workflow details",
      "description": "Retrieve a single workflow with its steps and edges",
      "resource": "workflows"
    },
    {
      "name": "Get Run",
      "value": "getRun",
      "action": "Get a workflow run",
      "description": "Retrieve a single workflow run",
      "resource": "workflows"
    },
    {
      "name": "Get Run Step",
      "value": "getRunStep",
      "action": "Get a workflow run step",
      "description": "Retrieve a single workflow run step",
      "resource": "workflows"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all workflows",
      "description": "Retrieve a list of workflows",
      "resource": "workflows"
    },
    {
      "name": "List Run Steps",
      "value": "listRunSteps",
      "action": "List workflow run steps",
      "description": "Retrieve a list of workflow run steps",
      "resource": "workflows"
    },
    {
      "name": "List Runs",
      "value": "listRuns",
      "action": "List workflow runs",
      "description": "Retrieve a list of workflow runs",
      "resource": "workflows"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update a workflow",
      "description": "Update an existing workflow (enable or disable)",
      "resource": "workflows"
    },
    {
      "name": "Create",
      "value": "create",
      "action": "Create an event",
      "description": "Create a new event that can be used to trigger workflows",
      "resource": "events"
    },
    {
      "name": "Delete",
      "value": "delete",
      "action": "Delete an event",
      "description": "Remove an existing event by ID or name",
      "resource": "events"
    },
    {
      "name": "Get",
      "value": "get",
      "action": "Get event details",
      "description": "Retrieve a single event by ID or name",
      "resource": "events"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all events",
      "description": "Retrieve a list of events",
      "resource": "events"
    },
    {
      "name": "Send",
      "value": "send",
      "action": "Send an event",
      "description": "Send a named event to trigger matching workflows",
      "resource": "events"
    },
    {
      "name": "Update",
      "value": "update",
      "action": "Update an event",
      "description": "Update an existing event schema",
      "resource": "events"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List all logs",
      "description": "Get all API request logs for the account including request and response details",
      "resource": "logs"
    },
    {
      "name": "Retrieve",
      "value": "retrieve",
      "action": "Retrieve a log entry",
      "description": "Retrieve details of a specific log entry including the request body, response body, and response status",
      "resource": "logs"
    }
  ]

npm_readme:
  <h1 align="center">
    <br>
    <a href="/"><img src=".github/media/resend-header.png" alt="n8n-nodes-resend" width="900"></a>
    <br>
  </h1>

  <p align="center">
  	<img alt="NPM Version" src="https://img.shields.io/npm/v/n8n-nodes-resend">
  	<img alt="GitHub License" src="https://img.shields.io/github/license/resend/n8n-nodes-resend">
  	<img alt="NPM Downloads" src="https://img.shields.io/npm/dm/n8n-nodes-resend">
  	<img alt="NPM Last Update" src="https://img.shields.io/npm/last-update/n8n-nodes-resend">
  	<img alt="Static Badge" src="https://img.shields.io/badge/n8n-2.6.4-EA4B71?logo=n8n">
  </p>

  <p align="center">
    <a href="#installation">Installation</a> |
    <a href="#credentials">Credentials</a> |
    <a href="#human-in-the-loop">Human in the Loop</a> |
    <a href="#resources">Resources</a> |
    <a href="#trigger-events">Trigger Events</a> |
    <a href="#limitations">Limitations</a> |
    <a href="#development">Development</a>
  </p>

  ---

  The official node for [n8n](https://n8n.io) that integrates with the [Resend](https://resend.com) API. Send emails, manage contacts, handle domains, and receive webhooks.

  ## API Coverage

  > [!WARNING]
  > Audiences are deprecated in favor of Segments and won't be supported in this node. Please use Segments for contact grouping and targeting.

  The table below shows which endpoints are currently implemented:

  <details>
  <summary><strong>View all endpoints</strong></summary>

  | API Resource           | Endpoint              | Status  | Operations                                                                                                       |
  | ---------------------- | --------------------- | ------- | ---------------------------------------------------------------------------------------------------------------- |
  | **Email**              | `/emails`             | ✅ Full | Send, Send Batch, Send and Wait, List, Get, Update, Cancel, List Attachments, Get Attachment                     |
  | **Receiving Emails**   | `/emails/receiving`   | ✅ Full | List, Get, List Attachments, Get Attachment                                                                      |
  | **Domains**            | `/domains`            | ✅ Full | Create, List, Get, Update, Delete, Verify                                                                        |
  | **Templates**          | `/templates`          | ✅ Full | Create, List, Get, Update, Delete, Publish, Duplicate                                                            |
  | **Contacts**           | `/contacts`           | ✅ Full | Create, List, Get, Update, Delete, Add to Segment, List Segments, Remove from Segment, Get Topics, Update Topics |
  | **Broadcasts**         | `/broadcasts`         | ✅ Full | Create, List, Get, Update, Delete, Send                                                                          |
  | **Segments**           | `/segments`           | ✅ Full | Create, List, Get, Delete                                                                                        |
  | **Topics**             | `/topics`             | ✅ Full | Create, List, Get, Update, Delete                                                                                |
  | **Contact Properties** | `/contact-properties` | ✅ Full | Create, List, Get, Update, Delete                                                                                |
  | **Webhooks**           | `/webhooks`           | ✅ Full | Create, List, Get, Update, Delete                                                                                |

  </details>

  ## Installation

  1. Make a new workflow or open an existing one
  2. Open the nodes panel by selecting **+** or pressing **Tab**
  3. Search for **Resend**
  4. Select **Install** to install the node for your instance

  ## Credentials

  This package uses two separate credentials:

  ### Resend API

  1. Get your API key from [Resend Dashboard](https://resend.com/api-keys)
  2. In n8n, go to **Credentials** > **Add credential**
  3. Search for **Resend API** and paste your key

  This credential is used by the main **Resend** node for all API operations.

  ### Resend Webhook Signing Secret

  1. Create a webhook endpoint in your [Resend Dashboard](https://resend.com/webhooks)
  2. Copy the signing secret (starts with `whsec_`)
  3. In n8n, go to **Credentials** > **Add credential**
  4. Search for **Resend Webhook Signing Secret** and paste your secret

  This credential is used exclusively by the **Resend Trigger** node for webhook signature verification. Each webhook endpoint in Resend has its own unique signing secret.

  ## Human in the Loop

  The **Send and Wait for Response** operation enables human-in-the-loop workflows. Send an email and pause the workflow until the recipient responds via approval buttons or a form.

  ### Features

  - **Approval Workflows**: Send emails with Approve/Decline buttons for quick decisions
  - **Free Text Responses**: Collect text input via a response form
  - **Configurable Wait Time**: Set a timeout or wait indefinitely
  - **Secure Callbacks**: Responses use signed URLs for security

  ### How to Use

  The workflow pauses at the email step and resumes automatically once the recipient clicks a button or submits the response form.

  1. In the node panel, go to **Human in the Loop** > **Resend**
  2. Or select **Email** resource > **Send and Wait for Response** operation

  ### Configuration Options

  | Option                   | Description                                              |
  | ------------------------ | -------------------------------------------------------- |
  | **Response Type**        | Choose between Approval (buttons) or Free Text (form)    |
  | **Approval Type**        | Single button (Approve only) or Double (Approve/Decline) |
  | **Button Labels**        | Customize the button text                                |
  | **Button Styles**        | Primary or Secondary styling                             |
  | **Message Button Label** | Label for the form link button (Free Text mode)          |
  | **Response Form Title**  | Title shown on the response form                         |
  | **Limit Wait Time**      | Set a timeout for the wait period                        |

  ## Resources

  <details>
  <summary><strong>Email</strong></summary>

  | Operation        | Description                                       |
  | ---------------- | ------------------------------------------------- |
  | Send             | Send a single email with optional attachments     |
  | Send Batch       | Send up to 100 emails in one request              |
  | Send and Wait    | Send email and wait for recipient response (HITL) |
  | List             | List sent emails                                  |
  | Get              | Retrieve email details and status                 |
  | Cancel           | Cancel a scheduled email                          |
  | Update           | Modify a scheduled email                          |
  | List Attachments | List attachments for a sent email                 |
  | Get Attachment   | Get a specific attachment from a sent email       |

  </details>

  <details>
  <summary><strong>Receiving Email</strong></summary>

  | Operation        | Description                                     |
  | ---------------- | ----------------------------------------------- |
  | List             | List all received emails                        |
  | Get              | Retrieve a received email                       |
  | List Attachments | List attachments for a received email           |
  | Get Attachment   | Get a specific attachment from a received email |

  </details>

  <details>
  <summary><strong>Contact</strong></summary>

  | Operation           | Description                              |
  | ------------------- | ---------------------------------------- |
  | Create              | Add a new contact                        |
  | Get                 | Retrieve contact details                 |
  | Update              | Modify contact information               |
  | Delete              | Remove a contact                         |
  | List                | List all contacts                        |
  | Add to Segment      | Add a contact to a segment               |
  | List Segments       | List segments for a contact              |
  | Remove From Segment | Remove a contact from a segment          |
  | Get Topics          | Get topic subscriptions for a contact    |
  | Update Topics       | Update topic subscriptions for a contact |

  </details>

  <details>
  <summary><strong>Contact Property</strong></summary>

  | Operation | Description                      |
  | --------- | -------------------------------- |
  | Create    | Create a custom contact property |
  | Get       | Retrieve property details        |
  | Update    | Modify property settings         |
  | Delete    | Remove a property                |
  | List      | List all contact properties      |

  </details>

  <details>
  <summary><strong>Segment</strong></summary>

  | Operation | Description                                          |
  | --------- | ---------------------------------------------------- |
  | Create    | Create a new segment with optional filter conditions |
  | Get       | Retrieve segment details                             |
  | Delete    | Remove a segment                                     |
  | List      | List all segments                                    |

  </details>

  <details>
  <summary><strong>Topic</strong></summary>

  | Operation | Description                 |
  | --------- | --------------------------- |
  | Create    | Create a subscription topic |
  | Get       | Retrieve topic details      |
  | Update    | Modify topic settings       |
  | Delete    | Remove a topic              |
  | List      | List all topics             |

  </details>

  <details>
  <summary><strong>Broadcast</strong></summary>

  | Operation | Description                   |
  | --------- | ----------------------------- |
  | Create    | Create an email campaign      |
  | Get       | Retrieve broadcast details    |
  | Send      | Send a broadcast to a segment |
  | Update    | Modify broadcast settings     |
  | Delete    | Remove a broadcast            |
  | List      | List all broadcasts           |

  </details>

  <details>
  <summary><strong>Template</strong></summary>

  | Operation | Description                    |
  | --------- | ------------------------------ |
  | Create    | Create an email template       |
  | Get       | Retrieve template details      |
  | Update    | Modify a template              |
  | Delete    | Remove a template              |
  | List      | List all templates             |
  | Publish   | Publish a template             |
  | Duplicate | Duplicate an existing template |

  </details>

  <details>
  <summary><strong>Domain</strong></summary>

  | Operation | Description                 |
  | --------- | --------------------------- |
  | Create    | Add a sending domain        |
  | Get       | Retrieve domain details     |
  | Verify    | Trigger domain verification |
  | Update    | Modify domain settings      |
  | Delete    | Remove a domain             |
  | List      | List all domains            |

  </details>

  <details>
  <summary><strong>Webhook</strong></summary>

  | Operation | Description               |
  | --------- | ------------------------- |
  | Create    | Create a webhook endpoint |
  | Get       | Retrieve webhook details  |
  | Update    | Modify webhook settings   |
  | Delete    | Remove a webhook          |
  | List      | List all webhooks         |

  </details>

  ## Trigger Events

  The **Resend Trigger** node receives webhooks for real-time email events. Signatures are automatically verified using Svix.

  > **Note:** The trigger node requires the **Resend Webhook Signing Secret** credential (separate from the Resend API credential). See the [Credentials](#credentials) section for setup instructions.

  <details>
  <summary><strong>View all events</strong></summary>

  | Event                    | Description                          |
  | ------------------------ | ------------------------------------ |
  | `email.sent`             | Email sent to recipient              |
  | `email.delivered`        | Email delivered successfully         |
  | `email.delivery_delayed` | Email delivery delayed               |
  | `email.opened`           | Recipient opened the email           |
  | `email.clicked`          | Link clicked in email                |
  | `email.bounced`          | Email bounced                        |
  | `email.complained`       | Spam complaint received              |
  | `email.failed`           | Email failed to send due to an error |
  | `email.received`         | Inbound email received by Resend     |
  | `email.scheduled`        | Email scheduled to be sent           |
  | `email.suppressed`       | Email suppressed by Resend           |
  | `contact.created`        | New contact added                    |
  | `contact.updated`        | Contact modified                     |
  | `contact.deleted`        | Contact removed                      |
  | `domain.created`         | New domain added                     |
  | `domain.updated`         | Domain modified                      |
  | `domain.deleted`         | Domain removed                       |

  </details>

  ## Limitations

  - Maximum email size: 40MB (including attachments)
  - Attachments not supported with scheduled emails

  ## Development

  ```bash
  git clone https://github.com/resend/n8n-nodes-resend.git
  cd n8n-nodes-resend
  npm install
  npm run build
  npm run lint
  ```

  ## License

  [MIT](LICENSE.md)

  <p align="center">
    <a href="https://github.com/resend/n8n-nodes-resend">GitHub</a> |
    <a href="https://github.com/resend/n8n-nodes-resend/issues">Issues</a> |
    <a href="https://resend.com/docs">Resend Docs</a>
  </p>

ai_documentation_summary:
  {"purpose":"This n8n node integrates with the Resend email platform to send emails, manage contacts, create broadcasts, handle templates and domains, and receive webhooks.","capabilities":["Send emails (single, batch, with wait)","List and retrieve email details and attachments","Manage email domains (create, verify, update, delete)","Create and manage email templates","Manage contacts and segments","Create and send broadcasts","Manage topics and contact properties","Set up and manage webhooks","Human-in-the-loop workflows with approval buttons and form responses"],"authentication":"API key required (Resend API key) and Webhook Signing Secret (whsec_) for trigger nodes","commonUseCases":["Transactional email notifications","Marketing email campaigns and broadcasts","Customer segmentation and targeted messaging","Email verification and domain management","Workflow pauses for user approval or feedback collection"],"limitations":["Audiences feature is deprecated; only Segments are supported","Webhook signing secrets are unique per endpoint and must be configured separately"],"relatedNodes":["Resend Trigger","Human in the Loop (Resend)","Generic HTTP Request","Generic Webhook"]}


node_type: @algolia/n8n-nodes-algolia.algolia
package_name: @algolia/n8n-nodes-algolia
display_name: Algolia
description: Use Algolia in your n8n workflows
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: ejaldorau
author_github_url: https://github.com/algolia/n8n-nodes-algolia.git
npm_package_name: @algolia/n8n-nodes-algolia
npm_version: 0.15.0
community_fetched_at: 2026-07-14T14:40:33.523Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Advanced",
          "value": "Advanced",
          "description": "Query your logs"
        },
        {
          "name": "Api Keys",
          "value": "Api Keys",
          "description": "Manage your API keys"
        },
        {
          "name": "Clusters",
          "value": "Clusters",
          "description": "Multi-cluster operations"
        },
        {
          "name": "Dictionaries",
          "value": "Dictionaries",
          "description": "Manage your dictionaries"
        },
        {
          "name": "Indices",
          "value": "Indices",
          "description": "Manage your indices and index settings"
        },
        {
          "name": "Records",
          "value": "Records",
          "description": "Add, update, and delete records from your indices"
        },
        {
          "name": "Rules",
          "value": "Rules",
          "description": "Create, update, delete, and search for rules"
        },
        {
          "name": "Search",
          "value": "Search",
          "description": "Search one or more indices for matching records or facet values"
        },
        {
          "name": "Synonyms",
          "value": "Synonyms",
          "description": "Create, update, delete, and search for synonyms"
        },
        {
          "name": "Vaults",
          "value": "Vaults",
          "description": "Algolia Vault lets you restrict access to your clusters to specific IP addresses and provides disk-level encryption at rest"
        }
      ],
      "description": "Select the resource to work with",
      "displayName": "Resource"
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Search an index",
          "value": "searchSingleIndex",
          "action": "Search an index",
          "routing": {
            "output": {
              "postReceive": [
                null
              ]
            },
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/query",
              "method": "POST"
            }
          },
          "description": "Searches a single index and returns matching search results as hits.",
          "inputSchema": {
            "simplifiedOutput": [
              "hits"
            ]
          }
        },
        {
          "name": "Search multiple indices",
          "value": "search",
          "action": "Search multiple indices",
          "routing": {
            "request": {
              "url": "=/1/indexes/*/queries",
              "method": "POST"
            }
          },
          "description": "Sends multiple search requests to one or more indices."
        },
        {
          "name": "Search for facet values",
          "value": "searchForFacetValues",
          "action": "Search for facet values",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/facets/{{ $parameter.facetName_string }}/query",
              "method": "POST"
            }
          },
          "description": "Searches for values of a specified facet attribute."
        },
        {
          "name": "Browse for records",
          "value": "browse",
          "action": "Browse for records",
          "routing": {
            "output": {
              "postReceive": [
                null
              ]
            },
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/browse",
              "method": "POST"
            }
          },
          "description": "Retrieves records from an index, up to 1,000 per request.",
          "inputSchema": {
            "simplifiedOutput": [
              "hits"
            ]
          }
        }
      ],
      "description": "Select the operation to work with",
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "Search"
          ]
        }
      }
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Add a new record (with auto-generated object ID)",
          "value": "saveObject",
          "action": "Add a new record (with auto-generated object ID)",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}",
              "method": "POST"
            }
          },
          "description": "Adds a record to an index or replaces it."
        },
        {
          "name": "Retrieve a record",
          "value": "getObject",
          "action": "Retrieve a record",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/{{ $parameter.objectID_string }}",
              "method": "GET"
            }
          },
          "description": "Retrieves one record by its object ID."
        },
        {
          "name": "Add or replace a record",
          "value": "addOrUpdateObject",
          "action": "Add or replace a record",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/{{ $parameter.objectID_string }}",
              "method": "PUT"
            }
          },
          "description": "If a record with the specified object ID exists, the existing record is replaced."
        },
        {
          "name": "Delete a record",
          "value": "deleteObject",
          "action": "Delete a record",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/{{ $parameter.objectID_string }}",
              "method": "DELETE"
            }
          },
          "description": "Deletes a record by its object ID."
        },
        {
          "name": "Delete records matching a filter",
          "value": "deleteBy",
          "action": "Delete records matching a filter",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/deleteByQuery",
              "method": "POST"
            }
          },
          "description": "This operation doesn't accept empty filters."
        },
        {
          "name": "Delete all records from an index",
          "value": "clearObjects",
          "action": "Delete all records from an index",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/clear",
              "method": "POST"
            }
          },
          "description": "Deletes only the records from an index while keeping settings, synonyms, and rules."
        },
        {
          "name": "Add or update attributes",
          "value": "partialUpdateObject",
          "action": "Add or update attributes",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/{{ $parameter.objectID_string }}/partial",
              "method": "POST"
            }
          },
          "description": "Adds new attributes to a record, or updates existing ones."
        },
        {
          "name": "Batch indexing operations on one index",
          "value": "batch",
          "action": "Batch indexing operations on one index",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/batch",
              "method": "POST"
            }
          },
          "description": "Adds, updates, or deletes records in one index with a single API request."
        },
        {
          "name": "Batch indexing operations on multiple indices",
          "value": "multipleBatch",
          "action": "Batch indexing operations on multiple indices",
          "routing": {
            "request": {
              "url": "=/1/indexes/*/batch",
              "method": "POST"
            }
          },
          "description": "Adds, updates, or deletes records in multiple indices with a single API request."
        },
        {
          "name": "Retrieve records",
          "value": "getObjects",
          "action": "Retrieve records",
          "routing": {
            "request": {
              "url": "=/1/indexes/*/objects",
              "method": "POST"
            }
          },
          "description": "Retrieves one or more records, potentially from different indices."
        }
      ],
      "description": "Select the operation to work with",
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "Records"
          ]
        }
      }
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Delete an index",
          "value": "deleteIndex",
          "action": "Delete an index",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}",
              "method": "DELETE"
            }
          },
          "description": "Deletes an index and all its settings."
        },
        {
          "name": "Retrieve index settings",
          "value": "getSettings",
          "action": "Retrieve index settings",
          "routing": {
            "output": {
              "postReceive": [
                null
              ]
            },
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/settings",
              "method": "GET"
            }
          },
          "description": "Retrieves an object with non-null index settings.",
          "inputSchema": {
            "simplifiedOutput": [
              "advancedSyntax",
              "advancedSyntaxFeatures",
              "allowCompressionOfIntegerArray",
              "allowTyposOnNumericTokens",
              "alternativesAsExact",
              "attributeCriteriaComputedByMinProximity",
              "attributeForDistinct",
              "attributesForFaceting",
              "attributesToHighlight",
              "attributesToRetrieve"
            ]
          }
        },
        {
          "name": "Update index settings",
          "value": "setSettings",
          "action": "Update index settings",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/settings",
              "method": "PUT"
            }
          },
          "description": "Update the specified index settings."
        },
        {
          "name": "Check task status",
          "value": "getTask",
          "action": "Check task status",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/task/{{ $parameter.taskID_number }}",
              "method": "GET"
            }
          },
          "description": "Checks the status of a given task."
        },
        {
          "name": "Copy or move an index",
          "value": "operationIndex",
          "action": "Copy or move an index",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/operation",
              "method": "POST"
            }
          },
          "description": "Copies or moves (renames) an index within the same Algolia application."
        },
        {
          "name": "List indices",
          "value": "listIndices",
          "action": "List indices",
          "routing": {
            "request": {
              "url": "=/1/indexes",
              "method": "GET"
            }
          },
          "description": "Lists all indices in the current Algolia application."
        }
      ],
      "description": "Select the operation to work with",
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "Indices"
          ]
        }
      }
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Retrieve a synonym",
          "value": "getSynonym",
          "action": "Retrieve a synonym",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/{{ $parameter.objectID_string }}",
              "method": "GET"
            }
          },
          "description": "Retrieves a synonym by its ID."
        },
        {
          "name": "Create or replace a synonym",
          "value": "saveSynonym",
          "action": "Create or replace a synonym",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/{{ $parameter.objectID_string }}",
              "method": "PUT"
            }
          },
          "description": "If a synonym with the specified object ID doesn't exist, Algolia adds a new one."
        },
        {
          "name": "Delete a synonym",
          "value": "deleteSynonym",
          "action": "Delete a synonym",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/{{ $parameter.objectID_string }}",
              "method": "DELETE"
            }
          },
          "description": "Deletes a synonym by its ID."
        },
        {
          "name": "Create or replace synonyms",
          "value": "saveSynonyms",
          "action": "Create or replace synonyms",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/batch",
              "method": "POST"
            }
          },
          "description": "If a synonym with the `objectID` doesn't exist, Algolia adds a new one."
        },
        {
          "name": "Delete all synonyms",
          "value": "clearSynonyms",
          "action": "Delete all synonyms",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/clear",
              "method": "POST"
            }
          },
          "description": "Deletes all synonyms from the index."
        },
        {
          "name": "Search for synonyms",
          "value": "searchSynonyms",
          "action": "Search for synonyms",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/search",
              "method": "POST"
            }
          },
          "description": "Searches for synonyms in your index."
        }
      ],
      "description": "Select the operation to work with",
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "Synonyms"
          ]
        }
      }
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "List API keys",
          "value": "listApiKeys",
          "action": "List API keys",
          "routing": {
            "request": {
              "url": "=/1/keys",
              "method": "GET"
            }
          },
          "description": "Lists all API keys associated with your Algolia application, including their permissions and restrictions."
        },
        {
          "name": "Create an API key",
          "value": "addApiKey",
          "action": "Create an API key",
          "routing": {
            "request": {
              "url": "=/1/keys",
              "method": "POST"
            }
          },
          "description": "Creates a new API key with specific permissions and restrictions."
        },
        {
          "name": "Retrieve API key permissions",
          "value": "getApiKey",
          "action": "Retrieve API key permissions",
          "routing": {
            "request": {
              "url": "=/1/keys/{{ $parameter.key_string }}",
              "method": "GET"
            }
          },
          "description": "Gets the permissions and restrictions of an API key."
        },
        {
          "name": "Update an API key",
          "value": "updateApiKey",
          "action": "Update an API key",
          "routing": {
            "request": {
              "url": "=/1/keys/{{ $parameter.key_string }}",
              "method": "PUT"
            }
          },
          "description": "Replaces the permissions of an existing API key."
        },
        {
          "name": "Delete an API key",
          "value": "deleteApiKey",
          "action": "Delete an API key",
          "routing": {
            "request": {
              "url": "=/1/keys/{{ $parameter.key_string }}",
              "method": "DELETE"
            }
          },
          "description": "Deletes the API key."
        },
        {
          "name": "Restore an API key",
          "value": "restoreApiKey",
          "action": "Restore an API key",
          "routing": {
            "request": {
              "url": "=/1/keys/{{ $parameter.key_string }}/restore",
              "method": "POST"
            }
          },
          "description": "Restores a deleted API key."
        }
      ],
      "description": "Select the operation to work with",
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "Api Keys"
          ]
        }
      }
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Retrieve a rule",
          "value": "getRule",
          "action": "Retrieve a rule",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/{{ $parameter.objectID_string }}",
              "method": "GET"
            }
          },
          "description": "Retrieves a rule by its ID."
        },
        {
          "name": "Create or replace a rule",
          "value": "saveRule",
          "action": "Create or replace a rule",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/{{ $parameter.objectID_string }}",
              "method": "PUT"
            }
          },
          "description": "If a rule with the specified object ID doesn't exist, it's created."
        },
        {
          "name": "Delete a rule",
          "value": "deleteRule",
          "action": "Delete a rule",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/{{ $parameter.objectID_string }}",
              "method": "DELETE"
            }
          },
          "description": "Deletes a rule by its ID."
        },
        {
          "name": "Create or update rules",
          "value": "saveRules",
          "action": "Create or update rules",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/batch",
              "method": "POST"
            }
          },
          "description": "Create or update multiple rules."
        },
        {
          "name": "Delete all rules",
          "value": "clearRules",
          "action": "Delete all rules",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/clear",
              "method": "POST"
            }
          },
          "description": "Deletes all rules from the index."
        },
        {
          "name": "Search for rules",
          "value": "searchRules",
          "action": "Search for rules",
          "routing": {
            "request": {
              "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/search",
              "method": "POST"
            }
          },
          "description": "Searches for rules in your index."
        }
      ],
      "description": "Select the operation to work with",
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "Rules"
          ]
        }
      }
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Add or delete dictionary entries",
          "value": "batchDictionaryEntries",
          "action": "Add or delete dictionary entries",
          "routing": {
            "request": {
              "url": "=/1/dictionaries/{{ $parameter.dictionaryName_options }}/batch",
              "method": "POST"
            }
          },
          "description": "Adds or deletes multiple entries from your plurals, segmentation, or stop word dictionaries."
        },
        {
          "name": "Search dictionary entries",
          "value": "searchDictionaryEntries",
          "action": "Search dictionary entries",
          "routing": {
            "request": {
              "url": "=/1/dictionaries/{{ $parameter.dictionaryName_options }}/search",
              "method": "POST"
            }
          },
          "description": "Searches for standard and custom dictionary entries."
        },
        {
          "name": "Retrieve dictionary settings",
          "value": "getDictionarySettings",
          "action": "Retrieve dictionary settings",
          "routing": {
            "request": {
              "url": "=/1/dictionaries/*/settings",
              "method": "GET"
            }
          },
          "description": "Retrieves the languages for which standard dictionary entries are turned off."
        },
        {
          "name": "Update dictionary settings",
          "value": "setDictionarySettings",
          "action": "Update dictionary settings",
          "routing": {
            "request": {
              "url": "=/1/dictionaries/*/settings",
              "method": "PUT"
            }
          },
          "description": "Turns standard stop word dictionary entries on or off for a given language."
        },
        {
          "name": "List available languages",
          "value": "getDictionaryLanguages",
          "action": "List available languages",
          "routing": {
            "request": {
              "url": "=/1/dictionaries/*/languages",
              "method": "GET"
            }
          },
          "description": "Lists supported languages with their supported dictionary types and number of custom entries."
        }
      ],
      "description": "Select the operation to work with",
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "Dictionaries"
          ]
        }
      }
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Assign or move a user ID",
          "value": "assignUserId",
          "action": "Assign or move a user ID",
          "routing": {
            "request": {
              "url": "=/1/clusters/mapping",
              "method": "POST"
            }
          },
          "description": "Assigns or moves a user ID to a cluster."
        },
        {
          "name": "List user IDs",
          "value": "listUserIds",
          "action": "List user IDs",
          "routing": {
            "request": {
              "url": "=/1/clusters/mapping",
              "method": "GET"
            }
          },
          "description": "Lists the userIDs assigned to a multi-cluster application."
        },
        {
          "name": "Assign multiple userIDs",
          "value": "batchAssignUserIds",
          "action": "Assign multiple userIDs",
          "routing": {
            "request": {
              "url": "=/1/clusters/mapping/batch",
              "method": "POST"
            }
          },
          "description": "Assigns multiple user IDs to a cluster."
        },
        {
          "name": "Get top user IDs",
          "value": "getTopUserIds",
          "action": "Get top user IDs",
          "routing": {
            "request": {
              "url": "=/1/clusters/mapping/top",
              "method": "GET"
            }
          },
          "description": "Get the IDs of the 10 users with the highest number of records per cluster."
        },
        {
          "name": "Retrieve user ID",
          "value": "getUserId",
          "action": "Retrieve user ID",
          "routing": {
            "request": {
              "url": "=/1/clusters/mapping/{{ $parameter.userID_string }}",
              "method": "GET"
            }
          },
          "description": "Returns the user ID data stored in the mapping."
        },
        {
          "name": "Delete user ID",
          "value": "removeUserId",
          "action": "Delete user ID",
          "routing": {
            "request": {
              "url": "=/1/clusters/mapping/{{ $parameter.userID_string }}",
              "method": "DELETE"
            }
          },
          "description": "Deletes a user ID and its associated data from the clusters."
        },
        {
          "name": "List clusters",
          "value": "listClusters",
          "action": "List clusters",
          "routing": {
            "request": {
              "url": "=/1/clusters",
              "method": "GET"
            }
          },
          "description": "Lists the available clusters in a multi-cluster setup."
        },
        {
          "name": "Search for user IDs",
          "value": "searchUserIds",
          "action": "Search for user IDs",
          "routing": {
            "request": {
              "url": "=/1/clusters/mapping/search",
              "method": "POST"
            }
          },
          "description": "Since it can take a few seconds to get the data from the different clusters,"
        },
        {
          "name": "Get migration and user mapping status",
          "value": "hasPendingMappings",
          "action": "Get migration and user mapping status",
          "routing": {
            "request": {
              "url": "=/1/clusters/mapping/pending",
              "method": "GET"
            }
          },
          "description": "To determine when the time-consuming process of creating a large batch of users or migrating users from one cluster to another is complete, this operation retrieves the status of the process."
        }
      ],
      "description": "Select the operation to work with",
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "Clusters"
          ]
        }
      }
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "List allowed sources",
          "value": "getSources",
          "action": "List allowed sources",
          "routing": {
            "request": {
              "url": "=/1/security/sources",
              "method": "GET"
            }
          },
          "description": "Retrieves all allowed IP addresses with access to your application."
        },
        {
          "name": "Replace allowed sources",
          "value": "replaceSources",
          "action": "Replace allowed sources",
          "routing": {
            "request": {
              "url": "=/1/security/sources",
              "method": "PUT"
            }
          },
          "description": "Replaces the list of allowed sources."
        },
        {
          "name": "Add a source",
          "value": "appendSource",
          "action": "Add a source",
          "routing": {
            "request": {
              "url": "=/1/security/sources/append",
              "method": "POST"
            }
          },
          "description": "Adds a source to the list of allowed sources."
        },
        {
          "name": "Delete a source",
          "value": "deleteSource",
          "action": "Delete a source",
          "routing": {
            "request": {
              "url": "=/1/security/sources/{{ $parameter.source_string }}",
              "method": "DELETE"
            }
          },
          "description": "Deletes a source from the list of allowed sources."
        }
      ],
      "description": "Select the operation to work with",
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "Vaults"
          ]
        }
      }
    },
    {
      "name": "operation",
      "type": "options",
      "default": "",
      "options": [
        {
          "name": "Retrieve log entries",
          "value": "getLogs",
          "action": "Retrieve log entries",
          "routing": {
            "request": {
              "url": "=/1/logs",
              "method": "GET"
            }
          },
          "description": "The request must be authenticated by an API key with the [`logs` ACL](https://www.algolia.com/doc/guides/security/api-keys/#access-control-list-acl)."
        },
        {
          "name": "Check application task status",
          "value": "getAppTask",
          "action": "Check application task status",
          "routing": {
            "request": {
              "url": "=/1/task/{{ $parameter.taskID_number }}",
              "method": "GET"
            }
          },
          "description": "Checks the status of a given application task."
        }
      ],
      "description": "Select the operation to work with",
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "Advanced"
          ]
        }
      }
    }
  ]

operations:
  [
    {
      "name": "Search an index",
      "value": "searchSingleIndex",
      "action": "Search an index",
      "routing": {
        "output": {
          "postReceive": [
            null
          ]
        },
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/query",
          "method": "POST"
        }
      },
      "description": "Searches a single index and returns matching search results as hits.",
      "inputSchema": {
        "simplifiedOutput": [
          "hits"
        ]
      },
      "resource": "Search"
    },
    {
      "name": "Search multiple indices",
      "value": "search",
      "action": "Search multiple indices",
      "routing": {
        "request": {
          "url": "=/1/indexes/*/queries",
          "method": "POST"
        }
      },
      "description": "Sends multiple search requests to one or more indices.",
      "resource": "Search"
    },
    {
      "name": "Search for facet values",
      "value": "searchForFacetValues",
      "action": "Search for facet values",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/facets/{{ $parameter.facetName_string }}/query",
          "method": "POST"
        }
      },
      "description": "Searches for values of a specified facet attribute.",
      "resource": "Search"
    },
    {
      "name": "Browse for records",
      "value": "browse",
      "action": "Browse for records",
      "routing": {
        "output": {
          "postReceive": [
            null
          ]
        },
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/browse",
          "method": "POST"
        }
      },
      "description": "Retrieves records from an index, up to 1,000 per request.",
      "inputSchema": {
        "simplifiedOutput": [
          "hits"
        ]
      },
      "resource": "Search"
    },
    {
      "name": "Add a new record (with auto-generated object ID)",
      "value": "saveObject",
      "action": "Add a new record (with auto-generated object ID)",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}",
          "method": "POST"
        }
      },
      "description": "Adds a record to an index or replaces it.",
      "resource": "Records"
    },
    {
      "name": "Retrieve a record",
      "value": "getObject",
      "action": "Retrieve a record",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/{{ $parameter.objectID_string }}",
          "method": "GET"
        }
      },
      "description": "Retrieves one record by its object ID.",
      "resource": "Records"
    },
    {
      "name": "Add or replace a record",
      "value": "addOrUpdateObject",
      "action": "Add or replace a record",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/{{ $parameter.objectID_string }}",
          "method": "PUT"
        }
      },
      "description": "If a record with the specified object ID exists, the existing record is replaced.",
      "resource": "Records"
    },
    {
      "name": "Delete a record",
      "value": "deleteObject",
      "action": "Delete a record",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/{{ $parameter.objectID_string }}",
          "method": "DELETE"
        }
      },
      "description": "Deletes a record by its object ID.",
      "resource": "Records"
    },
    {
      "name": "Delete records matching a filter",
      "value": "deleteBy",
      "action": "Delete records matching a filter",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/deleteByQuery",
          "method": "POST"
        }
      },
      "description": "This operation doesn't accept empty filters.",
      "resource": "Records"
    },
    {
      "name": "Delete all records from an index",
      "value": "clearObjects",
      "action": "Delete all records from an index",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/clear",
          "method": "POST"
        }
      },
      "description": "Deletes only the records from an index while keeping settings, synonyms, and rules.",
      "resource": "Records"
    },
    {
      "name": "Add or update attributes",
      "value": "partialUpdateObject",
      "action": "Add or update attributes",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/{{ $parameter.objectID_string }}/partial",
          "method": "POST"
        }
      },
      "description": "Adds new attributes to a record, or updates existing ones.",
      "resource": "Records"
    },
    {
      "name": "Batch indexing operations on one index",
      "value": "batch",
      "action": "Batch indexing operations on one index",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/batch",
          "method": "POST"
        }
      },
      "description": "Adds, updates, or deletes records in one index with a single API request.",
      "resource": "Records"
    },
    {
      "name": "Batch indexing operations on multiple indices",
      "value": "multipleBatch",
      "action": "Batch indexing operations on multiple indices",
      "routing": {
        "request": {
          "url": "=/1/indexes/*/batch",
          "method": "POST"
        }
      },
      "description": "Adds, updates, or deletes records in multiple indices with a single API request.",
      "resource": "Records"
    },
    {
      "name": "Retrieve records",
      "value": "getObjects",
      "action": "Retrieve records",
      "routing": {
        "request": {
          "url": "=/1/indexes/*/objects",
          "method": "POST"
        }
      },
      "description": "Retrieves one or more records, potentially from different indices.",
      "resource": "Records"
    },
    {
      "name": "Delete an index",
      "value": "deleteIndex",
      "action": "Delete an index",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}",
          "method": "DELETE"
        }
      },
      "description": "Deletes an index and all its settings.",
      "resource": "Indices"
    },
    {
      "name": "Retrieve index settings",
      "value": "getSettings",
      "action": "Retrieve index settings",
      "routing": {
        "output": {
          "postReceive": [
            null
          ]
        },
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/settings",
          "method": "GET"
        }
      },
      "description": "Retrieves an object with non-null index settings.",
      "inputSchema": {
        "simplifiedOutput": [
          "advancedSyntax",
          "advancedSyntaxFeatures",
          "allowCompressionOfIntegerArray",
          "allowTyposOnNumericTokens",
          "alternativesAsExact",
          "attributeCriteriaComputedByMinProximity",
          "attributeForDistinct",
          "attributesForFaceting",
          "attributesToHighlight",
          "attributesToRetrieve"
        ]
      },
      "resource": "Indices"
    },
    {
      "name": "Update index settings",
      "value": "setSettings",
      "action": "Update index settings",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/settings",
          "method": "PUT"
        }
      },
      "description": "Update the specified index settings.",
      "resource": "Indices"
    },
    {
      "name": "Check task status",
      "value": "getTask",
      "action": "Check task status",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/task/{{ $parameter.taskID_number }}",
          "method": "GET"
        }
      },
      "description": "Checks the status of a given task.",
      "resource": "Indices"
    },
    {
      "name": "Copy or move an index",
      "value": "operationIndex",
      "action": "Copy or move an index",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/operation",
          "method": "POST"
        }
      },
      "description": "Copies or moves (renames) an index within the same Algolia application.",
      "resource": "Indices"
    },
    {
      "name": "List indices",
      "value": "listIndices",
      "action": "List indices",
      "routing": {
        "request": {
          "url": "=/1/indexes",
          "method": "GET"
        }
      },
      "description": "Lists all indices in the current Algolia application.",
      "resource": "Indices"
    },
    {
      "name": "Retrieve a synonym",
      "value": "getSynonym",
      "action": "Retrieve a synonym",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/{{ $parameter.objectID_string }}",
          "method": "GET"
        }
      },
      "description": "Retrieves a synonym by its ID.",
      "resource": "Synonyms"
    },
    {
      "name": "Create or replace a synonym",
      "value": "saveSynonym",
      "action": "Create or replace a synonym",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/{{ $parameter.objectID_string }}",
          "method": "PUT"
        }
      },
      "description": "If a synonym with the specified object ID doesn't exist, Algolia adds a new one.",
      "resource": "Synonyms"
    },
    {
      "name": "Delete a synonym",
      "value": "deleteSynonym",
      "action": "Delete a synonym",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/{{ $parameter.objectID_string }}",
          "method": "DELETE"
        }
      },
      "description": "Deletes a synonym by its ID.",
      "resource": "Synonyms"
    },
    {
      "name": "Create or replace synonyms",
      "value": "saveSynonyms",
      "action": "Create or replace synonyms",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/batch",
          "method": "POST"
        }
      },
      "description": "If a synonym with the `objectID` doesn't exist, Algolia adds a new one.",
      "resource": "Synonyms"
    },
    {
      "name": "Delete all synonyms",
      "value": "clearSynonyms",
      "action": "Delete all synonyms",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/clear",
          "method": "POST"
        }
      },
      "description": "Deletes all synonyms from the index.",
      "resource": "Synonyms"
    },
    {
      "name": "Search for synonyms",
      "value": "searchSynonyms",
      "action": "Search for synonyms",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/synonyms/search",
          "method": "POST"
        }
      },
      "description": "Searches for synonyms in your index.",
      "resource": "Synonyms"
    },
    {
      "name": "List API keys",
      "value": "listApiKeys",
      "action": "List API keys",
      "routing": {
        "request": {
          "url": "=/1/keys",
          "method": "GET"
        }
      },
      "description": "Lists all API keys associated with your Algolia application, including their permissions and restrictions.",
      "resource": "Api Keys"
    },
    {
      "name": "Create an API key",
      "value": "addApiKey",
      "action": "Create an API key",
      "routing": {
        "request": {
          "url": "=/1/keys",
          "method": "POST"
        }
      },
      "description": "Creates a new API key with specific permissions and restrictions.",
      "resource": "Api Keys"
    },
    {
      "name": "Retrieve API key permissions",
      "value": "getApiKey",
      "action": "Retrieve API key permissions",
      "routing": {
        "request": {
          "url": "=/1/keys/{{ $parameter.key_string }}",
          "method": "GET"
        }
      },
      "description": "Gets the permissions and restrictions of an API key.",
      "resource": "Api Keys"
    },
    {
      "name": "Update an API key",
      "value": "updateApiKey",
      "action": "Update an API key",
      "routing": {
        "request": {
          "url": "=/1/keys/{{ $parameter.key_string }}",
          "method": "PUT"
        }
      },
      "description": "Replaces the permissions of an existing API key.",
      "resource": "Api Keys"
    },
    {
      "name": "Delete an API key",
      "value": "deleteApiKey",
      "action": "Delete an API key",
      "routing": {
        "request": {
          "url": "=/1/keys/{{ $parameter.key_string }}",
          "method": "DELETE"
        }
      },
      "description": "Deletes the API key.",
      "resource": "Api Keys"
    },
    {
      "name": "Restore an API key",
      "value": "restoreApiKey",
      "action": "Restore an API key",
      "routing": {
        "request": {
          "url": "=/1/keys/{{ $parameter.key_string }}/restore",
          "method": "POST"
        }
      },
      "description": "Restores a deleted API key.",
      "resource": "Api Keys"
    },
    {
      "name": "Retrieve a rule",
      "value": "getRule",
      "action": "Retrieve a rule",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/{{ $parameter.objectID_string }}",
          "method": "GET"
        }
      },
      "description": "Retrieves a rule by its ID.",
      "resource": "Rules"
    },
    {
      "name": "Create or replace a rule",
      "value": "saveRule",
      "action": "Create or replace a rule",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/{{ $parameter.objectID_string }}",
          "method": "PUT"
        }
      },
      "description": "If a rule with the specified object ID doesn't exist, it's created.",
      "resource": "Rules"
    },
    {
      "name": "Delete a rule",
      "value": "deleteRule",
      "action": "Delete a rule",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/{{ $parameter.objectID_string }}",
          "method": "DELETE"
        }
      },
      "description": "Deletes a rule by its ID.",
      "resource": "Rules"
    },
    {
      "name": "Create or update rules",
      "value": "saveRules",
      "action": "Create or update rules",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/batch",
          "method": "POST"
        }
      },
      "description": "Create or update multiple rules.",
      "resource": "Rules"
    },
    {
      "name": "Delete all rules",
      "value": "clearRules",
      "action": "Delete all rules",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/clear",
          "method": "POST"
        }
      },
      "description": "Deletes all rules from the index.",
      "resource": "Rules"
    },
    {
      "name": "Search for rules",
      "value": "searchRules",
      "action": "Search for rules",
      "routing": {
        "request": {
          "url": "=/1/indexes/{{ $parameter.indexName_string }}/rules/search",
          "method": "POST"
        }
      },
      "description": "Searches for rules in your index.",
      "resource": "Rules"
    },
    {
      "name": "Add or delete dictionary entries",
      "value": "batchDictionaryEntries",
      "action": "Add or delete dictionary entries",
      "routing": {
        "request": {
          "url": "=/1/dictionaries/{{ $parameter.dictionaryName_options }}/batch",
          "method": "POST"
        }
      },
      "description": "Adds or deletes multiple entries from your plurals, segmentation, or stop word dictionaries.",
      "resource": "Dictionaries"
    },
    {
      "name": "Search dictionary entries",
      "value": "searchDictionaryEntries",
      "action": "Search dictionary entries",
      "routing": {
        "request": {
          "url": "=/1/dictionaries/{{ $parameter.dictionaryName_options }}/search",
          "method": "POST"
        }
      },
      "description": "Searches for standard and custom dictionary entries.",
      "resource": "Dictionaries"
    },
    {
      "name": "Retrieve dictionary settings",
      "value": "getDictionarySettings",
      "action": "Retrieve dictionary settings",
      "routing": {
        "request": {
          "url": "=/1/dictionaries/*/settings",
          "method": "GET"
        }
      },
      "description": "Retrieves the languages for which standard dictionary entries are turned off.",
      "resource": "Dictionaries"
    },
    {
      "name": "Update dictionary settings",
      "value": "setDictionarySettings",
      "action": "Update dictionary settings",
      "routing": {
        "request": {
          "url": "=/1/dictionaries/*/settings",
          "method": "PUT"
        }
      },
      "description": "Turns standard stop word dictionary entries on or off for a given language.",
      "resource": "Dictionaries"
    },
    {
      "name": "List available languages",
      "value": "getDictionaryLanguages",
      "action": "List available languages",
      "routing": {
        "request": {
          "url": "=/1/dictionaries/*/languages",
          "method": "GET"
        }
      },
      "description": "Lists supported languages with their supported dictionary types and number of custom entries.",
      "resource": "Dictionaries"
    },
    {
      "name": "Assign or move a user ID",
      "value": "assignUserId",
      "action": "Assign or move a user ID",
      "routing": {
        "request": {
          "url": "=/1/clusters/mapping",
          "method": "POST"
        }
      },
      "description": "Assigns or moves a user ID to a cluster.",
      "resource": "Clusters"
    },
    {
      "name": "List user IDs",
      "value": "listUserIds",
      "action": "List user IDs",
      "routing": {
        "request": {
          "url": "=/1/clusters/mapping",
          "method": "GET"
        }
      },
      "description": "Lists the userIDs assigned to a multi-cluster application.",
      "resource": "Clusters"
    },
    {
      "name": "Assign multiple userIDs",
      "value": "batchAssignUserIds",
      "action": "Assign multiple userIDs",
      "routing": {
        "request": {
          "url": "=/1/clusters/mapping/batch",
          "method": "POST"
        }
      },
      "description": "Assigns multiple user IDs to a cluster.",
      "resource": "Clusters"
    },
    {
      "name": "Get top user IDs",
      "value": "getTopUserIds",
      "action": "Get top user IDs",
      "routing": {
        "request": {
          "url": "=/1/clusters/mapping/top",
          "method": "GET"
        }
      },
      "description": "Get the IDs of the 10 users with the highest number of records per cluster.",
      "resource": "Clusters"
    },
    {
      "name": "Retrieve user ID",
      "value": "getUserId",
      "action": "Retrieve user ID",
      "routing": {
        "request": {
          "url": "=/1/clusters/mapping/{{ $parameter.userID_string }}",
          "method": "GET"
        }
      },
      "description": "Returns the user ID data stored in the mapping.",
      "resource": "Clusters"
    },
    {
      "name": "Delete user ID",
      "value": "removeUserId",
      "action": "Delete user ID",
      "routing": {
        "request": {
          "url": "=/1/clusters/mapping/{{ $parameter.userID_string }}",
          "method": "DELETE"
        }
      },
      "description": "Deletes a user ID and its associated data from the clusters.",
      "resource": "Clusters"
    },
    {
      "name": "List clusters",
      "value": "listClusters",
      "action": "List clusters",
      "routing": {
        "request": {
          "url": "=/1/clusters",
          "method": "GET"
        }
      },
      "description": "Lists the available clusters in a multi-cluster setup.",
      "resource": "Clusters"
    },
    {
      "name": "Search for user IDs",
      "value": "searchUserIds",
      "action": "Search for user IDs",
      "routing": {
        "request": {
          "url": "=/1/clusters/mapping/search",
          "method": "POST"
        }
      },
      "description": "Since it can take a few seconds to get the data from the different clusters,",
      "resource": "Clusters"
    },
    {
      "name": "Get migration and user mapping status",
      "value": "hasPendingMappings",
      "action": "Get migration and user mapping status",
      "routing": {
        "request": {
          "url": "=/1/clusters/mapping/pending",
          "method": "GET"
        }
      },
      "description": "To determine when the time-consuming process of creating a large batch of users or migrating users from one cluster to another is complete, this operation retrieves the status of the process.",
      "resource": "Clusters"
    },
    {
      "name": "List allowed sources",
      "value": "getSources",
      "action": "List allowed sources",
      "routing": {
        "request": {
          "url": "=/1/security/sources",
          "method": "GET"
        }
      },
      "description": "Retrieves all allowed IP addresses with access to your application.",
      "resource": "Vaults"
    },
    {
      "name": "Replace allowed sources",
      "value": "replaceSources",
      "action": "Replace allowed sources",
      "routing": {
        "request": {
          "url": "=/1/security/sources",
          "method": "PUT"
        }
      },
      "description": "Replaces the list of allowed sources.",
      "resource": "Vaults"
    },
    {
      "name": "Add a source",
      "value": "appendSource",
      "action": "Add a source",
      "routing": {
        "request": {
          "url": "=/1/security/sources/append",
          "method": "POST"
        }
      },
      "description": "Adds a source to the list of allowed sources.",
      "resource": "Vaults"
    },
    {
      "name": "Delete a source",
      "value": "deleteSource",
      "action": "Delete a source",
      "routing": {
        "request": {
          "url": "=/1/security/sources/{{ $parameter.source_string }}",
          "method": "DELETE"
        }
      },
      "description": "Deletes a source from the list of allowed sources.",
      "resource": "Vaults"
    },
    {
      "name": "Retrieve log entries",
      "value": "getLogs",
      "action": "Retrieve log entries",
      "routing": {
        "request": {
          "url": "=/1/logs",
          "method": "GET"
        }
      },
      "description": "The request must be authenticated by an API key with the [`logs` ACL](https://www.algolia.com/doc/guides/security/api-keys/#access-control-list-acl).",
      "resource": "Advanced"
    },
    {
      "name": "Check application task status",
      "value": "getAppTask",
      "action": "Check application task status",
      "routing": {
        "request": {
          "url": "=/1/task/{{ $parameter.taskID_number }}",
          "method": "GET"
        }
      },
      "description": "Checks the status of a given application task.",
      "resource": "Advanced"
    }
  ]

npm_readme:
  # n8n-nodes-algolia

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

  Algolia is a hosted search API that provides search-as-a-service solutions. It offers powerful search capabilities including full-text search, faceting, geo-search, and instant search with typo tolerance.

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

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

  ## Installation

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

  ## Credentials

  To use this node, you need to authenticate with Algolia using API credentials.

  ### Prerequisites

  1. [Sign up](https://dashboard.algolia.com/users/sign_up) or [log into](https://dashboard.algolia.com/users/sign_in) your Algolia account
  2. In your Algolia dashboard, go to [Settings](https://dashboard.algolia.com/account/overview) > [API Keys](https://dashboard.algolia.com/account/api-keys/all)
  3. Copy your Application ID
  4. Copy your Admin API Key
     - Required for if you want to manage your API keys from n8n - otherwise the Write API Key should be enough)

  ![API Keys page](assets/algolia-credentials.png)

  5. In n8n, create new Algolia API credentials with:
     - **Application ID**: Your Algolia Application ID
     - **Admin API Key**: Your Algolia Admin API Key

  ![Credentials page](assets/n8n-credentials.png)

  ## Operations

  This node supports the following operations:

  <!-- THE FOLLOWING SECTION IS GENERATED BY CI. ANY CHANGES WILL BE OVERRIDDEN -->
  <!-- OPERATIONS START -->

  ### Advanced

  - **Retrieve log entries** - The request must be authenticated by an API key with the [`logs` ACL](https://www.algolia.com/doc/guides/security/api-keys/#access-control-list-acl).
  - **Check application task status** - Checks the status of a given application task.

  ### Api Keys

  - **List API keys** - Lists all API keys associated with your Algolia application, including their permissions and restrictions.
  - **Create an API key** - Creates a new API key with specific permissions and restrictions.
  - **Retrieve API key permissions** - Gets the permissions and restrictions of an API key.
  - **Update an API key** - Replaces the permissions of an existing API key.
  - **Delete an API key** - Deletes the API key.
  - **Restore an API key** - Restores a deleted API key.

  ### Clusters

  - **Assign or move a user ID** - Assigns or moves a user ID to a cluster.
  - **List user IDs** - Lists the userIDs assigned to a multi-cluster application.
  - **Assign multiple userIDs** - Assigns multiple user IDs to a cluster.
  - **Get top user IDs** - Get the IDs of the 10 users with the highest number of records per cluster.
  - **Retrieve user ID** - Returns the user ID data stored in the mapping.
  - **Delete user ID** - Deletes a user ID and its associated data from the clusters.
  - **List clusters** - Lists the available clusters in a multi-cluster setup.
  - **Search for user IDs** - Since it can take a few seconds to get the data from the different clusters,
  - **Get migration and user mapping status** - To determine when the time-consuming process of creating a large batch of users or migrating users from one cluster to another is complete, this operation retrieves the status of the process.

  ### Dictionaries

  - **Add or delete dictionary entries** - Adds or deletes multiple entries from your plurals, segmentation, or stop word dictionaries.
  - **Search dictionary entries** - Searches for standard and custom dictionary entries.
  - **Retrieve dictionary settings** - Retrieves the languages for which standard dictionary entries are turned off.
  - **Update dictionary settings** - Turns standard stop word dictionary entries on or off for a given language.
  - **List available languages** - Lists supported languages with their supported dictionary types and number of custom entries.

  ### Indices

  - **Delete an index** - Deletes an index and all its settings.
  - **Retrieve index settings** - Retrieves an object with non-null index settings.
  - **Update index settings** - Update the specified index settings.
  - **Check task status** - Checks the status of a given task.
  - **Copy or move an index** - Copies or moves (renames) an index within the same Algolia application.
  - **List indices** - Lists all indices in the current Algolia application.

  ### Records

  - **Add a new record (with auto-generated object ID)** - Adds a record to an index or replaces it.
  - **Retrieve a record** - Retrieves one record by its object ID.
  - **Add or replace a record** - If a record with the specified object ID exists, the existing record is replaced.
  - **Delete a record** - Deletes a record by its object ID.
  - **Delete records matching a filter** - This operation doesn't accept empty filters.
  - **Delete all records from an index** - Deletes only the records from an index while keeping settings, synonyms, and rules.
  - **Add or update attributes** - Adds new attributes to a record, or updates existing ones.
  - **Batch indexing operations on one index** - Adds, updates, or deletes records in one index with a single API request.
  - **Batch indexing operations on multiple indices** - Adds, updates, or deletes records in multiple indices with a single API request.
  - **Retrieve records** - Retrieves one or more records, potentially from different indices.

  ### Rules

  - **Retrieve a rule** - Retrieves a rule by its ID.
  - **Create or replace a rule** - If a rule with the specified object ID doesn't exist, it's created.
  - **Delete a rule** - Deletes a rule by its ID.
  - **Create or update rules** - Create or update multiple rules.
  - **Delete all rules** - Deletes all rules from the index.
  - **Search for rules** - Searches for rules in your index.

  ### Search

  - **Search an index** - Searches a single index and returns matching search results as hits.
  - **Search multiple indices** - Sends multiple search requests to one or more indices.
  - **Search for facet values** - Searches for values of a specified facet attribute.
  - **Browse for records** - Retrieves records from an index, up to 1,000 per request.

  ### Synonyms

  - **Retrieve a synonym** - Retrieves a synonym by its ID.
  - **Create or replace a synonym** - If a synonym with the specified object ID doesn't exist, Algolia adds a new one.
  - **Delete a synonym** - Deletes a synonym by its ID.
  - **Create or replace synonyms** - If a synonym with the `objectID` doesn't exist, Algolia adds a new one.
  - **Delete all synonyms** - Deletes all synonyms from the index.
  - **Search for synonyms** - Searches for synonyms in your index.

  ### Vaults

  - **List allowed sources** - Retrieves all allowed IP addresses with access to your application.
  - **Replace allowed sources** - Replaces the list of allowed sources.
  - **Add a source** - Adds a source to the list of allowed sources.
  - **Delete a source** - Deletes a source from the list of allowed sources.
  <!-- OPERATIONS END -->

  ## Usage examples

  ### Batch insert into an Algolia Index

  This section demonstrates how to efficiently perform batch inserts into an Algolia index using n8n.

  Batch operations are ideal for processing large datasets, as they reduce latency and improve data consistency.

  <div align="center">

  ![Batch workflow](assets/workflow-1.png)
  <i>Example of a Batch indexing operation workflow</i>

  </div>

  **Step-by-step guide:**

  1. Add a **webhook node** or a third-party webhook trigger node to initiate your workflow.

  2. Utilize a **Code node** (JavaScript or Python) to format your data for the Algolia batch insert operation.
     - If your items already have unique identifiers, assign them to the `objectID` property.
     - If not, Algolia will automatically generate a unique `objectID` when using the `addObject` or `partialUpdateObject` actions.
     - To remove a record, use the `deleteObject` action.
     - For a complete overview of available actions, refer to the [official documentation](https://www.algolia.com/doc/rest-api/search/batch#body-requests).

  ![Batch workflow](assets/workflow-1-1.png)

  3. Add the Algolia node for **Batch indexing operations on one index** and connect it to your data list. As a best practice, ensure that only one item at a time enters this node.

  ![Batch workflow](assets/workflow-1-2.png)

  4. Activate the workflow to begin automated batch indexing.

  Now, when items are added/deleted from your 3pr-party software, this workflow will make sure your index stays fresh and up to date!

  _API Rate limitations apply. See https://www.algolia.com/doc/guides/scaling/algolia-service-limits for more details._

  ### Scheduled Cleanup of Deprecated Synonyms

  Over time, synonyms in your Algolia index might become outdated or no longer needed. With n8n, you can automate the removal of deprecated or unused synonyms to keep your index clean and relevant.

  <div align="center">

  ![Synonym workflow](assets/workflow-2.png)
  <i>Example of a Batch indexing operation workflow</i>

  </div>

  **Step-by-step guide:**

  1. Use the **Schedule trigger** node to schedule the cleanup to run regularly (daily, weekly, etc.).
  2. Add the Algolia node to **"Search for synonyms"** in your target index.
  3. Use a code node to filter synonyms that match your criteria for deprecation or removal (for example, a custom `deprecated` tag or certain naming patterns).
  4. For each deprecated synonym, add an Algolia node to **"Delete a synonym"** by its ID.

  This workflow helps ensure your Algolia synonyms stay accurate and up to date.

  _API Rate limitations apply. See https://www.algolia.com/doc/guides/scaling/algolia-service-limits for more details._

  ## Compatibility

  This node requires n8n version 1.0.0 or higher and Node.js 20.15 or higher.

  ## Resources

  - [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  - [Algolia API documentation](https://www.algolia.com/doc/rest-api/search)
  - [Algolia dashboard](https://www.algolia.com/apps)

ai_documentation_summary:
  {"purpose":"This n8n community node enables integration with Algolia, a hosted search API, to perform full-text search, faceting, geo-search, and instant search with typo tolerance within n8n workflows.","capabilities":["Full-text search with typo tolerance","Faceting and geo-search","Instant search","Create, update, delete, and list API keys","Manage index settings and operations","Add, retrieve, update, and delete records","Batch indexing operations across one or multiple indices","Retrieve and manage synonyms and rules","Multi-cluster user management","Dictionary management (plurals, segmentation, stop words)"],"authentication":"API key required (Application ID and Admin API Key)","commonUseCases":["Implementing search functionality in web applications","Automating content indexing and search","Managing Algolia API keys and permissions","Handling multi-cluster user data","Performing batch record operations"],"limitations":["Community node (not official n8n node)","Requires Algolia account setup","Some operations require specific API key ACL permissions (e.g., logs)"],"relatedNodes":["Algolia Search","Algolia Index","Algolia Record","Algolia API Key","Algolia Cluster"]}


node_type: @heygenofficial/n8n-nodes-heygen-official.heygenNode
package_name: @heygenofficial/n8n-nodes-heygen-official
display_name: HeyGen
description: HeyGen community node
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: demianheygen
author_github_url: https://github.com/heygen-com/n8n-integration-heygen-node.git
npm_package_name: @heygenofficial/n8n-nodes-heygen-official
npm_version: 0.1.8
community_fetched_at: 2026-07-14T14:40:33.524Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "video",
      "options": [
        {
          "name": "Video",
          "value": "video"
        },
        {
          "name": "List",
          "value": "list"
        },
        {
          "name": "Asset",
          "value": "asset"
        },
        {
          "name": "OAuth",
          "value": "oauth"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "createAvatarVideo",
      "options": [
        {
          "name": "Create Avatar Video",
          "value": "createAvatarVideo",
          "action": "Create avatar video a video"
        },
        {
          "name": "Create Template Video",
          "value": "createTemplateVideo",
          "action": "Create template video a video"
        },
        {
          "name": "Create a Video From Video Agent",
          "value": "createVideoAgent",
          "action": "Create a video from video agent a video"
        },
        {
          "name": "Get Video Status",
          "value": "getVideoStatus",
          "action": "Get video status a video"
        },
        {
          "name": "Translate Video",
          "value": "translateVideo",
          "action": "Translate video a video"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "video"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "listAvatars",
      "options": [
        {
          "name": "List Avatars",
          "value": "listAvatars",
          "action": "List avatars a list"
        },
        {
          "name": "List Avatars Groups",
          "value": "listAvatarsGroups",
          "action": "List avatars groups a list"
        },
        {
          "name": "List Voices",
          "value": "listVoices",
          "action": "List voices a list"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "list"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "uploadAssets",
      "options": [
        {
          "name": "Upload Assets",
          "value": "uploadAssets",
          "action": "Upload assets an asset"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "asset"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "registerOAuthClient",
      "options": [
        {
          "name": "Register OAuth Client",
          "value": "registerOAuthClient",
          "action": "Register oauth client an oauth"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "oauth"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Create Avatar Video",
      "value": "createAvatarVideo",
      "action": "Create avatar video a video",
      "resource": "video"
    },
    {
      "name": "Create Template Video",
      "value": "createTemplateVideo",
      "action": "Create template video a video",
      "resource": "video"
    },
    {
      "name": "Create a Video From Video Agent",
      "value": "createVideoAgent",
      "action": "Create a video from video agent a video",
      "resource": "video"
    },
    {
      "name": "Get Video Status",
      "value": "getVideoStatus",
      "action": "Get video status a video",
      "resource": "video"
    },
    {
      "name": "Translate Video",
      "value": "translateVideo",
      "action": "Translate video a video",
      "resource": "video"
    },
    {
      "name": "List Avatars",
      "value": "listAvatars",
      "action": "List avatars a list",
      "resource": "list"
    },
    {
      "name": "List Avatars Groups",
      "value": "listAvatarsGroups",
      "action": "List avatars groups a list",
      "resource": "list"
    },
    {
      "name": "List Voices",
      "value": "listVoices",
      "action": "List voices a list",
      "resource": "list"
    },
    {
      "name": "Upload Assets",
      "value": "uploadAssets",
      "action": "Upload assets an asset",
      "resource": "asset"
    },
    {
      "name": "Register OAuth Client",
      "value": "registerOAuthClient",
      "action": "Register oauth client an oauth",
      "resource": "oauth"
    }
  ]

npm_readme:
  # Local development

  If n8n-nodes-heygen-official in some folder "/home/username/projects/n8n-heygen-node" after changing code of this node, we need:

  1. In this folder run `npm run build && npm link`
  2. Go to global n8n folder and sub-folder "custom" (for example: ~/.n8n/custom) and from this folder run `npm link n8n-nodes-heygen-official` where "n8n-nodes-heygen-official" is name of this package (in package.json)
  3. Run `n8n start` to start local n8n server
  4. Now we should be able to find HeyGen node
  5. Link operations need to be done only one time, then after changes in code, just run `npm run build` and then `n8n start` to see changes

  # Lint

  6. Run `npm run lint` to check for errors or `npm run lintfix` to automatically fix errors when possible.

  Important: Some of lint errors do not follow logic, so we can skip them.

  # Publish

  7. [Publish](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry) your package to npm.

  ## More information

  Refer to [documentation on creating nodes](https://docs.n8n.io/integrations/creating-nodes/) for detailed information on building your own nodes.

ai_documentation_summary:
  {"purpose":"This is a community-maintained n8n node that provides integration capabilities for the HeyGen AI video generation platform.","capabilities":[],"authentication":"None (Community node - specific auth methods not documented in README)","commonUseCases":[],"limitations":["No specific API authentication methods documented in the README","Limited documentation on actual HeyGen API features supported","Community-maintained node may lack official support or updates"],"relatedNodes":["n8n HeyGen official node (if available)","Video generation nodes","AI text-to-video nodes"]}


node_type: n8n-nodes-extract-monster.extractMonster
package_name: n8n-nodes-extract-monster
display_name: Extract Monster
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: extractmonster
author_github_url: https://github.com/extract-monster/
npm_package_name: n8n-nodes-extract-monster
npm_version: 1.0.3
community_fetched_at: 2026-07-14T14:40:33.525Z
ai_summary_generated_at: 2026-03-25 22:51:43
description:
  AI-powered data extraction from documents, images, video, and audio. Extract invoices, receipts, forms, and custom data with flexible JSON schemas.

properties_schema:
  [
    {
      "name": "operation",
      "type": "options",
      "default": "extractDocument",
      "options": [
        {
          "name": "Extract from Document",
          "value": "extractDocument",
          "action": "Extract data from document",
          "description": "Extract from PDF, CSV, and text files (TXT, MD, HTML, XML, JSON, RTF)"
        },
        {
          "name": "Extract from Image",
          "value": "extractImage",
          "action": "Extract data from image or photo",
          "description": "Extract from images and photos (PNG, JPG, JPEG, WebP, HEIC, HEIF, GIF, BMP)"
        },
        {
          "name": "Extract from Video",
          "value": "extractVideo",
          "action": "Extract data from video",
          "description": "Extract from video files (MP4, MOV, AVI, WebM, MPEG, FLV, MPG, WMV, 3GP)"
        },
        {
          "name": "Extract from Audio",
          "value": "extractAudio",
          "action": "Extract data from audio or voice recording",
          "description": "Extract from audio files (WAV, MP3, AIFF, AAC, OGG, FLAC)"
        },
        {
          "name": "Extract from Text",
          "value": "extractText",
          "action": "Extract data from text",
          "description": "Extract structured data from plain text input"
        }
      ],
      "displayName": "Operation",
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Extract from Document",
      "value": "extractDocument",
      "action": "Extract data from document",
      "description": "Extract from PDF, CSV, and text files (TXT, MD, HTML, XML, JSON, RTF)"
    },
    {
      "name": "Extract from Image",
      "value": "extractImage",
      "action": "Extract data from image or photo",
      "description": "Extract from images and photos (PNG, JPG, JPEG, WebP, HEIC, HEIF, GIF, BMP)"
    },
    {
      "name": "Extract from Video",
      "value": "extractVideo",
      "action": "Extract data from video",
      "description": "Extract from video files (MP4, MOV, AVI, WebM, MPEG, FLV, MPG, WMV, 3GP)"
    },
    {
      "name": "Extract from Audio",
      "value": "extractAudio",
      "action": "Extract data from audio or voice recording",
      "description": "Extract from audio files (WAV, MP3, AIFF, AAC, OGG, FLAC)"
    },
    {
      "name": "Extract from Text",
      "value": "extractText",
      "action": "Extract data from text",
      "description": "Extract structured data from plain text input"
    }
  ]

npm_readme:
  # n8n-nodes-extract-monster

  This is an n8n community node that lets you use [Extract Monster](https://extract.monster) in your n8n workflows.

  Extract Monster is a powerful API for extracting structured data from documents, images, and text using advanced AI models.

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

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

  ## Installation

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

  ### Community Nodes (Recommended)

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

  ### Manual Installation

  To install manually, navigate to your n8n installation folder and run:

  ```bash
  npm install n8n-nodes-extract-monster
  ```

  ## Operations

  ### Extract From File
  Extract structured data from files including:
  - PDF documents
  - Images (PNG, JPG, etc.)
  - Word documents
  - Spreadsheets
  - And more

  ### Extract From Text
  Extract structured data from plain text input.

  Both operations support optional JSON schemas to define the exact structure of data you want to extract.

  ## Credentials

  To use this node, you need an Extract Monster API key:

  1. Sign up at [extract.monster](https://extract.monster)
  2. Go to your dashboard
  3. Generate a new API key
  4. Copy the key (it starts with `em_`)

  Then in n8n:
  1. Go to **Credentials**
  2. Create new **Extract Monster API** credentials
  3. Paste your API key

  ## Compatibility

  - Minimum n8n version: 0.196.0
  - Tested against n8n version: 1.0.0+

  ## Usage

  ### Basic File Extraction

  1. Add a file input node (e.g., HTTP Request, Google Drive, etc.)
  2. Connect it to the Extract Monster node
  3. Select **Extract From File** operation
  4. Configure credentials
  5. Execute the workflow

  ### With JSON Schema

  Define exactly what data you want to extract:

  ```json
  {
    "invoice_number": {"type": "string"},
    "date": {"type": "string"},
    "total": {"type": "number"},
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "description": {"type": "string"},
          "quantity": {"type": "number"},
          "price": {"type": "number"}
        }
      }
    }
  }
  ```

  ### Text Extraction Example

  1. Add an input node with text data
  2. Connect to Extract Monster node
  3. Select **Extract From Text** operation
  4. Enter or map the text field
  5. Optionally add a schema to define structure
  6. Execute

  ## Resources

  * [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
  * [Extract Monster Documentation](https://extract.monster/docs)
  * [Extract Monster API Reference](https://extract.monster/docs/developers/api-reference)

  ## Support

  For issues with the n8n node:
  - Create an issue on [GitHub](https://github.com/extract-monster/n8n-nodes-extract-monster/issues)

  For Extract Monster API support:
  - Email: support@extract.monster
  - Documentation: https://extract.monster/docs

  ## License

  [MIT](LICENSE.md)

ai_documentation_summary:
  {"purpose":"Extracts structured data from documents, images, video, audio, and text using AI models with optional JSON schema definitions.","capabilities":["Extract data from PDF documents","Extract data from images (PNG, JPG)","Extract data from Word documents","Extract data from spreadsheets","Extract data from plain text","Extract data from video and audio","Define custom extraction structure using JSON schemas","Extract invoice data","Extract receipt data","Extract form data"],"authentication":"API key required (Extract Monster API key starting with 'em_')","commonUseCases":["Automated invoice processing","Receipt data extraction","Form data capture from uploaded documents","Document digitization and structuring","Text data parsing and organization"],"limitations":["Requires external API key from Extract Monster service","Minimum n8n version 0.196.0 required","Community node (may have stability risks compared to official nodes)"],"relatedNodes":["HTTP Request","Google Drive","File Input","Text Input"]}


node_type: n8n-nodes-paubox.paubox
package_name: n8n-nodes-paubox
display_name: Paubox
description: Send HIPAA-compliant email via Paubox Email API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: paubox_engineering
author_github_url: https://github.com/Paubox/
npm_package_name: n8n-nodes-paubox
npm_version: 1.0.4
community_fetched_at: 2026-07-14T14:40:33.529Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "message",
      "options": [
        {
          "name": "Message",
          "value": "message"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "send",
      "options": [
        {
          "name": "Send",
          "value": "send",
          "action": "Send a message",
          "description": "Send an email message"
        },
        {
          "name": "Get Disposition",
          "value": "getDisposition",
          "action": "Get message disposition",
          "description": "Get email delivery status and tracking information"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "message"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Send",
      "value": "send",
      "action": "Send a message",
      "description": "Send an email message",
      "resource": "message"
    },
    {
      "name": "Get Disposition",
      "value": "getDisposition",
      "action": "Get message disposition",
      "description": "Get email delivery status and tracking information",
      "resource": "message"
    }
  ]

npm_readme:
  # n8n-nodes-paubox

  This is an n8n community node that lets you send HIPAA-compliant email using the [Paubox Email API](https://www.paubox.com/) in your n8n workflows.

  Paubox enables healthcare organizations to send secure, encrypted email without requiring recipients to use portals or passwords.

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

  ## Table of Contents

  - [Installation](#installation)
  - [Prerequisites](#prerequisites)
  - [Operations](#operations)
  - [Credentials](#credentials)
  - [Compatibility](#compatibility)
  - [Usage](#usage)
  - [Resources](#resources)
  - [License](#license)

  ## Installation

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

  ### For n8n Cloud or Self-Hosted:

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

  ### For Local Development:

  ```bash
  # Navigate to your n8n installation's node_modules directory
  cd ~/.n8n/nodes

  # Clone or install the package
  npm install n8n-nodes-paubox

  # Restart n8n
  ```

  ### Manual Installation for Testing:

  ```bash
  # Clone this repository
  git clone https://github.com/yourusername/n8n-nodes-paubox.git
  cd n8n-nodes-paubox

  # Install dependencies
  npm install

  # Build the node
  npm run build

  # Link for local testing
  npm link

  # In your n8n installation directory
  cd ~/.n8n
  npm link n8n-nodes-paubox

  # Restart n8n
  ```

  ## Prerequisites

  To use this node, you need:

  1. **A Paubox Account**: Sign up at [paubox.com](https://www.paubox.com/)
  2. **API Credentials**:
     - API Username (your API endpoint username)
     - API Key (your authentication token)
  3. **Verified Domain**: Your sending email domain must be verified with Paubox
  4. **n8n Installation**: Version 0.187.0 or later

  ## Operations

  The Paubox node supports the following operations:

  ### Message

  #### Send

  Send a HIPAA-compliant email message.

  **Required Fields:**

  - **From**: Sender email address (must match your verified domain)
  - **To**: Recipient email address(es) - comma-separated for multiple recipients
  - **Subject**: Email subject line
  - **Content**: Either plain text, HTML, or both

  **Optional Fields:**

  - **CC**: Carbon copy recipients
  - **BCC**: Blind carbon copy recipients
  - **Reply To**: Reply-to email address
  - **Attachments**: File attachments (base64-encoded)
  - **Allow Non-TLS**: Allow delivery over non-TLS (not HIPAA-compliant for PHI)
  - **Force Secure Notification**: Force Paubox Secure Message delivery
  - **Override Open Tracking**: Enable open tracking for this message
  - **Override Link Tracking**: Enable click tracking (up to 1000 links)
  - **Unsubscribe URL**: URL for unsubscribe tracking
  - **List-Unsubscribe Header**: Standard unsubscribe header
  - **Custom Headers**: Additional email headers (must start with X-)

  **Returns:**

  - `sourceTrackingId`: Unique identifier for tracking the message
  - `data`: Status message
  - `customHeaders`: Any custom headers that were set

  #### Get Disposition

  Retrieve delivery status and tracking information for a sent message.

  **Required Fields:**

  - **Source Tracking ID**: The tracking ID returned when the message was sent

  **Returns:**

  - Delivery status (delivered, bounced, processing, etc.)
  - Open tracking information (if enabled)
  - Click tracking information (if enabled)
  - Unsubscribe status
  - Delivery timestamps

  ## Credentials

  The Paubox node requires the following credentials:

  ### Paubox API Credentials

  1. In n8n, go to **Credentials** > **New**
  2. Search for **Paubox API**
  3. Enter your credentials:
     - **API Username**: Your Paubox Email API endpoint username
     - **API Key**: Your Paubox authentication token

  To obtain these credentials:

  1. Log in to your [Paubox account](https://paubox.com/login)
  2. Navigate to your API settings
  3. Copy your API Username and API Key

  **Note**: The Paubox API uses a custom authentication format with the prefix `Token token=`. This is handled automatically by the node.

  ## Compatibility

  - **n8n Version**: 0.187.0 or later
  - **Tested With**: n8n 1.0.0+
  - **Node Version**: Requires Node.js 16.x or later

  ## Usage

  ### Example 1: Send a Simple Email

  ```
  1. Add a Paubox node to your workflow
  2. Select Operation: Send
  3. Configure:
     - From: doctor@yourhospital.com
     - To: patient@email.com
     - Subject: Your Lab Results
     - Content Type: HTML
     - HTML Content: <h1>Your results are ready</h1><p>Please log in to view them.</p>
  4. Execute the node
  ```

  ### Example 2: Send Email with Attachment

  ```
  1. Use a "Read Binary File" node to read your file
  2. Use a "Function" node to convert to base64:
     return items.map(item => ({
       json: {
         fileContent: item.binary.data.toString('base64'),
         fileName: 'results.pdf'
       }
     }));
  3. Add Paubox node with:
     - Basic fields (From, To, Subject, Content)
     - In Additional Fields > Attachments:
       - File Name: {{ $json.fileName }}
       - Content Type: application/pdf
       - Content (Base64): {{ $json.fileContent }}
  ```

  ### Example 3: Check Message Status

  ```
  1. After sending a message, save the sourceTrackingId
  2. Add another Paubox node
  3. Select Operation: Get Disposition
  4. Source Tracking ID: {{ $json.sourceTrackingId }}
  5. Execute to see delivery status
  ```

  ### Example 4: Send Secure Email to Multiple Recipients

  ```
  1. Add Paubox node
  2. Configure:
     - From: admin@clinic.com
     - To: patient1@email.com, patient2@email.com
     - Subject: Monthly Health Newsletter
     - CC: doctor@clinic.com
     - Force Secure Notification: true (recipients get secure pickup link)
     - Override Open Tracking: true
  ```

  ## API Notes and Limitations

  - **Base URL**: The API endpoint includes your username: `https://api.paubox.net/v1/{api_username}`
  - **Authentication**: Custom format `Authorization: Token token={api_key}`
  - **Content Requirement**: At least one of `text/plain` or `text/html` must be provided
  - **Attachments**: Must be base64-encoded
  - **Batch Sending**: For bulk messages, use multiple executions or implement bulk endpoint
  - **Tracking**: The `sourceTrackingId` from send operation is needed for disposition checks
  - **Rate Limits**: Check Paubox documentation for current API rate limits
  - **HIPAA Compliance**: Setting `allowNonTLS` to `true` is not HIPAA-compliant if the message contains PHI

  ## Resources

  - [n8n Community Nodes Documentation](https://docs.n8n.io/integrations/community-nodes/)
  - [Paubox Email API Documentation](https://docs.paubox.com/)
  - [Paubox Support](https://www.paubox.com/support/)

  ## Development

  ```bash
  # Install dependencies
  npm install

  # Build the node
  npm run build

  # Watch mode for development
  npm run dev

  # Lint code
  npm run lint

  # Format code
  npm run format
  ```

  ## Version History

  - **1.0.0**: Initial release
    - Send Message operation
    - Get Message Disposition operation
    - Full support for attachments, custom headers, and tracking

  ## Contributing

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

  ## License

  [MIT](LICENSE)

  ## Support

  If you encounter any issues or have questions:

  1. Check the [n8n community forum](https://community.n8n.io/)
  2. Review [Paubox API documentation](https://docs.paubox.com/)
  3. Open an issue in this repository

  ## Disclaimer

  This is a community-created node and is not officially supported by Paubox or n8n. Use at your own discretion.

ai_documentation_summary:
  {"purpose":"Sends HIPAA-compliant, encrypted emails via the Paubox Email API, enabling secure healthcare communication without requiring recipients to use portals or passwords.","capabilities":["Send encrypted emails with plain text, HTML, or both content types","Support for multiple recipients (To, CC, BCC)","Attach files (base64-encoded)","Track message delivery status","Track email opens and clicks","Manage unsubscribe links","Custom email headers","Force secure notification delivery"],"authentication":"API key required (API Username and API Key)","commonUseCases":["Sending lab results to patients","Healthcare provider notifications","Secure patient communications","HIPAA-compliant email campaigns","Monitoring email delivery and engagement"],"limitations":["Requires n8n version 0.187.0 or later","Requires Node.js 16.x or later","Requires a verified domain with Paubox","Allows non-TLS delivery which is not HIPAA-compliant for PHI","Custom headers must start with X-"],"relatedNodes":["n8n-nodes-base.httpRequest","n8n-nodes-base.binaryFile","n8n-nodes-base.function"]}


node_type: @flowbie/n8n-nodes-transcribe-video.flowbie
package_name: @flowbie/n8n-nodes-transcribe-video
display_name: Flowbie
description: Upload and transcribe videos using Flowbie API
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: flowbie-admin
author_github_url: https://github.com/flowbie-ai/n8n-nodes-transcribe-video.git
npm_package_name: @flowbie/n8n-nodes-transcribe-video
npm_version: 1.0.2
community_fetched_at: 2026-07-14T14:40:33.530Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "video",
      "options": [
        {
          "name": "Video",
          "value": "video"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "transcribe",
      "options": [
        {
          "name": "Transcribe",
          "value": "transcribe",
          "action": "Transcribe a video",
          "description": "Transcribe a video file"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "video"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Transcribe",
      "value": "transcribe",
      "action": "Transcribe a video",
      "description": "Transcribe a video file",
      "resource": "video"
    }
  ]

npm_readme:
  # @flowbie/n8n-nodes-transcribe-video

  This is an n8n community node. It lets you transcribe videos using Flowbie AI in your n8n workflows.

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

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

  ## Installation

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

  ## Operations

  - **Transcribe Video**
    - Upload video from binary data (from previous node output)
    - Automatically polls for completion
    - Returns transcription text and timestamped segments

  ## Credentials

  You need a Flowbie API key to use this node.

  ### Getting an API Key

  1. Sign up at [flowbie.ai](https://flowbie.ai)
  2. Go to your dashboard
  3. Navigate to the API Keys section
  4. Click "Create New API Key"
  5. Give it a descriptive name (e.g., "n8n integration")
  6. Copy the generated API key

  ### Configuring in n8n

  1. In n8n, click on "Credentials" in the left sidebar
  2. Click "Add Credential"
  3. Search for "Flowbie API"
  4. Enter your API key
  5. (Optional) Change the API Gateway URL if using a self-hosted instance
  6. Click "Save"

  ## Compatibility

  Compatible with n8n@1.60.0 or later

  ## Usage

  ### Basic Example

  1. Use an HTTP Request node to download a video (or any node that outputs binary data)
  2. Add the Flowbie node to your workflow
  3. Select your Flowbie API credentials
  4. Configure binary property name (default: "data")
  5. Configure polling interval and timeout
  6. Execute the workflow

  ### Example Output

  ```json
  {
    "transcription": "Hello world, this is a test video...",
    "segments": [
      {
        "id": 0,
        "seek": 0,
        "start": 0.0,
        "end": 2.5,
        "text": "Hello world,",
        "tokens": [50364, 50414],
        "temperature": 0.0,
        "avg_logprob": -0.25,
        "compression_ratio": 1.2,
        "no_speech_prob": 0.01
      }
    ],
    "jobId": "job_abc123"
  }
  ```

  ### Accessing Data in Workflow

  Use expressions to access the transcription:
  - Full text: `{{ $json.transcription }}`
  - Segments: `{{ $json.segments }}`
  - First segment text: `{{ $json.segments[0].text }}`
  - Job ID: `{{ $json.jobId }}`

  ## Resources

  * [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
  * [Flowbie API documentation](https://flowbie.ai/docs)
  * [Flowbie website](https://flowbie.ai)

ai_documentation_summary:
  {"purpose":"This n8n community node uploads and transcribes videos using the Flowbie AI API, returning both full transcription text and timestamped segments.","capabilities":["Upload video from binary data","Automatic polling for transcription completion","Return full transcription text","Return timestamped transcription segments with metadata","Support for custom binary property names","Configurable polling interval and timeout"],"authentication":"API key required","commonUseCases":["Automated video captioning in content workflows","Extracting spoken content from uploaded videos","Integrating video analysis into marketing or support automation pipelines"],"limitations":["Requires a valid Flowbie API key and account","Binary data must be provided via previous node output (e.g., HTTP Request node)","Requires n8n version 1.60.0 or later"],"relatedNodes":["HTTP Request","Credentials","Set Binary Data"]}


node_type: @gammatech/n8n-nodes-gamma.gamma
package_name: @gammatech/n8n-nodes-gamma
display_name: Gamma
description: Create AI-powered presentations, documents, and websites with Gamma
category: Community
development_style: declarative
version: 1
credentials_required: []
outputs:
  [
  "main"
  ]

updated_at: 2026-07-14 14:40:33
author_name: max.jackson
author_github_url: https://github.com/gamma-app/n8n-nodes-gamma.git
npm_package_name: @gammatech/n8n-nodes-gamma
npm_version: 0.1.3
community_fetched_at: 2026-07-14T14:40:33.531Z
ai_summary_generated_at: 2026-03-25 22:51:43
properties_schema:
  [
    {
      "name": "resource",
      "type": "options",
      "default": "generation",
      "options": [
        {
          "name": "Generation",
          "value": "generation",
          "description": "Create and manage AI-generated presentations, documents, and social posts"
        },
        {
          "name": "Theme",
          "value": "theme",
          "description": "Browse available themes (read-only - use to get theme IDs)"
        },
        {
          "name": "Folder",
          "value": "folder",
          "description": "Browse workspace folders (read-only - use to get folder IDs)"
        },
        {
          "name": "User",
          "value": "user",
          "description": "Get your account information"
        }
      ],
      "displayName": "Resource",
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "create",
      "options": [
        {
          "name": "Create",
          "value": "create",
          "action": "Create a generation",
          "routing": {
            "request": {
              "url": "/v1.0/generations",
              "method": "POST"
            }
          },
          "description": "Create a new presentation, document, social post, or webpage"
        },
        {
          "name": "Create from Template",
          "value": "createFromTemplate",
          "action": "Create from template",
          "routing": {
            "request": {
              "url": "/v1.0/generations/from-template",
              "method": "POST"
            }
          },
          "description": "Remix an existing Gamma with new prompt"
        },
        {
          "name": "Get Status",
          "value": "getStatus",
          "action": "Get generation status",
          "routing": {
            "request": {
              "url": "=/v1.0/generations/{{$parameter[\"generationId\"]}}",
              "method": "GET"
            }
          },
          "description": "Check the status of a generation"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "generation"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "List",
          "value": "list",
          "action": "List themes",
          "routing": {
            "request": {
              "url": "/v1.0/themes",
              "method": "GET"
            }
          },
          "description": "Get available themes from your workspace"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "theme"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "list",
      "options": [
        {
          "name": "List",
          "value": "list",
          "action": "List folders",
          "routing": {
            "request": {
              "url": "/v1.0/folders",
              "method": "GET"
            }
          },
          "description": "Get workspace folders"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "folder"
          ]
        }
      },
      "noDataExpression": true
    },
    {
      "name": "operation",
      "type": "options",
      "default": "getMe",
      "options": [
        {
          "name": "Get Me",
          "value": "getMe",
          "action": "Get user information",
          "routing": {
            "request": {
              "url": "/v1.0/me",
              "method": "GET"
            }
          },
          "description": "Get authenticated user and workspace information"
        }
      ],
      "displayName": "Operation",
      "displayOptions": {
        "show": {
          "resource": [
            "user"
          ]
        }
      },
      "noDataExpression": true
    }
  ]

operations:
  [
    {
      "name": "Create",
      "value": "create",
      "action": "Create a generation",
      "routing": {
        "request": {
          "url": "/v1.0/generations",
          "method": "POST"
        }
      },
      "description": "Create a new presentation, document, social post, or webpage",
      "resource": "generation"
    },
    {
      "name": "Create from Template",
      "value": "createFromTemplate",
      "action": "Create from template",
      "routing": {
        "request": {
          "url": "/v1.0/generations/from-template",
          "method": "POST"
        }
      },
      "description": "Remix an existing Gamma with new prompt",
      "resource": "generation"
    },
    {
      "name": "Get Status",
      "value": "getStatus",
      "action": "Get generation status",
      "routing": {
        "request": {
          "url": "=/v1.0/generations/{{$parameter[\"generationId\"]}}",
          "method": "GET"
        }
      },
      "description": "Check the status of a generation",
      "resource": "generation"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List themes",
      "routing": {
        "request": {
          "url": "/v1.0/themes",
          "method": "GET"
        }
      },
      "description": "Get available themes from your workspace",
      "resource": "theme"
    },
    {
      "name": "List",
      "value": "list",
      "action": "List folders",
      "routing": {
        "request": {
          "url": "/v1.0/folders",
          "method": "GET"
        }
      },
      "description": "Get workspace folders",
      "resource": "folder"
    },
    {
      "name": "Get Me",
      "value": "getMe",
      "action": "Get user information",
      "routing": {
        "request": {
          "url": "/v1.0/me",
          "method": "GET"
        }
      },
      "description": "Get authenticated user and workspace information",
      "resource": "user"
    }
  ]

npm_readme:
  # @gammatech/n8n-nodes-gamma

  Official Gamma community node for n8n - Create AI-powered presentations, documents, and websites directly from your n8n workflows.

  **Status:** Published to npm ✅  
  **Created:** November 10, 2025  
  **Maintained by:** Max Jackson (Gamma)

  ---

  ## 🎯 What This Does

  Integrates Gamma's API v1.0 into n8n, allowing you to:
  - Generate presentations, documents, social posts, and webpages with AI
  - Create content from templates
  - List themes and folders
  - Export to PDF/PPTX
  - Automate content creation workflows

  ---

  ## 📦 Installation

  ### Option 1: From npm (After Publishing)

  ```bash
  # In n8n
  Settings → Community Nodes → Install
  # Enter: @gammatech/n8n-nodes-gamma
  ```

  ### Option 2: Local Development

  ```bash
  # Clone and build
  git clone <this-repo>
  cd n8n-gamma-node
  npm install
  npm run build
  npm link

  # In your n8n installation
  cd ~/.n8n/custom
  npm link n8n-nodes-gamma

  # Start n8n
  n8n start
  ```

  ---

  ## 🏗️ Project Structure

  Based on Gamma's OpenAPI spec and using `@devlikeapro/n8n-openapi-node` for generation.

  ```
  n8n-gamma-node/
  ├── GAMMA_API_OPENAPI_SPEC.yaml   # OpenAPI spec (copied from root)
  ├── package.json                   # npm package config
  ├── tsconfig.json                  # TypeScript config
  ├── .gitignore
  ├── credentials/
  │   └── GammaApi.credentials.ts   # API key authentication
  ├── nodes/
  │   └── Gamma/
  │       ├── Gamma.node.ts         # Main node (generated from OpenAPI)
  │       ├── Gamma.node.json       # Node metadata
  │       └── gamma.svg             # Gamma logo icon
  ├── docs/
  │   ├── SETUP.md                  # Setup and development guide
  │   ├── PUBLISHING.md             # How to publish to npm
  │   ├── VERIFICATION.md           # n8n verification process
  │   └── n8n-resources.md          # All n8n documentation links
  └── examples/
      ├── basic-presentation.json   # Simple workflow example
      ├── zapier-alternative.json   # Replacing Zapier
      └── content-pipeline.json     # Advanced automation
  ```

  ---

  ## 🚀 Quick Start (Development)

  ### Prerequisites

  - Node.js 18+ (LTS recommended)
  - npm or pnpm
  - n8n installed globally (`npm install n8n -g`)

  ### Development Workflow

  ```bash
  # 1. Install dependencies
  npm install

  # 2. Build the node
  npm run build

  # 3. Link to local n8n
  npm link
  cd ~/.n8n/custom
  npm link n8n-nodes-gamma

  # 4. Start n8n
  n8n start

  # 5. Open browser at http://localhost:5678
  # Search for "Gamma" in nodes panel
  ```

  ### Watch Mode (Auto-rebuild)

  ```bash
  npm run dev
  # Rebuilds automatically on file changes
  ```

  ---

  ## 🔑 Authentication

  The node requires a Gamma API key:

  1. Log into [gamma.app](https://gamma.app)
  2. Go to Settings & Members → API
  3. Generate API key
  4. Copy key (starts with `sk-gamma-`)
  5. In n8n: Create credential → Gamma API
  6. Paste API key

  ---

  ## 📚 Available Operations

  ### Generations Resource

  **Create Generation**
  - Generate presentations, documents, social posts, or webpages
  - Parameters: inputText, textMode, format, numCards, themeId, imageOptions, etc.
  - Returns: generationId for status polling

  **Get Generation Status**
  - Poll for generation completion
  - Returns: status, gammaUrl, exportUrl, credits used

  **Create from Template**
  - Remix existing Gamma with new prompt
  - Parameters: prompt, gammaId (template), themeId, imageOptions
  - Returns: generationId

  ### Themes Resource

  **List Themes**
  - Get available themes for styling
  - Parameters: limit, query (search), cursor (pagination)
  - Returns: themes with IDs and names

  ### Folders Resource

  **List Folders**
  - Get workspace folders for organization
  - Parameters: limit, query (search), cursor (pagination)
  - Returns: folders with IDs and names

  ### User Resource

  **Get Me**
  - Get authenticated user info
  - Returns: email, workspaceName

  ---

  ## 🎨 Example Workflows

  ### ⚡ Auto-Polling Workflow (Recommended!)

  **Problem**: Gamma generations are async and take 30-90 seconds to complete.  
  **Solution**: Use our auto-polling workflow template!

  📥 **Import the workflow**: `examples/auto-polling-workflow.json`

  This workflow automatically:
  - ✅ Creates the generation
  - ✅ Waits 30 seconds
  - ✅ Checks status
  - ✅ Loops until completed
  - ✅ Outputs gammaUrl when done

  **No more manual clicking!** This gives users a much better experience.

  ### Basic: Generate Presentation (Manual Polling)

  ```
  Trigger (Manual/Schedule)
      ↓
  Gamma: Create Generation
      - inputText: "Introduction to renewable energy"
      - format: presentation
      - textMode: generate
      - numCards: 10
      ↓
  Gamma: Get Generation Status 
      (Click Execute repeatedly until status = "completed")
      ↓
  Send Email with gammaUrl
  ```

  ⚠️ **Note**: Manual polling requires clicking "Execute" every 30 seconds. Use the auto-polling workflow above for better UX!

  ### Advanced: Content Pipeline

  ```
  RSS Feed (New Article)
      ↓
  Extract Content
      ↓
  Gamma: Create Generation
      - inputText: {{article content}}
      - format: presentation
      ↓
  Wait for Completion (poll)
      ↓
  Gamma: Create from Template (social version)
      - gammaId: {{generated gammaId}}
      - format: social
      ↓
  Post to Social Media
  ```

  ---

  ## 🛠️ Development Guide

  ### Built With

  - **TypeScript** - Node language
  - **@devlikeapro/n8n-openapi-node** - OpenAPI → n8n generator
  - **n8n-workflow** - n8n node types
  - **Gamma OpenAPI v3 spec** - API specification

  ### Key Files

  **`nodes/Gamma/Gamma.node.ts`**
  - Main node implementation
  - Auto-generated from OpenAPI spec using `@devlikeapro/n8n-openapi-node`
  - Handles all API operations

  **`credentials/GammaApi.credentials.ts`**
  - API key authentication
  - Header: `X-API-KEY`

  **`package.json`**
  - npm package configuration
  - MUST include `n8n-community-node-package` in keywords
  - MUST start with `n8n-nodes-`

  ---

  ## 📝 Publishing

  ### To npm (Unverified)

  ```bash
  # 1. Update version
  npm version patch  # or minor, major

  # 2. Build
  npm run build

  # 3. Publish
  npm publish
  ```

  Anyone with self-hosted n8n can install immediately.

  ### For Verification (n8n Cloud)

  Requirements for verified community nodes:
  - ✅ MIT license
  - ✅ No runtime dependencies in package.json
  - ✅ Passes `npm run lint`
  - ✅ Good documentation
  - ✅ Follows n8n conventions

  **Steps:**
  1. Publish to npm (unverified first)
  2. Submit via [n8n Creator Portal](https://n8n.io/creators)
  3. Wait for manual review
  4. Once approved → available on n8n Cloud

  See `docs/VERIFICATION.md` for details.

  ---

  ## 🔗 Resources

  **Official Gamma:**
  - API Docs: https://developers.gamma.app
  - Help Center: https://help.gamma.app
  - API Slack: [Contact for access]

  **n8n Documentation:**
  - Creating Nodes: https://docs.n8n.io/integrations/creating-nodes/
  - Community Nodes: https://docs.n8n.io/integrations/community-nodes/
  - Declarative Style: https://docs.n8n.io/integrations/creating-nodes/build/declarative-style-node/

  **OpenAPI Generator:**
  - devlikeapro/n8n-openapi-node: https://github.com/devlikeapro/n8n-openapi-node
  - npm package: https://www.npmjs.com/package/@devlikeapro/n8n-openapi-node

  ---

  ## 💪 Why This Matters for Gamma

  ### Developer Relations Play

  **This n8n node:**
  - Expands Gamma's automation ecosystem beyond Zapier/Make
  - Reaches self-hosted automation users (enterprises, privacy-focused)
  - Shows commitment to open integration ecosystem
  - Provides reference implementation for community

  **For your DevRel role:**
  - Demonstrates technical ability (built official integration)
  - Shows ownership (end-to-end from spec to published node)
  - Community engagement (support users in n8n forums)
  - Content creation opportunity (tutorials, videos, blog posts)

  ---

  ## 📊 Success Metrics

  Track these to show impact:
  - npm downloads/week
  - n8n Cloud installations (after verification)
  - Community forum mentions
  - GitHub stars/forks
  - Support tickets reduced (self-service via node)

  ---

  ## 🎯 Roadmap

  **Phase 1: MVP** (You are here)
  - [x] Project setup
  - [ ] Generate node from OpenAPI spec
  - [ ] Test locally
  - [ ] Publish to npm (unverified)

  **Phase 2: Polish**
  - [ ] Add examples folder with workflows
  - [ ] Create video tutorial
  - [ ] Write blog post
  - [ ] Submit for verification

  **Phase 3: Growth**
  - [ ] Community support in n8n forums
  - [ ] Advanced workflow examples
  - [ ] Integration guides
  - [ ] Case studies

  ---

  ## 👥 Contributing

  This is an official Gamma integration. For bugs or features:
  - Internal: Message Max directly
  - External (after publishing): GitHub issues

  ---

  **Status:** Published to npm ✅  
  **Next Step:** Continue n8n review / listing workflow as needed

  See `docs/SETUP.md` for complete development guide.

ai_documentation_summary:
  {"purpose":"This n8n community node integrates with Gamma's API to create AI-powered presentations, documents, social posts, and webpages directly from workflows.","capabilities":["Generate presentations, documents, social posts, and webpages from text prompts","Create content from existing Gamma templates","List available themes and workspace folders","Export generated content to PDF and PPTX formats","Retrieve generation status and URLs (gammaUrl, exportUrl)","Get authenticated user information"],"authentication":"API key required (Gamma API key starting with 'sk-gamma-')","commonUseCases":["Automated content creation from RSS feeds or external data sources","Scheduled generation of presentations or social media posts","Content pipelines that convert articles into presentations","Template-based content remixing for different formats"],"limitations":["Generations are asynchronous and require polling for completion (30-90 seconds)","Manual polling requires repeated execution clicks; auto-polling workflow recommended","Requires npm installation and potential local development setup for community nodes","Some advanced features may require n8n Cloud verification for publishing"],"relatedNodes":["Manual Trigger","Schedule Trigger","Wait Node","HTTP Request Node","Function Node"]}


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

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

npm_readme:
  # n8n-nodes-immojump

  ImmoJump n8n Community Nodes (Trigger + Actions)

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

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

  ## Installation

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

  ## Operations

  The ImmoJump n8n Community Nodes provide comprehensive automation capabilities:

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

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

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

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

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

  ### Dynamic option loading (Tags & Status)

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

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

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

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

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

  ## Credentials

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

  ## Compatibility

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

  ## Usage

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

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

  ## Resources

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

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


