AutomationFlowsWeb Scraping › Omniproject Core Sync

Omniproject Core Sync

OmniProject Core Sync. Uses httpRequest. Webhook trigger; 17 nodes.

Webhook trigger★★★★☆ complexity17 nodesHTTP Request
Web Scraping Trigger: Webhook Nodes: 17 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #omniproject-core-sync — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "OmniProject Core Sync",
  "meta": {
    "templateId": "omniproject-core-sync",
    "description": "Reference workflow implementing the OmniProject gateway contract: routes X-OmniProject-Action to Plane / OpenProject and returns a normalized N8nActionResult."
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "omniproject",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "11111111-1111-4111-8111-111111111111",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -360,
        320
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "leftValue": "={{ $json.body.origin }}",
              "rightValue": "={{ $json.body.payload && $json.body.payload.lastUpdatedBy }}",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "22222222-2222-4222-8222-222222222222",
      "name": "Drop Loop? (origin != lastUpdatedBy)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        -160,
        320
      ],
      "notes": "Idempotency / loop guard. The gateway sends X-OmniProject-Idempotency-Key and origin. If an inbound change already originated from this system (origin === lastUpdatedBy), the false branch drops it to prevent webhook storms."
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.body.action }}",
                    "rightValue": "list_projects",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "list_projects"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.body.action }}",
                    "rightValue": "list_issues",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "list_issues"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.body.action }}",
                    "rightValue": "create_issue",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "create_issue"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.body.action }}",
                    "rightValue": "update_issue",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "update_issue"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.body.action }}",
                    "rightValue": "get_resource_capacity",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "get_resource_capacity"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.body.action }}",
                    "rightValue": "get_project_financials",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "get_project_financials"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.body.action }}",
                    "rightValue": "get_portfolio_health",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "get_portfolio_health"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.body.action }}",
                    "rightValue": "get_capabilities",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "get_capabilities"
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "id": "33333333-3333-4333-8333-333333333333",
      "name": "Route Action",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        80,
        240
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ ($json.body.source === 'openproject' ? $env.OPENPROJECT_INSTANCE_URL : $env.PLANE_INSTANCE_URL) }}/api/v1/projects",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.body.payload.userContext.token }}"
            }
          ]
        },
        "options": {}
      },
      "id": "44444444-4444-4444-8444-444444444444",
      "name": "List Projects (Plane/OpenProject)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        340,
        -120
      ],
      "notes": "Dynamic per-user impersonation: Authorization uses the active user's OIDC token from payload.userContext.token, not a shared admin key."
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ ($json.body.source === 'openproject' ? $env.OPENPROJECT_INSTANCE_URL : $env.PLANE_INSTANCE_URL) }}/api/v1/projects/{{ $json.body.payload.projectId }}/issues",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.body.payload.userContext.token }}"
            }
          ]
        },
        "options": {}
      },
      "id": "55555555-5555-4555-8555-555555555555",
      "name": "List Issues",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        340,
        80
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ ($json.body.source === 'openproject' ? $env.OPENPROJECT_INSTANCE_URL : $env.PLANE_INSTANCE_URL) }}/api/v1/projects/{{ $json.body.payload.projectId }}/issues",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.body.payload.userContext.token }}"
            },
            {
              "name": "X-OmniProject-Idempotency-Key",
              "value": "={{ $json.body.idempotencyKey }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify(Object.assign({}, $json.body.payload, { lastUpdatedBy: $json.body.origin })) }}",
        "options": {}
      },
      "id": "66666666-6666-4666-8666-666666666666",
      "name": "Create Issue",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        340,
        300
      ],
      "notes": "Writes lastUpdatedBy = origin so the loop guard can drop the echo webhook this write will trigger."
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "={{ ($json.body.source === 'openproject' ? $env.OPENPROJECT_INSTANCE_URL : $env.PLANE_INSTANCE_URL) }}/api/v1/projects/{{ $json.body.payload.projectId }}/issues/{{ $json.body.payload.issueId }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.body.payload.userContext.token }}"
            },
            {
              "name": "X-OmniProject-Idempotency-Key",
              "value": "={{ $json.body.idempotencyKey }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify(Object.assign({}, $json.body.payload, { lastUpdatedBy: $json.body.origin })) }}",
        "options": {}
      },
      "id": "77777777-7777-4777-8777-777777777777",
      "name": "Update Issue",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        340,
        520
      ]
    },
    {
      "parameters": {
        "jsCode": "// Normalize any backend response into the N8nActionResult contract\n// expected by the OmniProject gateway: { success, data, message }.\nconst action = $('Webhook').first().json.body.action;\nconst rows = items.map((i) => i.json);\nconst data = rows.length === 1 ? rows[0] : rows;\nreturn [{ json: { success: true, data, message: action + ' ok' } }];"
      },
      "id": "88888888-8888-4888-8888-888888888888",
      "name": "Normalize \u2192 N8nActionResult",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        620,
        200
      ]
    },
    {
      "parameters": {
        "jsCode": "const action = $('Webhook').first().json.body.action;\nreturn [{ json: { success: false, data: {}, message: 'Unsupported action: ' + action } }];"
      },
      "id": "99999999-9999-4999-8999-999999999999",
      "name": "Unsupported Action",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        620,
        480
      ]
    },
    {
      "parameters": {
        "jsCode": "return [{ json: { success: true, data: { dropped: true }, message: 'Loop mutation dropped (origin === lastUpdatedBy)' } }];"
      },
      "id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
      "name": "Drop (loop)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        80,
        560
      ]
    },
    {
      "parameters": {
        "respondWith": "firstIncomingItem",
        "options": {}
      },
      "id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
      "name": "Respond N8nActionResult",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        880,
        320
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $env.DYNAMICS_BC_URL }}/api/v2.0/companies/jobs?$filter=projectId eq '{{ $json.body.payload.projectId }}'",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.body.payload.userContext.token }}"
            }
          ]
        },
        "options": {}
      },
      "id": "d1d1d1d1-d1d1-4d1d-8d1d-d1d1d1d1d1d1",
      "name": "Resource Capacity (Enterprise: Dynamics 365 BC Jobs / OSS: TaskJuggler)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        340,
        740
      ],
      "notes": "ENTERPRISE: Microsoft Dynamics 365 Business Central Job Ledger (/api/v2.0/companies/jobs). OSS: replace with TaskJuggler resource report or native worker-assignment tables. Map \u2192 ResourceCapacity[] (resourceId, resourceName, role, allocationPercentage, assignedHours, availableHours, utilizationState)."
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $env.SAP_S4_URL }}/sap/opu/odata/sap/API_ENTERPRISE_PROJECT_SRV/A_EnterpriseProject('{{ $json.body.payload.projectId }}')",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.body.payload.userContext.token }}"
            }
          ]
        },
        "options": {}
      },
      "id": "d2d2d2d2-d2d2-4d2d-8d2d-d2d2d2d2d2d2",
      "name": "Project Financials (Enterprise: SAP S/4HANA / OSS: Dolibarr|Odoo)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        340,
        940
      ],
      "notes": "ENTERPRISE: SAP S/4HANA Project API (OData). OSS: pull budget line items from Dolibarr ERP or Odoo Community tables. Compute EVM and map \u2192 ProjectFinancials (currency, budgetAllocated, actualBurn, earnedValue, cpi, spi, financialHealth, forecastCostAtCompletion)."
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $env.OPENPROJECT_INSTANCE_URL }}/api/v3/projects",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.body.payload.userContext.token }}"
            }
          ]
        },
        "options": {}
      },
      "id": "d3d3d3d3-d3d3-4d3d-8d3d-d3d3d3d3d3d3",
      "name": "Portfolio Health (OSS: OpenProject PPM / Enterprise: SAP|Dynamics rollup)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        340,
        1140
      ],
      "notes": "OSS: OpenProject high-level PPM project health records (/api/v3/projects). ENTERPRISE: aggregate from SAP S/4HANA / Dynamics 365 rollups. Map \u2192 PortfolioHealthSummary[] (projectId, projectName, ragStatus, scheduleVarianceDays, budgetVariancePercentage, activeBlockersCount)."
    },
    {
      "parameters": {
        "jsCode": "// Declare which data domains THIS deployment's backends can populate.\n// Edit to match what you've wired (resources/financials need a capacity / ERP\n// source; history needs backend journals/changelog; raid needs a risk\n// register). The gateway exposes this at GET /api/capabilities so the UI labels\n// available reports/views.\nreturn [{ json: { success: true, data: {\n  issues: true,\n  scheduling: true,\n  resources: false,\n  financials: false,\n  portfolio: true,\n  baseline: false,\n  blockers: false,\n  history: false,\n  raid: false\n} } }];"
      },
      "id": "e1e1e1e1-e1e1-4e1e-8e1e-e1e1e1e1e1e1",
      "name": "Capabilities (edit me)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        340,
        1340
      ]
    },
    {
      "parameters": {
        "content": "## Dual routing: Open-Source vs Enterprise\n\nEach analytics action has an OSS and an Enterprise mapping \u2014 pick one HTTP node per action and delete the other, then normalize to the Part 1 schema.\n\n| Action | Open-source | Enterprise |\n|---|---|---|\n| get_resource_capacity | TaskJuggler / worker tables | Dynamics 365 BC Jobs |\n| get_project_financials | Dolibarr / Odoo CE | SAP S/4HANA Project API |\n| get_portfolio_health | OpenProject PPM | SAP / Dynamics rollup |\n\nAll variants MUST return the exact `ResourceCapacity` / `ProjectFinancials` / `PortfolioHealthSummary` shapes before the Normalize node.",
        "height": 300,
        "width": 460
      },
      "id": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
      "name": "Dual Routing Notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -360,
        220
      ]
    },
    {
      "parameters": {
        "content": "## OmniProject Core Sync\n\nImplements the gateway contract:\n- **Webhook** receives `POST` with body `{ action, payload, source, origin, idempotencyKey }` and headers `X-OmniProject-Action / -Source / -Origin / -Idempotency-Key`.\n- **Loop guard** drops echoes where `origin === payload.lastUpdatedBy`.\n- **Route Action** switches on `list_projects | list_issues | create_issue | update_issue`.\n- HTTP nodes call Plane (`PLANE_INSTANCE_URL`) or OpenProject (`OPENPROJECT_INSTANCE_URL`) and authenticate **as the active user** via `payload.userContext.token`.\n- **Normalize** returns `{ success, data, message }` (N8nActionResult).\n\nSet env vars `PLANE_INSTANCE_URL` and `OPENPROJECT_INSTANCE_URL` in n8n. Adjust the API paths to your backend versions.",
        "height": 320,
        "width": 420
      },
      "id": "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
      "name": "Notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -360,
        -160
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Drop Loop? (origin != lastUpdatedBy)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Drop Loop? (origin != lastUpdatedBy)": {
      "main": [
        [
          {
            "node": "Route Action",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Drop (loop)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route Action": {
      "main": [
        [
          {
            "node": "List Projects (Plane/OpenProject)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "List Issues",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create Issue",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Issue",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Resource Capacity (Enterprise: Dynamics 365 BC Jobs / OSS: TaskJuggler)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Project Financials (Enterprise: SAP S/4HANA / OSS: Dolibarr|Odoo)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Portfolio Health (OSS: OpenProject PPM / Enterprise: SAP|Dynamics rollup)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Capabilities (edit me)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Unsupported Action",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Capabilities (edit me)": {
      "main": [
        [
          {
            "node": "Respond N8nActionResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Resource Capacity (Enterprise: Dynamics 365 BC Jobs / OSS: TaskJuggler)": {
      "main": [
        [
          {
            "node": "Normalize \u2192 N8nActionResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Project Financials (Enterprise: SAP S/4HANA / OSS: Dolibarr|Odoo)": {
      "main": [
        [
          {
            "node": "Normalize \u2192 N8nActionResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Portfolio Health (OSS: OpenProject PPM / Enterprise: SAP|Dynamics rollup)": {
      "main": [
        [
          {
            "node": "Normalize \u2192 N8nActionResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "List Projects (Plane/OpenProject)": {
      "main": [
        [
          {
            "node": "Normalize \u2192 N8nActionResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "List Issues": {
      "main": [
        [
          {
            "node": "Normalize \u2192 N8nActionResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Issue": {
      "main": [
        [
          {
            "node": "Normalize \u2192 N8nActionResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Issue": {
      "main": [
        [
          {
            "node": "Normalize \u2192 N8nActionResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize \u2192 N8nActionResult": {
      "main": [
        [
          {
            "node": "Respond N8nActionResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Unsupported Action": {
      "main": [
        [
          {
            "node": "Respond N8nActionResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Drop (loop)": {
      "main": [
        [
          {
            "node": "Respond N8nActionResult",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

OmniProject Core Sync. Uses httpRequest. Webhook trigger; 17 nodes.

Source: https://github.com/walshd1/Omniproject/blob/main/artifacts/n8n-blueprints/omniproject-core-sync.json — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

This n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports – get proper commit history, visual di

n8n, Execute Workflow Trigger, HTTP Request +1
Web Scraping

This flow creates dummy files for every item added in your *Arrs (Radarr/Sonarr) with the tag .

HTTP Request, Ssh
Web Scraping

eek-Go v2 (Batch-Then-Review). Uses httpRequest. Webhook trigger; 75 nodes.

HTTP Request
Web Scraping

This workflow receives webhook requests from a content calendar and uses the X API v2 to publish text posts, threads, image/video posts, and polls, as well as delete existing posts and run a credentia

HTTP Request
Web Scraping

This workflow acts as a central API gateway for all technical indicator agents in the Binance Spot Market Quant AI system. It listens for incoming webhook requests and dynamically routes them to the c

HTTP Request