Crear tickets de Linear a partir de contenido de Notion
Este es unEngineering, Productflujo de automatización del dominio deautomatización que contiene 24 nodos.Utiliza principalmente nodos como If, Set, Code, Filter, Linear. Crear tickets de Linear a partir de contenido de Notion
- •Clave de API de Notion
- •Pueden requerirse credenciales de autenticación para la API de destino
- •Clave de API de OpenAI
- •Punto final de HTTP Webhook (n8n generará automáticamente)
Nodos utilizados (24)
Categoría
{
"meta": {
"instanceId": "cb484ba7b742928a2048bf8829668bed5b5ad9787579adea888f05980292a4a7"
},
"nodes": [
{
"id": "7d11aa76-c7bf-4aa3-9f94-fb2231f5055b",
"name": "Iterar sobre elementos",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-1460,
2080
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "bea6febe-077f-4b90-887c-9c82954ef5d9",
"name": "Obtener detalles del equipo de Linear",
"type": "n8n-nodes-base.graphql",
"position": [
-1220,
1760
],
"parameters": {
"query": "=query GetTeamsAndProjects {\n teams(filter: {name: {contains: \"{{ $json['Linear team name'] }}\"}}) {\n nodes {\n id\n name\n members {\n nodes {\n id\n name\n email\n }\n }\n projects {\n nodes {\n id\n name\n description\n }\n }\n }\n }\n}\n",
"endpoint": "https://api.linear.app/graphql",
"requestMethod": "GET",
"authentication": "headerAuth"
},
"credentials": {
"httpHeaderAuth": {
"id": "zYILrk4RKFqdP66s",
"name": "[Omar] Notion credentials for GraphQL API"
}
},
"executeOnce": true,
"typeVersion": 1,
"continueOnFail": true
},
{
"id": "27a2111b-716b-4150-af92-ab90d7e83642",
"name": "Obtener contenido de la incidencia",
"type": "n8n-nodes-base.notion",
"position": [
-1220,
2340
],
"parameters": {
"blockId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set assignee and title').item.json.id }}"
},
"resource": "block",
"operation": "getAll",
"returnAll": true,
"simplifyOutput": false,
"fetchNestedBlocks": true
},
"credentials": {
"notionApi": {
"id": "80",
"name": "Notion david-internal"
}
},
"typeVersion": 2.1,
"alwaysOutputData": true
},
{
"id": "96bde9b1-b84e-445a-b90c-6dd4031076aa",
"name": "Agregar",
"type": "n8n-nodes-base.aggregate",
"position": [
-560,
2340
],
"parameters": {
"options": {},
"fieldsToAggregate": {
"fieldToAggregate": [
{
"fieldToAggregate": "markdown"
}
]
}
},
"typeVersion": 1
},
{
"id": "45c63a2d-d457-4882-ac99-4b8e4a63ae43",
"name": "Preparar datos de la incidencia",
"type": "n8n-nodes-base.set",
"position": [
-1220,
2620
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "e1b44489-ee32-4da4-816e-f56d640a9731",
"name": "title",
"type": "string",
"value": "={{ $if($('Set assignee and title').item.json.title.length <= 70, $('Set assignee and title').item.json.title, $('Shorten title').item.json.message.content) }}"
},
{
"id": "f3fab4f6-8ea3-4b93-91ea-ec08c2d9eded",
"name": "description",
"type": "string",
"value": "=_Issue created automatically from a [Notion block]({{ $('Set page URL').last().json.page_url + '?pvs=4#' + $('Loop Over Items').item.json.id.replaceAll('-', '') }})_ {{ $if($('Set assignee and title').item.json.assignee_fragment && !$('Set assignee and title').item.json.assignee, \"\\nAssignee '\" + $('Set assignee and title').item.json.assignee_fragment + \"' not found\", '') }}\n\n{{ $json.markdown?.join('\\n') }}"
}
]
}
},
"typeVersion": 3.3
},
{
"id": "ce619c7d-8363-48ec-86c8-a1f16097eb3e",
"name": "Crear incidencia en Linear",
"type": "n8n-nodes-base.linear",
"position": [
-1000,
2620
],
"parameters": {
"title": "={{ $json.title }}",
"teamId": "={{ $('Set team ID').item.json.team_id }}",
"additionalFields": {
"assigneeId": "={{ $('Set assignee and title').item.json.assignee.id }}",
"description": "={{ $json.description }}"
}
},
"credentials": {
"linearApi": {
"id": "218",
"name": "Linear account (David)"
}
},
"typeVersion": 1
},
{
"id": "cc365fa1-a2cc-430c-8cb4-5d708b7a66b9",
"name": "Establecer asignado y título",
"type": "n8n-nodes-base.code",
"position": [
-1220,
2080
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Set the title and the assignee based on the first line of the item\n\nlet firstLine = $json[$json.type].text.reduce((s, o) => {\n return s + o.text.content\n}, \"\")\nconsole.log('firstLin', firstLine)\nconst regex = /^(\\[[^\\]]*\\]\\s)?(.+)$/;\nconst match = firstLine.match(regex);\nconsole.log('match', match)\n\nif (match) {\n // If the first part is not present, match[1] will be undefined\n item.json.assignee_fragment = match[1]?.slice(1, -2) || null;\n item.json.title = match[2];\n} else {\n item.json.title = firstLine;\n item.json.assignee_fragment = null;\n}\n\n// Set the new title in Notion format\n// $url will be set later, once we have it\nconst prefix_link = [\n {\"text\":{\"content\":\"[\"}},\n {\"text\":{\"content\":\"In Linear\", \"link\":{\"url\": \"$url\"} }},\n {\"text\":{\"content\":\"] \"}}\n]\nitem.json.new_content = {\n \"rich_text\": [...prefix_link, ...item.json.to_do.text]\n}\n\n// Find a matching assignee\nconst members = $('Fetch Linear team details').item.json.data.teams.nodes[0].members.nodes\nconsole.log('people', members)\nconsole.log('fragment', item.json.assignee_fragment)\nconst matching_people = members.filter(p => \n p.name.toLowerCase().startsWith(item.json.assignee_fragment?.toLowerCase())\n)\nconsole.log('mpeople', matching_people)\nif (matching_people.length > 0) {\n item.json.assignee = matching_people[0]\n}\n\nitem.pairedItem = 0\n\nreturn item"
},
"typeVersion": 2
},
{
"id": "15d9c526-95f3-4209-9a9f-a0ba4c09a67e",
"name": "¿Equipo faltante?",
"type": "n8n-nodes-base.if",
"position": [
-1000,
1760
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "047fbe62-ebab-44ab-89b1-232f5f15874d",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.data.teams?.nodes?.length < 1 }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2
},
{
"id": "491a1176-18a7-442a-8f64-a8c946ac25dc",
"name": "Establecer URL de la página",
"type": "n8n-nodes-base.set",
"position": [
-1000,
2340
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "0b0ced59-14c9-43e9-a5ee-f4b1862fccd6",
"name": "page_url",
"type": "string",
"value": "={{ $('n8n Form Trigger').item.json['Notion block URL'].substr(0, $('n8n Form Trigger').item.json['Notion block URL'].indexOf('?')) || $('n8n Form Trigger').item.json['Notion block URL'] }}"
},
{
"id": "3df2b2e6-38ca-4fb6-b00c-e3a6ceb3f9b3",
"name": "root_content",
"type": "object",
"value": "={{ $('Set assignee and title').item.json[$('Set assignee and title').item.json.type] }}"
},
{
"id": "41a18b43-49fd-45a5-850d-55b9f08f9b93",
"name": "root_id",
"type": "string",
"value": "={{ $('Set assignee and title').item.json.id }}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.3
},
{
"id": "bd107990-b009-4b81-8fee-a80c9ab09b4c",
"name": "Establecer ID de equipo",
"type": "n8n-nodes-base.set",
"position": [
-740,
1760
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "b22a4a67-67b5-415a-ab38-4d7f781e8b7e",
"name": "team_id",
"type": "string",
"value": "={{ $json.data.teams.nodes[0].id }}"
}
]
}
},
"typeVersion": 3.3
},
{
"id": "010105b6-38a2-4859-9e5d-b9624addeacc",
"name": "Agregar enlace al bloque Notion",
"type": "n8n-nodes-base.httpRequest",
"position": [
-560,
2620
],
"parameters": {
"url": "=https://api.notion.com/v1/blocks/{{ $('Loop Over Items').item.json.id }}",
"method": "PATCH",
"options": {},
"jsonBody": "={\n \"to_do\":\n {{ JSON.stringify($('Set assignee and title').item.json.new_content).replace('$url', $json.data.issue.url) }}\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"headerParameters": {
"parameters": [
{
"name": "Notion-Version",
"value": "2022-06-28"
}
]
},
"nodeCredentialType": "notionApi"
},
"credentials": {
"notionApi": {
"id": "80",
"name": "Notion david-internal"
}
},
"typeVersion": 4.1
},
{
"id": "bb88f7df-deef-4c9d-b8af-3e59bf0e0b7d",
"name": "Obtener URL de la incidencia",
"type": "n8n-nodes-base.graphql",
"position": [
-780,
2620
],
"parameters": {
"query": "=query IssueDetails {\n issue(id: \"{{ $json.id }}\") {\n url\n }\n}",
"endpoint": "https://api.linear.app/graphql",
"requestMethod": "GET",
"authentication": "headerAuth"
},
"credentials": {
"httpHeaderAuth": {
"id": "zYILrk4RKFqdP66s",
"name": "[Omar] Notion credentials for GraphQL API"
}
},
"executeOnce": true,
"typeVersion": 1,
"continueOnFail": true
},
{
"id": "00736596-08ee-4ff6-b907-bd454dd406d9",
"name": "Acortar título",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
-1000,
2080
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4",
"cachedResultName": "GPT-4"
},
"options": {},
"messages": {
"values": [
{
"content": "=Make the following text more concise, so that it's max 150 chars long. If it's already less than 70 chars long, just return the original text. Do not return anything else other than the text.\n\nTEXT:\n{{ $json.title }}"
}
]
}
},
"credentials": {
"openAiApi": {
"id": "VQtv7frm7eLiEDnd",
"name": "OpenAi account 7"
}
},
"typeVersion": 1
},
{
"id": "729928a2-8a5e-4b25-82be-ba1916b9953f",
"name": "Nota adhesiva 1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1260,
2020
],
"parameters": {
"color": 7,
"width": 877.8549621677266,
"height": 214.7985362687051,
"content": "### Figure out issue assignee and title (shortening if necessary)"
},
"typeVersion": 1
},
{
"id": "86d3b72f-f235-4b37-877f-c8ab1f39ba30",
"name": "Nota adhesiva 2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1260,
2280
],
"parameters": {
"color": 7,
"width": 877.8549621677266,
"height": 216.9904777194533,
"content": "### Compose issue description"
},
"typeVersion": 1
},
{
"id": "01c06352-76fa-4d63-b37b-2a0239d81302",
"name": "Nota adhesiva 4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1260,
2560
],
"parameters": {
"color": 7,
"width": 877.8549621677266,
"height": 216.9904777194533,
"content": "### Create issue and add link to it in Notion"
},
"typeVersion": 1
},
{
"id": "1f152f08-0c5a-4806-bd85-7e9a5e386fe4",
"name": "Nota adhesiva 5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1260,
1500
],
"parameters": {
"color": 7,
"width": 1164.99929221574,
"height": 442.760447146518,
"content": "### Get the issues to create from Notion (and load Linear team details)"
},
"typeVersion": 1
},
{
"id": "839d1815-419d-4acb-8668-94f1cbe45f1f",
"name": "Nota adhesiva 3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1780,
1700
],
"parameters": {
"height": 278.9250421966361,
"content": "# Try me out\n1. In the form trigger node, enter the names of your Linear team(s) to display on the form \n2. Make sure your Notion page is formatted according to the [spec](https://www.notion.so/n8n/Template-for-design-review-automatic-Linear-import-8848dd09892341969faedd1313eea586?pvs=4) and shared with your Notion integration\n2. Click the 'test workflow' button below"
},
"typeVersion": 1
},
{
"id": "f9bc6e67-a9f0-4c9b-a9b3-fdea1fd9de3e",
"name": "Solo bloques de tareas no importados y no marcados",
"type": "n8n-nodes-base.filter",
"position": [
-220,
1760
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d7e85c09-8548-4fc8-a8a9-636e4529e9d9",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.type }}",
"rightValue": "to_do"
},
{
"id": "13fb565d-8951-4c89-9684-85c357459794",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ !$json.to_do.text.reduce((s, o) => s + o.plain_text, \"\").startsWith('[In Linear]') }}",
"rightValue": ""
},
{
"id": "0a9c8e94-11ec-4317-8de5-f22862555b78",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.to_do.checked }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2
},
{
"id": "186a4272-4550-441e-9ef2-66de2dac5b8a",
"name": "Disparador de formulario n8n",
"type": "n8n-nodes-base.formTrigger",
"position": [
-1460,
1760
],
"webhookId": "5a631d63-f899-4967-acad-69924674e96a",
"parameters": {
"path": "5a631d63-f899-4967-acad-69924674e96a",
"formTitle": "Import Linear issues from Notion",
"formFields": {
"values": [
{
"fieldLabel": "Notion page URL",
"requiredField": true
},
{
"fieldType": "dropdown",
"fieldLabel": "Linear team name",
"fieldOptions": {
"values": [
{
"option": "AI"
},
{
"option": "Adore"
},
{
"option": "Payday"
},
{
"option": "NODES"
}
]
},
"requiredField": true
}
]
},
"responseMode": "responseNode",
"formDescription": "More information on Notion formatting required here: https://www.notion.so/n8n/8848dd09892341969faedd1313eea586"
},
"typeVersion": 2
},
{
"id": "0fed5dbe-54e9-4bc3-8ab3-6175347ecce7",
"name": "Obtener incidencias",
"type": "n8n-nodes-base.notion",
"onError": "continueErrorOutput",
"position": [
-500,
1760
],
"parameters": {
"blockId": {
"__rl": true,
"mode": "url",
"value": "={{ $('n8n Form Trigger').item.json['Notion page URL'] }}"
},
"resource": "block",
"operation": "getAll",
"returnAll": true,
"simplifyOutput": false
},
"credentials": {
"notionApi": {
"id": "80",
"name": "Notion david-internal"
}
},
"typeVersion": 2.1
},
{
"id": "7cc756ad-55f3-4596-989a-df90d4c829c7",
"name": "Convertir contenido a Markdown",
"type": "n8n-nodes-base.code",
"position": [
-780,
2340
],
"parameters": {
"jsCode": "function extractMarkdown(obj) {\n console.log('obj', obj.text)\n return obj.text.reduce((s, o) => {\n if(o.text?.link) {\n return s + '[' + o.text.content + '](' + o.text.link?.url + ')'\n }\n return s + o.text.content\n }, \"\")\n}\n\n\nconst indent = \" \"; // Four spaces\nlet parent_ids = [$input.all()[0].json.root_id]\n\nfor(item of $input.all()){\n\n // Generate the markdown\n\n if(item.json.type) {\n \n const type = item.json.type\n \n if(type == 'bulleted_list_item' || type == 'toggle') {\n item.json.markdown = '* ' + extractMarkdown(item.json[type])\n } else if(type == 'numbered_list_item') {\n item.json.markdown = '1. ' + extractMarkdown(item.json[type])\n } else if(type == 'to_do') {\n item.json.markdown = '+ [ ] ' + extractMarkdown(item.json[type])\n } else if(type == 'image') {\n item.json.markdown = ''\n } else if(type == 'video') {\n item.json.markdown = '[🎬 Video]('+$input.all()[0].json.page_url + '?pvs=4#' + item.json.id.replaceAll('-', '') +')'\n } else {\n item.json.markdown = extractMarkdown(item.json[type])\n }\n \n // Figure out how much to indent it\n // If parent ID is in list, remove everything after that ID\n // If parent ID is not in list, add it\n // If parent is the same, do nothing\n const parent_id_index = parent_ids.indexOf(item.json.parent_id);\n \n // Check if the value is found\n if (parent_id_index !== -1) {\n // Remove all elements after the first occurrence\n parent_ids.splice(parent_id_index + 1);\n } else {\n parent_ids.push(item.json.parent_id)\n }\n \n // Indent the markdown\n //if (type != \"image\") {\n item.json.markdown = indent.repeat(parent_ids.length - 1) + item.json.markdown\n //}\n }\n}\n\n// On returning, add in the root block content at the beginning\nreturn [\n ...[\n {\n \"json\": {\n \"markdown\":\nextractMarkdown($input.all()[0].json.root_content)\n },\n \"pairedItem\": 0\n }\n ],\n ...$input.all()\n]"
},
"typeVersion": 2
},
{
"id": "2bbd43a2-aebe-4ee5-8682-d76791214168",
"name": "Responder con error",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-220,
1580
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "{\n \"formSubmittedText\": \"Couldn't fetch page content from Notion. Is it shared with your Notion integration?\"\n}"
},
"typeVersion": 1
},
{
"id": "c39912c4-d33f-4d79-9d5c-e71bd0f2517d",
"name": "Responder con error 1",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-740,
1580
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"formSubmittedText\": \"Couldn't find the team called '\" + {{ $('n8n Form Trigger').item.json['Linear team name'] }} + \"'\"\n} "
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"96bde9b1-b84e-445a-b90c-6dd4031076aa": {
"main": [
[
{
"node": "45c63a2d-d457-4882-ac99-4b8e4a63ae43",
"type": "main",
"index": 0
}
]
]
},
"0fed5dbe-54e9-4bc3-8ab3-6175347ecce7": {
"main": [
[
{
"node": "f9bc6e67-a9f0-4c9b-a9b3-fdea1fd9de3e",
"type": "main",
"index": 0
}
],
[
{
"node": "2bbd43a2-aebe-4ee5-8682-d76791214168",
"type": "main",
"index": 0
}
]
]
},
"bd107990-b009-4b81-8fee-a80c9ab09b4c": {
"main": [
[
{
"node": "0fed5dbe-54e9-4bc3-8ab3-6175347ecce7",
"type": "main",
"index": 0
}
]
]
},
"491a1176-18a7-442a-8f64-a8c946ac25dc": {
"main": [
[
{
"node": "7cc756ad-55f3-4596-989a-df90d4c829c7",
"type": "main",
"index": 0
}
]
]
},
"bb88f7df-deef-4c9d-b8af-3e59bf0e0b7d": {
"main": [
[
{
"node": "010105b6-38a2-4859-9e5d-b9624addeacc",
"type": "main",
"index": 0
}
]
]
},
"00736596-08ee-4ff6-b907-bd454dd406d9": {
"main": [
[
{
"node": "27a2111b-716b-4150-af92-ab90d7e83642",
"type": "main",
"index": 0
}
]
]
},
"15d9c526-95f3-4209-9a9f-a0ba4c09a67e": {
"main": [
[
{
"node": "c39912c4-d33f-4d79-9d5c-e71bd0f2517d",
"type": "main",
"index": 0
}
],
[
{
"node": "bd107990-b009-4b81-8fee-a80c9ab09b4c",
"type": "main",
"index": 0
}
]
]
},
"7d11aa76-c7bf-4aa3-9f94-fb2231f5055b": {
"main": [
null,
[
{
"node": "cc365fa1-a2cc-430c-8cb4-5d708b7a66b9",
"type": "main",
"index": 0
}
]
]
},
"186a4272-4550-441e-9ef2-66de2dac5b8a": {
"main": [
[
{
"node": "bea6febe-077f-4b90-887c-9c82954ef5d9",
"type": "main",
"index": 0
}
]
]
},
"27a2111b-716b-4150-af92-ab90d7e83642": {
"main": [
[
{
"node": "491a1176-18a7-442a-8f64-a8c946ac25dc",
"type": "main",
"index": 0
}
]
]
},
"45c63a2d-d457-4882-ac99-4b8e4a63ae43": {
"main": [
[
{
"node": "ce619c7d-8363-48ec-86c8-a1f16097eb3e",
"type": "main",
"index": 0
}
]
]
},
"ce619c7d-8363-48ec-86c8-a1f16097eb3e": {
"main": [
[
{
"node": "bb88f7df-deef-4c9d-b8af-3e59bf0e0b7d",
"type": "main",
"index": 0
}
]
]
},
"cc365fa1-a2cc-430c-8cb4-5d708b7a66b9": {
"main": [
[
{
"node": "00736596-08ee-4ff6-b907-bd454dd406d9",
"type": "main",
"index": 0
}
]
]
},
"010105b6-38a2-4859-9e5d-b9624addeacc": {
"main": [
[
{
"node": "7d11aa76-c7bf-4aa3-9f94-fb2231f5055b",
"type": "main",
"index": 0
}
]
]
},
"bea6febe-077f-4b90-887c-9c82954ef5d9": {
"main": [
[
{
"node": "15d9c526-95f3-4209-9a9f-a0ba4c09a67e",
"type": "main",
"index": 0
}
]
]
},
"7cc756ad-55f3-4596-989a-df90d4c829c7": {
"main": [
[
{
"node": "96bde9b1-b84e-445a-b90c-6dd4031076aa",
"type": "main",
"index": 0
}
]
]
},
"f9bc6e67-a9f0-4c9b-a9b3-fdea1fd9de3e": {
"main": [
[
{
"node": "7d11aa76-c7bf-4aa3-9f94-fb2231f5055b",
"type": "main",
"index": 0
}
]
]
}
}
}¿Cómo usar este flujo de trabajo?
Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.
¿En qué escenarios es adecuado este flujo de trabajo?
Avanzado - Ingeniería, Producto
¿Es de pago?
Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.
Flujos de trabajo relacionados recomendados
David Roberts
@davidn8nCompartir este flujo de trabajo