{ "name": "Webhook Events — Gitea/GitHub to CRM", "nodes": [ { "parameters": { "httpMethod": "POST", "path": "git-events", "options": {} }, "id": "webhook-git", "name": "Webhook — Git Events", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "position": [240, 300], "webhookId": "git-events" }, { "parameters": { "jsCode": "const body = $input.first().json.body;\nconst headers = $input.first().json.headers;\n\n// Detect source (Gitea vs GitHub)\nconst isGitea = headers['x-gitea-event'] !== undefined;\nconst isGitHub = headers['x-github-event'] !== undefined;\n\nconst eventType = isGitea \n ? headers['x-gitea-event'] \n : headers['x-github-event'] || 'unknown';\n\nlet result = {\n source: isGitea ? 'gitea' : isGitHub ? 'github' : 'unknown',\n eventType,\n repo: body.repository?.full_name || body.repository?.name || 'unknown',\n action: body.action || 'push',\n sender: body.sender?.login || body.pusher?.name || 'unknown',\n senderEmail: body.sender?.email || body.pusher?.email || '',\n url: '',\n title: '',\n description: ''\n};\n\nswitch (eventType) {\n case 'push':\n const commits = body.commits || [];\n result.title = `Push: ${commits.length} commit(s) to ${result.repo}`;\n result.description = commits.map(c => `- ${c.message}`).join('\\n');\n result.url = body.compare_url || body.compare || '';\n break;\n case 'issues':\n result.title = `Issue ${body.action}: ${body.issue?.title}`;\n result.description = body.issue?.body || '';\n result.url = body.issue?.html_url || '';\n break;\n case 'pull_request':\n result.title = `PR ${body.action}: ${body.pull_request?.title}`;\n result.description = body.pull_request?.body || '';\n result.url = body.pull_request?.html_url || '';\n break;\n case 'create':\n result.title = `Created ${body.ref_type}: ${body.ref}`;\n result.description = `New ${body.ref_type} created in ${result.repo}`;\n break;\n case 'star':\n case 'watch':\n result.title = `${result.sender} starred ${result.repo}`;\n result.description = `Repository now has ${body.repository?.stars_count || body.repository?.stargazers_count || '?'} stars`;\n break;\n default:\n result.title = `${eventType} event on ${result.repo}`;\n result.description = JSON.stringify(body).substring(0, 500);\n}\n\nreturn [{ json: result }];" }, "id": "parse-event", "name": "Parse Git Event", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [460, 300] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "is-significant", "leftValue": "={{ $json.eventType }}", "rightValue": "push,issues,pull_request,star", "operator": { "type": "string", "operation": "contains" } } ], "combinator": "and" }, "options": {} }, "id": "filter-significant", "name": "Filter Significant Events", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [680, 300] }, { "parameters": { "method": "POST", "url": "https://crm.cosmolocal.world/api/v1/objects/notes", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Authorization", "value": "Bearer {{ $env.TWENTY_API_KEY }}" }, { "name": "Content-Type", "value": "application/json" } ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"title\": \"[{{ $json.source }}] {{ $json.title }}\",\n \"body\": \"**Event**: {{ $json.eventType }}\\n**Repo**: {{ $json.repo }}\\n**By**: {{ $json.sender }}\\n\\n{{ $json.description }}\\n\\n{{ $json.url ? '[View on ' + $json.source + '](' + $json.url + ')' : '' }}\"\n}", "options": {} }, "id": "log-to-crm", "name": "Log Activity to CRM", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [900, 240] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "has-email", "leftValue": "={{ $('Parse Git Event').item.json.senderEmail }}", "rightValue": "", "operator": { "type": "string", "operation": "isNotEmpty" } } ], "combinator": "and" }, "options": {} }, "id": "has-sender-email", "name": "Sender Has Email?", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [1120, 240] }, { "parameters": { "method": "GET", "url": "https://crm.cosmolocal.world/api/v1/objects/people", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Authorization", "value": "Bearer {{ $env.TWENTY_API_KEY }}" } ] }, "sendQuery": true, "queryParameters": { "parameters": [ { "name": "filter", "value": "={\"emails\":{\"primaryEmail\":{\"eq\":\"{{ $('Parse Git Event').item.json.senderEmail }}\"}}}" } ] }, "options": {} }, "id": "find-contact", "name": "Find Contact by Email", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1340, 180] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "contact-found", "leftValue": "={{ $json.data?.people?.length }}", "rightValue": 0, "operator": { "type": "number", "operation": "gt" } } ], "combinator": "and" }, "options": {} }, "id": "contact-exists", "name": "Contact Found?", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [1560, 180] }, { "parameters": { "method": "PATCH", "url": "=https://crm.cosmolocal.world/api/v1/objects/people/{{ $json.data.people[0].id }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Authorization", "value": "Bearer {{ $env.TWENTY_API_KEY }}" }, { "name": "Content-Type", "value": "application/json" } ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"updatedAt\": \"{{ new Date().toISOString() }}\"\n}", "options": {} }, "id": "touch-contact", "name": "Touch Contact — Update Timestamp", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1780, 120] }, { "parameters": { "method": "POST", "url": "https://crm.cosmolocal.world/api/v1/objects/people", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Authorization", "value": "Bearer {{ $env.TWENTY_API_KEY }}" }, { "name": "Content-Type", "value": "application/json" } ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"name\": {\n \"firstName\": \"{{ $('Parse Git Event').item.json.sender }}\",\n \"lastName\": \"\"\n },\n \"emails\": {\n \"primaryEmail\": \"{{ $('Parse Git Event').item.json.senderEmail }}\"\n }\n}", "options": {} }, "id": "create-contributor-contact", "name": "Create Contributor Contact", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1780, 280] }, { "parameters": {}, "id": "no-op-skip", "name": "Skip — Not Significant", "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [900, 420] } ], "connections": { "Webhook — Git Events": { "main": [ [ { "node": "Parse Git Event", "type": "main", "index": 0 } ] ] }, "Parse Git Event": { "main": [ [ { "node": "Filter Significant Events", "type": "main", "index": 0 } ] ] }, "Filter Significant Events": { "main": [ [ { "node": "Log Activity to CRM", "type": "main", "index": 0 } ], [ { "node": "Skip — Not Significant", "type": "main", "index": 0 } ] ] }, "Log Activity to CRM": { "main": [ [ { "node": "Sender Has Email?", "type": "main", "index": 0 } ] ] }, "Sender Has Email?": { "main": [ [ { "node": "Find Contact by Email", "type": "main", "index": 0 } ], [] ] }, "Find Contact by Email": { "main": [ [ { "node": "Contact Found?", "type": "main", "index": 0 } ] ] }, "Contact Found?": { "main": [ [ { "node": "Touch Contact — Update Timestamp", "type": "main", "index": 0 } ], [ { "node": "Create Contributor Contact", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1" }, "staticData": null, "tags": [ { "name": "cosmolocal" }, { "name": "git" }, { "name": "crm" } ], "pinData": {} }