{
  "id": "r8An9r48hAtoGyS9",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Automate Redmine & GitLab Account Creation for New Hires \u2013 Effortless Onboarding",
  "tags": [
    {
      "id": "HyLSX6V7QZs5MrZy",
      "name": "Odoo",
      "createdAt": "2025-03-24T01:54:27.030Z",
      "updatedAt": "2025-03-24T01:54:27.030Z"
    }
  ],
  "nodes": [
    {
      "id": "fd3a413f-9752-4830-ac13-55142f181d40",
      "name": "Step1: Odoo 18 called a webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -3808,
        -208
      ],
      "parameters": {
        "path": "79db09d5-d242-42ff-9792-5840b41f9de6",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode",
        "authentication": "headerAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "11e85182-7dd8-4fc1-b8d8-a839505ffd84",
      "name": "Step2: Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -3600,
        -208
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "5df0d75d-d3bb-4d3c-a918-6b9ee8a2fed0",
      "name": "Step 3: Wait 5s",
      "type": "n8n-nodes-base.wait",
      "position": [
        -3808,
        -16
      ],
      "parameters": {},
      "executeOnce": true,
      "typeVersion": 1.1,
      "alwaysOutputData": true
    },
    {
      "id": "8f07db09-e5f5-4f27-a8b8-07827bd4516c",
      "name": "Step5: Handle get today",
      "type": "n8n-nodes-base.code",
      "position": [
        -3808,
        176
      ],
      "parameters": {
        "jsCode": "const today = new Date();\nconst tomorrow = new Date(today);\ntomorrow.setDate(today.getDate() + 1);\nconst lastday = new Date(today);\nlastday.setDate(today.getDate() - 1);\n\nconst formatDate = (date) => {\n  const year = date.getFullYear();\n  const month = String(date.getMonth() + 1).padStart(2, '0');\n  const day = String(date.getDate()).padStart(2, '0');\n  return `${year}-${month}-${day}`;\n};\nconst getDayOfWeek = (date) => {\n  const days = [\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"];\n  return days[date.getDay()];\n};\n\nreturn [\n  {\n    date: formatDate(today),\n    lastday: formatDate(lastday),\n    tomorrow: formatDate(tomorrow),\n    dayOfWeek: getDayOfWeek(today)\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "5a3e93c2-5918-4453-b87a-0561a516ad9b",
      "name": "Step 6: Check if there's a requirement to create a Redmine or GitLab account on Odoo 18.",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3344,
        16
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.web_search_read}}",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"id\": 18,\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"call\",\n  \"params\": {\n    \"model\": \"approval.request\",\n    \"method\": \"web_search_read\",\n    \"args\": [],\n    \"kwargs\": {\n      \"specification\": {\n        \"request_status\": {},\n        \"user_status\": {},\n        \"name\": {},\n        \"reason\": {},\n        \"category_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        },\n        \"date_start\": {},\n        \"date_end\": {},\n        \"activity_ids\": {\n          \"fields\": {}\n        },\n        \"activity_exception_decoration\": {},\n        \"activity_exception_icon\": {},\n        \"activity_state\": {},\n        \"activity_summary\": {},\n        \"activity_type_icon\": {},\n        \"activity_type_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        },\n        \"request_owner_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        }\n      },\n      \"offset\": 0,\n      \"order\": \"create_date DESC\",\n      \"limit\": {{ $node['Step5: Handle get today'].json.limit }},\n      \"context\": {\n        \"lang\": \"en_US\",\n        \"tz\": \"Asia/Bangkok\",\n        \"allowed_company_ids\": [\n          1,\n          2\n        ],\n        \"bin_size\": true,\n        \"params\": {\n          \"displayName\": \"All Approvals\",\n          \"action\": 408,\n          \"view_type\": \"kanban\",\n          \"actionStack\": [\n            {\n              \"displayName\": \"All Approvals\",\n              \"action\": 408,\n              \"view_type\": \"kanban\"\n            }\n          ]\n        },\n        \"default_category_id\": 14\n      },\n      \"count_limit\": 10001,\n      \"domain\": [\n        \"&\",\n        \"&\",\n        \"&\",\n        [\n          \"create_date\",\n          \">=\",\n          \"{{ $node['Step5: Handle get today'].json.lastday }} 17:10:01\"\n        ],\n        [\n          \"create_date\",\n          \"<=\",\n          \"{{ $node['Step5: Handle get today'].json.date }} 16:55:01\"\n        ],\n        \"&\",\n        \"|\",\n        \"|\",\n        \"|\",\n        \"|\",\n        \"|\",\n        [\n          \"name\",\n          \"ilike\",\n          \"Redmine\"\n        ],\n        [\n          \"name\",\n          \"ilike\",\n          \"gitlab\"\n        ],\n        [\n          \"name\",\n          \"ilike\",\n          \"RM\"\n        ],\n        [\n          \"reason\",\n          \"ilike\",\n          \"Redmine\"\n        ],\n        [\n          \"reason\",\n          \"ilike\",\n          \"Gitlab\"\n        ],\n        [\n          \"reason\",\n          \"ilike\",\n          \"RM\"\n        ],\n        [\n          \"request_status\",\n          \"=\",\n          \"pending\"\n        ],\n        [\n          \"category_id\",\n          \"=\",\n          14\n        ]\n      ]\n    }\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "433c373f-bc3e-4a4c-ab35-4562c13b5e62",
      "name": "Step7: If total_count is not equal to 0 = true",
      "type": "n8n-nodes-base.if",
      "position": [
        -3120,
        16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "40359ca9-0845-423c-9a96-58f06c9155df",
              "operator": {
                "type": "number",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.result.length }}",
              "rightValue": "={{ 0 }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "9049b915-7816-462c-a3ea-3012530e9048",
      "name": "Step8: Get many users",
      "type": "n8n-nodes-base.slack",
      "position": [
        -2928,
        -96
      ],
      "parameters": {
        "limit": 100,
        "resource": "user",
        "operation": "getAll"
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.4,
      "alwaysOutputData": true
    },
    {
      "id": "24ad9346-6afa-4d02-a309-da49b9ddc4de",
      "name": "End",
      "type": "n8n-nodes-base.noOp",
      "position": [
        -2928,
        144
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "caa28fcb-6d2e-4685-8ac6-e94ed7cbff2b",
      "name": "Step 9: Find the record requesting both Redmine and Gitlab accounts on Odoo 18.",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2704,
        -96
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.web_search_read}}",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"id\": 34,\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"call\",\n  \"params\": {\n    \"model\": \"approval.request\",\n    \"method\": \"web_search_read\",\n    \"args\": [],\n    \"kwargs\": {\n      \"specification\": {\n        \"request_status\": {},\n        \"user_status\": {},\n        \"name\": {},\n        \"reason\": {},\n        \"category_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        },\n        \"date_start\": {},\n        \"date_end\": {},\n        \"activity_ids\": {\n          \"fields\": {}\n        },\n        \"activity_exception_decoration\": {},\n        \"activity_exception_icon\": {},\n        \"activity_state\": {},\n        \"activity_summary\": {},\n        \"activity_type_icon\": {},\n        \"activity_type_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        },\n        \"request_owner_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        }\n      },\n      \"offset\": 0,\n      \"order\": \"create_date DESC\",\n      \"limit\": {{ $node['Step4: Set Variables'].json.limit }},\n      \"context\": {\n        \"lang\": \"en_US\",\n        \"tz\": \"Asia/Bangkok\",\n        \"allowed_company_ids\": [\n          1,\n          2\n        ],\n        \"bin_size\": true,\n        \"params\": {\n          \"view_type\": \"kanban\",\n          \"action\": 408,\n          \"actionStack\": [\n            {\n              \"action\": 408\n            }\n          ]\n        },\n        \"default_category_id\": 14\n      },\n      \"count_limit\": 10001,\n      \"domain\": [\n        \"&\",\n        \"&\",\n        [\n          \"request_status\",\n          \"=\",\n          \"pending\"\n        ],\n        \"&\",\n        \"&\",\n        [\n          \"create_date\",\n          \">=\",\n          \"{{ $node['Step5: Handle get today'].json.lastday }} 17:55:55\"\n        ],\n        [\n          \"create_date\",\n          \"<=\",\n          \"{{ $node['Step5: Handle get today'].json.date }} 16:55:55\"\n        ],\n        \"|\",\n        \"|\",\n        \"|\",\n        \"&\",\n        [\n          \"name\",\n          \"ilike\",\n          \"Redmine\"\n        ],\n        [\n          \"name\",\n          \"ilike\",\n          \"Gitlab\"\n        ],\n        \"&\",\n        [\n          \"name\",\n          \"ilike\",\n          \"RM\"\n        ],\n        [\n          \"name\",\n          \"ilike\",\n          \"gitlab\"\n        ],\n        \"&\",\n        [\n          \"reason\",\n          \"ilike\",\n          \"Redmine\"\n        ],\n        [\n          \"reason\",\n          \"ilike\",\n          \"Gitlab\"\n        ],\n        \"&\",\n        [\n          \"reason\",\n          \"ilike\",\n          \"RM\"\n        ],\n        [\n          \"reason\",\n          \"ilike\",\n          \"gitlab\"\n        ],\n        [\n          \"category_id\",\n          \"=\",\n          14\n        ]\n      ]\n    }\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "75feffc9-a399-4f29-8bd4-d45bb2dc0d96",
      "name": "Step10: If total_count is not equal to 0 = true",
      "type": "n8n-nodes-base.if",
      "position": [
        -2496,
        -96
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "40359ca9-0845-423c-9a96-58f06c9155df",
              "operator": {
                "type": "number",
                "operation": "notEquals"
              },
              "leftValue": "={{ $node['Step 9: Find the record requesting both Redmine and Gitlab accounts on Odoo 18.'].json.result.length }}",
              "rightValue": "={{ 0 }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "66feaed8-89ff-46a0-8fc5-4283b1333425",
      "name": "Step 11.1: Find and extract the email from the request",
      "type": "n8n-nodes-base.code",
      "position": [
        -2304,
        -304
      ],
      "parameters": {
        "jsCode": "const emailRegex = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/;\nconst input = items;\nconst source = input[0] && input[0].json ? input[0].json : input[0];\nconst records = (source && source.result && Array.isArray(source.result.records))\n  ? source.result.records\n  : [];\n\nconst output = records.map(rec => {\n  const tryFindEmail = (text) => {\n    if (!text || typeof text !== 'string') return null;\n    const m = text.match(emailRegex);\n    return m ? m[0] : null;\n  };\n\n  let found = tryFindEmail(rec.name);\n  if (!found) found = tryFindEmail(rec.reason);\n\n  if (!found) {\n    return { json: { id: rec.id, result: \"Email not found\" } };\n  }\n\n  const local = found.split('@')[0];\n  const beforeFirstDot = local.split('.')[0];\n  return { json: { id: rec.id, rawEmail: found, extracted: beforeFirstDot, login: local } };\n});\n\nreturn output;\n"
      },
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "57833d6c-3d07-4440-a2b9-f8437aea252e",
      "name": "Step12.1: Get user info in RM6",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2096,
        -304
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.redmine6_Url}}users.json?mail={{ $node['Step 11.1: Find and extract the email from the request'].json.rawEmail }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2
    },
    {
      "id": "c4885880-cac3-445c-bd2c-6c46400782b1",
      "name": "Step13.1: If total_count is not equal to 0 = true",
      "type": "n8n-nodes-base.if",
      "position": [
        -1888,
        -304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "40359ca9-0845-423c-9a96-58f06c9155df",
              "operator": {
                "type": "number",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.total_count }}",
              "rightValue": "={{ 0 }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "c7e649cc-0af5-4ca8-a314-4be472169e71",
      "name": "Step14.1: Get user information in Gitlab",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1664,
        -672
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.gitlab_Url}}api/v4/users?search={{ $node['Step 11.1: Find and extract the email from the request'].json.extracted }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "614297bd-8d0b-40bf-8b06-219f363b237e",
      "name": "Step15.1:  Check if there is a record = true",
      "type": "n8n-nodes-base.if",
      "position": [
        -1424,
        -752
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "557ae5fd-116a-4bc5-aac2-7343bc7e3eb8",
              "operator": {
                "type": "object",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $node['Step14.1: Get user information in Gitlab'].json }}",
              "rightValue": "={{ 1 }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "c70c9c7e-840c-454d-9c53-c0352d2a6afc",
      "name": "Step16.1: Send a message to channel",
      "type": "n8n-nodes-base.slack",
      "position": [
        -1200,
        -848
      ],
      "parameters": {
        "text": "=The email: {{ $node['Step 11.1: Find and extract the email from the request'].json.rawEmail }} is already available in Redmine and Gitlab.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.4,
      "alwaysOutputData": true
    },
    {
      "id": "92e4f7a8-caf6-4a38-aec8-9548a51877f0",
      "name": "Step 16.2: Create a new user in Gitlab",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1200,
        -656
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.gitlab_Url}}api/v4/users",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"email\": \"{{ $node['Step 11.1: Find and extract the email from the request'].json.rawEmail }}\",\n  \"username\": \"{{ $node['Step 11.1: Find and extract the email from the request'].json.extracted }}\",\n  \"name\": \"{{ $node['Step 11.1: Find and extract the email from the request'].json.extracted }}\",\n  \"reset_password\": true,\n  \"skip_confirmation\": true\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": false,
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "06f9d002-06a9-49db-a193-68b38ade18e7",
      "name": "Step 17.1: Send reset password",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -992,
        -656
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.gitlab_Url}}api/v4/users/{{ $json.id }}",
        "method": "PUT",
        "options": {},
        "jsonBody": "={\n  \"password\": \"Helloworld@123\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "dfcbaf04-a32f-4e7f-a8b7-8695f74c6bd2",
      "name": "Step 19.1: Send a message to the member via Slack once their Gitlab account has been created.",
      "type": "n8n-nodes-base.slack",
      "position": [
        -528,
        -656
      ],
      "parameters": {
        "text": "=The system has created a Gitlab account:\nWeb: {{ $node['Step4: Set Variables'].json.gitlab_Url}}\nUsername: {{ $node['Step 11.1: Find and extract the email from the request'].json.extracted}}\nEmail: {{ $node['Step 11.1: Find and extract the email from the request'].json.rawEmail}}\nPassword: Helloworld@123\n\nPlease change your password and enable 2FA + update your profile picture + full name for your account.",
        "user": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $node['Step18.1: Convert to text'].json.memberID}}"
        },
        "select": "user",
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.4,
      "alwaysOutputData": true
    },
    {
      "id": "f25a98a1-2128-49a6-84ab-80afabc2f8c2",
      "name": "Step18.1: Convert to text",
      "type": "n8n-nodes-base.code",
      "position": [
        -768,
        -656
      ],
      "parameters": {
        "jsCode": "const rawEmail = $node['Step 11.1: Find and extract the email from the request'].json.rawEmail;\nconst data = $node[\"Step8: Get many users\"].json.members;\nconst normalizedEmail = rawEmail.toLowerCase();\n\nconst matched = data.find(item => \n  item.profile.email && item.profile.email.toLowerCase() === normalizedEmail\n);\n\nreturn [\n  {\n    json: {\n      memberID: matched ? matched.id : \"Email not found\"\n    }\n  }\n];\n"
      },
      "executeOnce": true,
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "22743402-01ed-4123-8a12-f68536bd2a53",
      "name": "Step20.1: Send a message to channel REPORT",
      "type": "n8n-nodes-base.slack",
      "position": [
        -288,
        -656
      ],
      "parameters": {
        "text": "=:speech_balloon: The system has created a GitLab account for <@{{ $node['Step18.1: Convert to text'].json.memberID}}>",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.4,
      "alwaysOutputData": true
    },
    {
      "id": "fdb797f6-ad0c-4480-8964-b7ff573489e7",
      "name": "Step 14.2: Create a new user in Redmine",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1664,
        -288
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.redmine6_Url}}users.json",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n    \"user\": {\n        \"login\": \"{{ $node['Step 11.1: Find and extract the email from the request'].json.login }}\",\n        \"firstname\": \"{{ $node['Step 11.1: Find and extract the email from the request'].json.extracted }}\",\n        \"lastname\": \"{{ $node['Step 11.1: Find and extract the email from the request'].json.extracted }}\",\n        \"mail\": \"{{ $node['Step 11.1: Find and extract the email from the request'].json.rawEmail }}\",\n        \"password\": \"Helloworld@132\" \n    }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2
    },
    {
      "id": "b3693fda-367d-4bf1-a332-0f8b907793c9",
      "name": "Step 15.2: Assign the user to a group in Redmine",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1424,
        -288
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.redmine6_Url}}users/{{ $json.user.id }}.json",
        "method": "PUT",
        "options": {},
        "jsonBody": "={\n  \"user\": {\n    \"group_ids\": [22]\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2
    },
    {
      "id": "b642eeab-129b-4e25-8e87-1328e604eaa7",
      "name": "Step 16.3: Code",
      "type": "n8n-nodes-base.code",
      "position": [
        -1200,
        -288
      ],
      "parameters": {
        "jsCode": "return [\n  {}\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "fa923fa9-afc7-4422-802d-653e6d339169",
      "name": "Step 17.2: Create a new user in Gitlab",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -992,
        -288
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.gitlab_Url}}api/v4/users",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"email\": \"{{ $node['Step 11.1: Find and extract the email from the request'].json.rawEmail }}\",\n  \"username\": \"{{ $node['Step 11.1: Find and extract the email from the request'].json.extracted }}\",\n  \"name\": \"{{ $node['Step 11.1: Find and extract the email from the request'].json.extracted }}\",\n  \"reset_password\": true,\n  \"skip_confirmation\": true\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "64b4bdf5-5fa0-4006-b126-b23a76dba935",
      "name": "Step 18.2: Send reset password",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -768,
        -288
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.gitlab_Url}}api/v4/users/{{ $json.id }}",
        "method": "PUT",
        "options": {},
        "jsonBody": "={\n  \"password\": \"Helloworld@123\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "dcdd16d6-ed9f-4114-a6e8-03e234574c22",
      "name": "Step19.2: Convert to text",
      "type": "n8n-nodes-base.code",
      "position": [
        -528,
        -288
      ],
      "parameters": {
        "jsCode": "const rawEmail = $node['Step 11.1: Find and extract the email from the request'].json.rawEmail;\nconst data = $node[\"Step8: Get many users\"].json.members;\nconst normalizedEmail = rawEmail.toLowerCase();\n\nconst matched = data.find(item => \n  item.profile.email && item.profile.email.toLowerCase() === normalizedEmail\n);\n\nreturn [\n  {\n    json: {\n      memberID: matched ? matched.id : \"Email not found\"\n    }\n  }\n];\n"
      },
      "executeOnce": true,
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "24087559-784c-4c3e-909e-0c5073ab98d3",
      "name": "Step 20.2: Send a message to the member via Slack once their Gitlab and Redmine account has been created.",
      "type": "n8n-nodes-base.slack",
      "position": [
        -288,
        -288
      ],
      "parameters": {
        "text": "=The system has created a Gitlab and Redmine account:\nWeb: {{ $node['Step4: Set Variables'].json.redmine6_Url}}\nLogin: {{ $node['Step 11.1: Find and extract the email from the request'].json.login }}\nPassword: Helloworld@123\n\nWeb: {{ $node['Step4: Set Variables'].json.gitlab_Url}}\nUsername: {{ $node['Step 11.1: Find and extract the email from the request'].json.extracted}}\nEmail: {{ $node['Step 11.1: Find and extract the email from the request'].json.rawEmail}}\nPassword: Helloworld@123\n\nPlease change your password and enable 2FA + update your profile picture + full name for your accounts.",
        "user": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $node['Step19.2: Convert to text'].json.memberID}}"
        },
        "select": "user",
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.4,
      "alwaysOutputData": true
    },
    {
      "id": "08ecca2a-ec5d-4835-9399-4f863c62c172",
      "name": "Step21.1: Send a message to channel REPORT",
      "type": "n8n-nodes-base.slack",
      "position": [
        -64,
        -288
      ],
      "parameters": {
        "text": "=:speech_balloon: The system has created a GitLab and Redmine account for <@{{ $node['Step19.2: Convert to text'].json.memberID}}>",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.4,
      "alwaysOutputData": true
    },
    {
      "id": "88984285-7c07-4504-bcee-6651956df91b",
      "name": "Step 11.2: Find the Redmine account request record on Odoo 18",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2304,
        160
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.web_search_read}}",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"id\": 28,\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"call\",\n  \"params\": {\n    \"model\": \"approval.request\",\n    \"method\": \"web_search_read\",\n    \"args\": [],\n    \"kwargs\": {\n      \"specification\": {\n        \"request_status\": {},\n        \"user_status\": {},\n        \"name\": {},\n        \"reason\": {},\n        \"category_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        },\n        \"date_start\": {},\n        \"date_end\": {},\n        \"activity_ids\": {\n          \"fields\": {}\n        },\n        \"activity_exception_decoration\": {},\n        \"activity_exception_icon\": {},\n        \"activity_state\": {},\n        \"activity_summary\": {},\n        \"activity_type_icon\": {},\n        \"activity_type_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        },\n        \"request_owner_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        }\n      },\n      \"offset\": 0,\n      \"order\": \"create_date DESC\",\n      \"limit\": {{ $node['Step4: Set Variables'].json.limit }},\n      \"context\": {\n        \"lang\": \"en_US\",\n        \"tz\": \"Asia/Bangkok\",\n        \"allowed_company_ids\": [\n          1,\n          2\n        ],\n        \"bin_size\": true,\n        \"params\": {\n          \"view_type\": \"kanban\",\n          \"action\": 408,\n          \"actionStack\": [\n            {\n              \"action\": 408\n            }\n          ]\n        },\n        \"default_category_id\": 14\n      },\n      \"count_limit\": 10001,\n      \"domain\": [\n        \"&\",\n        \"&\",\n        [\n          \"request_status\",\n          \"=\",\n          \"pending\"\n        ],\n        \"&\",\n        \"|\",\n        \"|\",\n        \"|\",\n        [\n          \"reason\",\n          \"ilike\",\n          \"Redmine\"\n        ],\n        [\n          \"reason\",\n          \"ilike\",\n          \"RM\"\n        ],\n        [\n          \"name\",\n          \"ilike\",\n          \"Redmine\"\n        ],\n        [\n          \"name\",\n          \"ilike\",\n          \"Rm\"\n        ],\n        \"&\",\n        [\n          \"create_date\",\n          \">=\",\n          \"{{ $node['Step5: Handle get today'].json.lastday }} 17:55:55\"\n        ],\n        [\n          \"create_date\",\n          \"<=\",\n          \"{{ $node['Step5: Handle get today'].json.date }} 16:55:55\"\n        ],\n        [\n          \"category_id\",\n          \"=\",\n          14\n        ]\n      ]\n    }\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "3a4e97d7-86a3-4766-838c-5f19647e0310",
      "name": "Step12.2: If total_count is not equal to 0 = true",
      "type": "n8n-nodes-base.if",
      "position": [
        -2096,
        160
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "40359ca9-0845-423c-9a96-58f06c9155df",
              "operator": {
                "type": "number",
                "operation": "notEquals"
              },
              "leftValue": "={{ $node['Step 11.2: Find the Redmine account request record on Odoo 18'].json.result.length }}",
              "rightValue": "={{ 0 }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "5bad87a7-2c76-40b8-803f-ac4d56134744",
      "name": "Step13.2: Find and extract the email from the request",
      "type": "n8n-nodes-base.code",
      "position": [
        -1888,
        32
      ],
      "parameters": {
        "jsCode": "const emailRegex = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/;\nconst input = items;\nconst source = input[0] && input[0].json ? input[0].json : input[0];\nconst records = (source && source.result && Array.isArray(source.result.records))\n  ? source.result.records\n  : [];\n\nconst output = records.map(rec => {\n  const tryFindEmail = (text) => {\n    if (!text || typeof text !== 'string') return null;\n    const m = text.match(emailRegex);\n    return m ? m[0] : null;\n  };\n\n  let found = tryFindEmail(rec.name);\n  if (!found) found = tryFindEmail(rec.reason);\n\n  if (!found) {\n    return { json: { id: rec.id, result: \"Email not found\" } };\n  }\n\n  const local = found.split('@')[0];\n  const beforeFirstDot = local.split('.')[0];\n  return { json: { id: rec.id, rawEmail: found, extracted: beforeFirstDot, login: local } };\n});\n\nreturn output;\n"
      },
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "0c399a28-9011-4b0d-90f6-b6f843f9476a",
      "name": "Step 14.3: Get user info in RM6",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1664,
        32
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.redmine6_Url}}users.json?mail={{ $node['Step13.2: Find and extract the email from the request'].json.rawEmail }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2
    },
    {
      "id": "160cc5eb-e665-4d8c-af39-e86268894ed4",
      "name": "Step 15.3: If total_count is not equal to 0 = true",
      "type": "n8n-nodes-base.if",
      "position": [
        -1424,
        32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "40359ca9-0845-423c-9a96-58f06c9155df",
              "operator": {
                "type": "number",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.total_count }}",
              "rightValue": "={{ 0 }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "e1d89d7e-9dc0-4109-bbe2-dbea1347a2ae",
      "name": "Step 16.3: Create a new user in Redmine",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1200,
        32
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.redmine6_Url}}users.json",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n    \"user\": {\n        \"login\": \"{{ $node['Step13.2: Find and extract the email from the request'].json.login }}\",\n        \"firstname\": \"{{ $node['Step13.2: Find and extract the email from the request'].json.extracted }}\",\n        \"lastname\": \"{{ $node['Step13.2: Find and extract the email from the request'].json.extracted }}\",\n        \"mail\": \"{{ $node['Step13.2: Find and extract the email from the request'].json.rawEmail }}\",\n        \"password\": \"Helloworld@132\" \n    }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2
    },
    {
      "id": "a7906046-e160-4440-a81d-031427eb94bb",
      "name": "Step 17.3: Assign the user to a group in Redmine",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -992,
        32
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.redmine6_Url}}users/{{ $json.user.id }}.json",
        "method": "PUT",
        "options": {},
        "jsonBody": "={\n  \"user\": {\n    \"group_ids\": [22]\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2
    },
    {
      "id": "74f1a13e-c852-4139-97ef-f4234c3fd735",
      "name": "Step18.3: Convert to text",
      "type": "n8n-nodes-base.code",
      "position": [
        -768,
        32
      ],
      "parameters": {
        "jsCode": "const rawEmail = $node['Step13.2: Find and extract the email from the request'].json.rawEmail;\n\nconst data = $node[\"Step8: Get many users\"].json.members;\nconst normalizedEmail = rawEmail.toLowerCase();\n\nconst matched = data.find(item => \n  item.profile.email && item.profile.email.toLowerCase() === normalizedEmail\n);\n\nreturn [\n  {\n    json: {\n      memberID: matched ? matched.id : \"Email not found\"\n    }\n  }\n];"
      },
      "executeOnce": true,
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "47480008-7b9a-4a3e-85f1-74527c4e8c0d",
      "name": "Step20.3: Send a message to channel REPORT",
      "type": "n8n-nodes-base.slack",
      "position": [
        -288,
        32
      ],
      "parameters": {
        "text": "=:speech_balloon: The system has created a Redmine account for <@{{ $node['Step18.3: Convert to text'].json.memberID}}>",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.4,
      "alwaysOutputData": true
    },
    {
      "id": "c2a34a6b-0277-414b-8b88-9db2909cbefb",
      "name": "Step 19.3: Send a message to the member via Slack once their Redmine account has been created.",
      "type": "n8n-nodes-base.slack",
      "position": [
        -528,
        32
      ],
      "parameters": {
        "text": "=The system has created a Redmine account:\nWeb: {{ $node['Step4: Set Variables'].json.redmine6_Url}}\nLogin: {{ $node['Step13.2: Find and extract the email from the request'].json.login }}\nPassword: Helloworld@123\n\nPlease change your password and enable 2FA + update your profile picture + full name for your accounts.",
        "user": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $node['Step18.3: Convert to text'].json.memberID}}"
        },
        "select": "user",
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.4,
      "alwaysOutputData": true
    },
    {
      "id": "d2a3ad6e-1b4e-4839-ba95-4c137e1bd9d8",
      "name": "Step 13.3: Find the Gitlab account request record on Odoo 18",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1888,
        336
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.web_search_read }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"id\": 31,\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"call\",\n  \"params\": {\n    \"model\": \"approval.request\",\n    \"method\": \"web_search_read\",\n    \"args\": [],\n    \"kwargs\": {\n      \"specification\": {\n        \"request_status\": {},\n        \"user_status\": {},\n        \"name\": {},\n        \"reason\": {},\n        \"category_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        },\n        \"date_start\": {},\n        \"date_end\": {},\n        \"activity_ids\": {\n          \"fields\": {}\n        },\n        \"activity_exception_decoration\": {},\n        \"activity_exception_icon\": {},\n        \"activity_state\": {},\n        \"activity_summary\": {},\n        \"activity_type_icon\": {},\n        \"activity_type_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        },\n        \"request_owner_id\": {\n          \"fields\": {\n            \"display_name\": {}\n          }\n        }\n      },\n      \"offset\": 0,\n      \"order\": \"create_date DESC\",\n      \"limit\": {{ $node['Step4: Set Variables'].json.limit }},\n      \"context\": {\n        \"lang\": \"en_US\",\n        \"tz\": \"Asia/Bangkok\",\n        \"uid\": 166,\n        \"allowed_company_ids\": [\n          1,\n          2\n        ],\n        \"user_employee_id\": 179,\n        \"user_department_id\": 78,\n        \"bin_size\": true,\n        \"params\": {\n          \"view_type\": \"kanban\",\n          \"action\": 408,\n          \"actionStack\": [\n            {\n              \"action\": 408\n            }\n          ]\n        },\n        \"default_category_id\": 14\n      },\n      \"count_limit\": 10001,\n      \"domain\": [\n        \"&\",\n        \"&\",\n        [\n          \"request_status\",\n          \"=\",\n          \"pending\"\n        ],\n        \"&\",\n        \"&\",\n        [\n          \"create_date\",\n          \">=\",\n          \"{{ $node['Step5: Handle get today'].json.lastday }} 17:55:55\"\n        ],\n        [\n          \"create_date\",\n          \"<=\",\n          \"{{ $node['Step5: Handle get today'].json.date }} 16:55:55\"\n        ],\n        \"|\",\n        [\n          \"reason\",\n          \"ilike\",\n          \"Gitlab\"\n        ],\n        [\n          \"name\",\n          \"ilike\",\n          \"gitlab\"\n        ],\n        [\n          \"category_id\",\n          \"=\",\n          14\n        ]\n      ]\n    }\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "8d0a76ca-431c-4b93-8b4c-2edcd1092052",
      "name": "Step 14.4: If total_count is not equal to 0 = true",
      "type": "n8n-nodes-base.if",
      "position": [
        -1664,
        336
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "40359ca9-0845-423c-9a96-58f06c9155df",
              "operator": {
                "type": "number",
                "operation": "notEquals"
              },
              "leftValue": "={{ $node['Step 13.3: Find the Gitlab account request record on Odoo 18'].json.result.length }}",
              "rightValue": "={{ 0 }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "c3a7ebbd-e70b-457c-bf67-3fa9f2e1f5e2",
      "name": "Step15.4: Find and extract the email from the request",
      "type": "n8n-nodes-base.code",
      "position": [
        -1424,
        336
      ],
      "parameters": {
        "jsCode": "const emailRegex = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/;\nconst input = items;\nconst source = input[0] && input[0].json ? input[0].json : input[0];\nconst records = (source && source.result && Array.isArray(source.result.records))\n  ? source.result.records\n  : [];\n\nconst output = records.map(rec => {\n  const tryFindEmail = (text) => {\n    if (!text || typeof text !== 'string') return null;\n    const m = text.match(emailRegex);\n    return m ? m[0] : null;\n  };\n\n  let found = tryFindEmail(rec.name);\n  if (!found) found = tryFindEmail(rec.reason);\n\n  if (!found) {\n    return { json: { id: rec.id, result: \"Email not found\" } };\n  }\n\n  const local = found.split('@')[0];\n  const beforeFirstDot = local.split('.')[0];\n  return { json: { id: rec.id, rawEmail: found, extracted: beforeFirstDot, login: local } };\n});\n\nreturn output;\n"
      },
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "ae4d1837-158f-4857-93b8-d5e7e84f8c24",
      "name": "Step 16.4: Code",
      "type": "n8n-nodes-base.code",
      "position": [
        -1200,
        336
      ],
      "parameters": {
        "jsCode": "return [\n  {}\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "251bea1b-3856-4be1-8f82-f2ed60344d7f",
      "name": "Step 17.4: Create a new user in Gitlab",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -992,
        336
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.gitlab_Url}}api/v4/users",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"email\": \"{{ $node['Step15.4: Find and extract the email from the request'].json.rawEmail }}\",\n  \"username\": \"{{ $node['Step15.4: Find and extract the email from the request'].json.extracted }}\",\n  \"name\": \"{{ $node['Step15.4: Find and extract the email from the request'].json.extracted }}\",\n  \"reset_password\": true,\n  \"skip_confirmation\": true\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "eab6690d-854d-4a88-82ca-1e4757e501ac",
      "name": "Step 18.4: Send reset password",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -768,
        336
      ],
      "parameters": {
        "url": "={{ $node['Step4: Set Variables'].json.gitlab_Url}}api/v4/users/{{ $json.id }}",
        "method": "PUT",
        "options": {},
        "jsonBody": "={\n  \"password\": \"Helloworld@123\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "48868b7d-264c-4f23-95db-9abd6b60cdc7",
      "name": "Step19.4: Convert to text",
      "type": "n8n-nodes-base.code",
      "position": [
        -528,
        336
      ],
      "parameters": {
        "jsCode": "const rawEmail = $node['Step15.4: Find and extract the email from the request'].json.rawEmail;\n\nconst data = $node[\"Step8: Get many users\"].json.members;\nconst normalizedEmail = rawEmail.toLowerCase();\n\nconst matched = data.find(item => \n  item.profile.email && item.profile.email.toLowerCase() === normalizedEmail\n);\n\nreturn [\n  {\n    json: {\n      memberID: matched ? matched.id : \"Email not found\"\n    }\n  }\n];\n"
      },
      "executeOnce": true,
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "85fb1d5c-1a9c-44f3-b781-e6424679fc20",
      "name": "Step 20.4: Send a message to the member via Gitlab once their Redmine account has been created.",
      "type": "n8n-nodes-base.slack",
      "position": [
        -288,
        336
      ],
      "parameters": {
        "text": "=The system has created a Gitlab account:\n\nWeb: {{ $node['Step4: Set Variables'].json.gitlab_Url}}\nUsername: {{ $node['Step15.4: Find and extract the email from the request'].json.extracted}}\nEmail: {{ $node['Step15.4: Find and extract the email from the request'].json.rawEmail}}\nPassword: Helloworld@123\n\nPlease change your password and enable 2FA + update your profile picture + full name for your account.\n",
        "user": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $node['Step19.4: Convert to text'].json.memberID}}"
        },
        "select": "user",
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.4,
      "alwaysOutputData": true
    },
    {
      "id": "0d704dc3-fb63-4316-b0e2-e04307b9bdd1",
      "name": "Step21.2: Send a message to channel REPORT",
      "type": "n8n-nodes-base.slack",
      "position": [
        -64,
        336
      ],
      "parameters": {
        "text": "=:speech_balloon: The system has created a Gitlab account for <@{{ $node['Step19.4: Convert to text'].json.memberID}}>",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.4,
      "alwaysOutputData": true
    },
    {
      "id": "9a45d96f-5db0-48df-9f32-89325df5de77",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1232,
        -912
      ],
      "parameters": {
        "width": 1136,
        "height": 432,
        "content": "## Create a new user in Gitlab"
      },
      "typeVersion": 1
    },
    {
      "id": "06e41044-60e5-4e2e-8d27-0ef771bcca6d",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1728,
        -384
      ],
      "parameters": {
        "width": 1808,
        "height": 288,
        "content": "## Create a new user in Gitlab and Redmine \n- Then announce the results via Slack."
      },
      "typeVersion": 1
    },
    {
      "id": "8be82ed9-4284-4d7f-ac23-d5ebb10dfbaf",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3888,
        -352
      ],
      "parameters": {
        "width": 464,
        "height": 672,
        "content": "## Odoo 18 called a webhook\n- The flow will be triggered when the Odoo system activates an action."
      },
      "typeVersion": 1
    },
    {
      "id": "4c6d1d54-0532-4de1-99b7-e5fc75ec3b1a",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3392,
        -208
      ],
      "parameters": {
        "color": 4,
        "width": 592,
        "height": 496,
        "content": "## Querying data in Odoo\n- Check if there are any request records for creating Redmine or Gitlab accounts, or both?\n- If there are no records, end the flow.\n- If so, proceed to the next step for filtering."
      },
      "typeVersion": 1
    },
    {
      "id": "44b6758a-45b0-4c2d-9c60-21aa3535c0b5",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2768,
        -336
      ],
      "parameters": {
        "color": 2,
        "width": 416,
        "height": 416,
        "content": "## Find the record requesting both Redmine and Gitlab accounts on Odoo 18.\n- If the data from Odoo includes both (Gitlab and Redmine account creation requests), then extract the \"email\" from the request record.\n- If the data doesn't meet the requirements, branch to the subflow \"Gitlab or Redmine account creation requests\"."
      },
      "typeVersion": 1
    },
    {
      "id": "b7a80327-5fec-4b2a-a909-d114a17f45d1",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2320,
        -480
      ],
      "parameters": {
        "width": 560,
        "height": 352,
        "content": "## Check on Redmine if that user's account already exists (by email).\n- If you already have a Redmine account, proceed to the GitLab account creation flow.\n- If you don't have an account yet, proceed to the GitLab account creation flow."
      },
      "typeVersion": 1
    },
    {
      "id": "0377e3f7-173b-438d-91dd-8b65be6c8faa",
      "name": "Step4: Set Variables",
      "type": "n8n-nodes-base.set",
      "disabled": true,
      "position": [
        -3600,
        -16
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={\n  \"limit\": 100,\n  \"web_search_read_api\": \"\",\n  \"web_read_api\": \"\"\n}"
      },
      "typeVersion": 3.4
    },
    {
      "id": "799bec4b-9ec3-46e0-bcb9-54ab04f4f76d",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1728,
        -912
      ],
      "parameters": {
        "color": 6,
        "width": 464,
        "height": 400,
        "content": "## Check on Gitlab if that user's account already exists (by email).\n- If you already have an account, the flow will end and send a notification via Slack.\n- If you don't have an account yet, proceed to the GitLab account creation flow and send a notification via Slack."
      },
      "typeVersion": 1
    },
    {
      "id": "899d6fa7-c0c0-42ed-8dfc-f42a51a36a9c",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2320,
        48
      ],
      "parameters": {
        "width": 384,
        "height": 320,
        "content": "## Find the Redmine account request record on Odoo 18"
      },
      "typeVersion": 1
    },
    {
      "id": "9edbca4e-5924-42b7-8d29-3885fb08358a",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1904,
        -64
      ],
      "parameters": {
        "width": 1840,
        "height": 272,
        "content": "## Create a new user and assign the user to a group in Redmine"
      },
      "typeVersion": 1
    },
    {
      "id": "2861aa9f-5d5d-4d05-8cea-991d6e0ba032",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1904,
        240
      ],
      "parameters": {
        "width": 1984,
        "height": 272,
        "content": "## Create a new user and send reset password in Gitlab"
      },
      "typeVersion": 1
    },
    {
      "id": "8123d054-13c3-44b0-b4a0-e07a24eb9645",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3888,
        -1776
      ],
      "parameters": {
        "width": 720,
        "height": 1360,
        "content": "# Automate Redmine & GitLab Account Creation for New Hires \u2013 Effortless Onboarding.\n\nThis workflow is designed for HR teams, IT Operations, and System Administrators managing employee onboarding at scale.\nIt\u2019s perfect if you use Odoo 18 to trigger account requests and need Redmine + GitLab accounts created instantly and consistently.\n\n## \ud83d\udcccThe Problem\nManual account creation creates bottlenecks:\n- IT waits for HR notifications that often get missed or delayed.\n- Duplicate accounts pile up when no one checks existing data first.\n- Account creation timelines are inconsistent (sometimes correct, sometimes wrong).\n- Zero visibility into what succeeded or failed.\n\n## \ud83d\udcccWhat It Does\n Catches an employee account creation request from Odoo 18 \u2192 queries employee data \u2192 checks for existing accounts \u2192 creates only the accounts that are missing \u2192 notifies your team via Slack with the full status.\nHere's the flow in detail:\n1. **Webhook trigger from Odoo 18** - automatically fires when your HR team marks an employee as needing Redmine, GitLab, or both.\n2. **Fetch employee data from Odoo 18** - pulls email and account type requirements.\n3. **Deduplicate requests** - separates \"needs both,\" \"GitLab only,\" and \"Redmine only\" cases.\n4. **Check for existing accounts** - queries Redmine and GitLab to avoid duplicate creation attempts.\n5. **Create accounts** - uses Redmine and GitLab APIs to spin up accounts (only if they don't exist).\n6. **Slack notification** - sends a summary showing what was created, skipped, or failed- including error details if something went wrong.\n\n## \ud83d\udcccQuick Setup\n- Generate a Webhook URL in n8n and configure Odoo 18 to call it during onboarding.\n- Add Redmine admin API key and GitLab admin access token.\n- Enable /users.json (Redmine) and /users (GitLab) endpoints.\n- Configure Slack webhook for notifications.\n- Test with sample data, then activate.\n\n## \ud83d\udcccResults\n- Zero duplicates - the system checks before creating.\n- Full visibility - Slack reports every single attempt.\n- Instant delivery - new hires get accounts on day one.\n- Dramatically reduced IT workload.\n\n## \ud83d\udcccTake It Further\n- Auto-assign projects in Redmine/GitLab post-creation.\n- Generate passwords or TOTP setup and email them to new hires.\n- Log everything to Google Sheets, Notion, or a database.\n- Handle multi-tenant setups if your Odoo manages multiple organizations.\n- Add provisioning for Slack, Google Workspace, VPN, or other systems.\n\n## \ud83d\udcccNeed help customizing?\nContact me for consulting and support:\n[Linkedin](https://www.linkedin.com/company/bac-ha-software/posts/?feedView=all) / [Website](https://bachasoftware.com/bhsoft-contacts)"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "4c3c9034-9f71-49d7-833b-2177d3f71a04",
  "connections": {
    "Step 16.3: Code": {
      "main": [
        [
          {
            "node": "Step 17.2: Create a new user in Gitlab",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 16.4: Code": {
      "main": [
        [
          {
            "node": "Step 17.4: Create a new user in Gitlab",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 3: Wait 5s": {
      "main": [
        [
          {
            "node": "Step4: Set Variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step4: Set Variables": {
      "main": [
        [
          {
            "node": "Step5: Handle get today",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step8: Get many users": {
      "main": [
        [
          {
            "node": "Step 9: Find the record requesting both Redmine and Gitlab accounts on Odoo 18.",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step5: Handle get today": {
      "main": [
        [
          {
            "node": "Step 6: Check if there's a requirement to create a Redmine or GitLab account on Odoo 18.",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step18.1: Convert to text": {
      "main": [
        [
          {
            "node": "Step 19.1: Send a message to the member via Slack once their Gitlab account has been created.",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step18.3: Convert to text": {
      "main": [
        [
          {
            "node": "Step 19.3: Send a message to the member via Slack once their Redmine account has been created.",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step19.2: Convert to text": {
      "main": [
        [
          {
            "node": "Step 20.2: Send a message to the member via Slack once their Gitlab and Redmine account has been created.",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step19.4: Convert to text": {
      "main": [
        [
          {
            "node": "Step 20.4: Send a message to the member via Gitlab once their Redmine account has been created.",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step2: Respond to Webhook": {
      "main": [
        [
          {
            "node": "Step 3: Wait 5s",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 17.1: Send reset password": {
      "main": [
        [
          {
            "node": "Step18.1: Convert to text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 18.2: Send reset password": {
      "main": [
        [
          {
            "node": "Step19.2: Convert to text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 18.4: Send reset password": {
      "main": [
        [
          {
            "node": "Step19.4: Convert to text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step12.1: Get user info in RM6": {
      "main": [
        [
          {
            "node": "Step13.1: If total_count is not equal to 0 = true",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 14.3: Get user info in RM6": {
      "main": [
        [
          {
            "node": "Step 15.3: If total_count is not equal to 0 = true",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step1: Odoo 18 called a webhook": {
      "main": [
        [
          {
            "node": "Step2: Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 16.2: Create a new user in Gitlab": {
      "main": [
        [
          {
            "node": "Step 17.1: Send reset password",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 17.2: Create a new user in Gitlab": {
      "main": [
        [
          {
            "node": "Step 18.2: Send reset password",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 17.4: Create a new user in Gitlab": {
      "main": [
        [
          {
            "node": "Step 18.4: Send reset password",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 14.2: Create a new user in Redmine": {
      "main": [
        [
          {
            "node": "Step 15.2: Assign the user to a group in Redmine",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 16.3: Create a new user in Redmine": {
      "main": [
        [
          {
            "node": "Step 17.3: Assign the user to a group in Redmine",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step14.1: Get user information in Gitlab": {
      "main": [
        [
          {
            "node": "Step15.1:  Check if there is a record = true",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step15.1:  Check if there is a record = true": {
      "main": [
        [
          {
            "node": "Step16.1: Send a message to channel",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Step 16.2: Create a new user in Gitlab",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step7: If total_count is not equal to 0 = true": {
      "main": [
        [
          {
            "node": "Step8: Get many users",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "End",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step10: If total_count is not equal to 0 = true": {
      "main": [
        [
          {
            "node": "Step 11.1: Find and extract the email from the request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Step 11.2: Find the Redmine account request record on Odoo 18",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 15.2: Assign the user to a group in Redmine": {
      "main": [
        [
          {
            "node": "Step 16.3: Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 17.3: Assign the user to a group in Redmine": {
      "main": [
        [
          {
            "node": "Step18.3: Convert to text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step12.2: If total_count is not equal to 0 = true": {
      "main": [
        [
          {
            "node": "Step13.2: Find and extract the email from the request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Step 13.3: Find the Gitlab account request record on Odoo 18",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step13.1: If total_count is not equal to 0 = true": {
      "main": [
        [
          {
            "node": "Step14.1: Get user information in Gitlab",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Step 14.2: Create a new user in Redmine",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 14.4: If total_count is not equal to 0 = true": {
      "main": [
        [
          {
            "node": "Step15.4: Find and extract the email from the request",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Step 15.3: If total_count is not equal to 0 = true": {
      "main": [
        [],
        [
          {
            "node": "Step 16.3: Create a new user in Redmine",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step13.2: Find and extract the email from the request": {
      "main": [
        [
          {
            "node": "Step 14.3: Get user info in RM6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step15.4: Find and extract the email from the request": {
      "main": [
        [
          {
            "node": "Step 16.4: Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 11.1: Find and extract the email from the request": {
      "main": [
        [
          {
            "node": "Step12.1: Get user info in RM6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 13.3: Find the Gitlab account request record on Odoo 18": {
      "main": [
        [
          {
            "node": "Step 14.4: If total_count is not equal to 0 = true",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 11.2: Find the Redmine account request record on Odoo 18": {
      "main": [
        [
          {
            "node": "Step12.2: If total_count is not equal to 0 = true",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 9: Find the record requesting both Redmine and Gitlab accounts on Odoo 18.": {
      "main": [
        [
          {
            "node": "Step10: If total_count is not equal to 0 = true",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 6: Check if there's a requirement to create a Redmine or GitLab account on Odoo 18.": {
      "main": [
        [
          {
            "node": "Step7: If total_count is not equal to 0 = true",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 19.1: Send a message to the member via Slack once their Gitlab account has been created.": {
      "main": [
        [
          {
            "node": "Step20.1: Send a message to channel REPORT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 19.3: Send a message to the member via Slack once their Redmine account has been created.": {
      "main": [
        [
          {
            "node": "Step20.3: Send a message to channel REPORT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 20.4: Send a message to the member via Gitlab once their Redmine account has been created.": {
      "main": [
        [
          {
            "node": "Step21.2: Send a message to channel REPORT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 20.2: Send a message to the member via Slack once their Gitlab and Redmine account has been created.": {
      "main": [
        [
          {
            "node": "Step21.1: Send a message to channel REPORT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}