ChatGPT를 사용한 의심스러운 이메일 내용 분석 및 분류
이것은AI, SecOps분야의자동화 워크플로우로, 25개의 노드를 포함합니다.주로 If, Set, Code, Jira, HttpRequest 등의 노드를 사용하며인공지능 기술을 결합하여 스마트 자동화를 구현합니다. ChatGPT를 사용하여 의심스러운 이메일 콘텐츠 분석 및 분류
- •대상 API의 인증 정보가 필요할 수 있음
- •Google 계정 및 Gmail API 인증 정보
- •OpenAI API Key
{
"meta": {
"instanceId": "03e9d14e9196363fe7191ce21dc0bb17387a6e755dcc9acc4f5904752919dca8"
},
"nodes": [
{
"id": "94dd7f48-0013-4fb5-89c4-826ecd7f2d66",
"name": "Gmail 트리거",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
1460,
120
],
"parameters": {
"simple": false,
"filters": {},
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
}
},
"credentials": {
"gmailOAuth2": {
"id": "kkhNhqKpZt6IUZd0",
"name": "Gmail"
}
},
"typeVersion": 1.2
},
{
"id": "ca2023fa-ceca-4923-80e4-a3843803536c",
"name": "Microsoft Outlook 트리거",
"type": "n8n-nodes-base.microsoftOutlookTrigger",
"disabled": true,
"position": [
1480,
680
],
"parameters": {
"fields": [
"body",
"toRecipients",
"subject",
"bodyPreview"
],
"output": "fields",
"filters": {},
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
}
},
"credentials": {
"microsoftOutlookOAuth2Api": {
"id": "vTCK0oVQ0WjFrI5H",
"name": " Outlook Credential"
}
},
"typeVersion": 1
},
{
"id": "1f011214-91a0-4cfa-9d9e-29864937c0a3",
"name": "HTML 스크린샷",
"type": "n8n-nodes-base.httpRequest",
"position": [
2620,
420
],
"parameters": {
"url": "https://hcti.io/v1/image",
"method": "POST",
"options": {},
"sendBody": true,
"sendQuery": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "html",
"value": "={{ $('Set Email Variables').item.json.htmlBody }}"
}
]
},
"genericAuthType": "httpBasicAuth",
"queryParameters": {
"parameters": [
{}
]
}
},
"credentials": {
"httpBasicAuth": {
"id": "8tm8mUWmPvtmPFPk",
"name": "hcti.io"
}
},
"typeVersion": 4.2
},
{
"id": "64f4789f-9de8-414f-af62-ddc339f0d0ac",
"name": "스크린샷 검색",
"type": "n8n-nodes-base.httpRequest",
"position": [
2800,
420
],
"parameters": {
"url": "={{ $json.url }}",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth"
},
"credentials": {
"httpBasicAuth": {
"id": "8tm8mUWmPvtmPFPk",
"name": "hcti.io"
}
},
"typeVersion": 4.2
},
{
"id": "db707bd9-6abc-4ab7-8ffa-ad25c5e8adc4",
"name": "Outlook 변수 설정",
"type": "n8n-nodes-base.set",
"position": [
2040,
680
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "38bd3db2-1a8d-4c40-a2dd-336e0cc84224",
"name": "htmlBody",
"type": "string",
"value": "={{ $('Microsoft Outlook Trigger').item.json.body.content }}"
},
{
"id": "13bdd95b-ef02-486e-b38b-d14bd05a4a8a",
"name": "headers",
"type": "string",
"value": "={{ $json}}"
},
{
"id": "20566ad4-7eb7-42b1-8a0d-f8b759610f10",
"name": "subject",
"type": "string",
"value": "={{ $('Microsoft Outlook Trigger').item.json.subject }}"
},
{
"id": "7171998f-a5a2-4e23-946a-9c1ad75710e7",
"name": "recipient",
"type": "string",
"value": "={{ $('Microsoft Outlook Trigger').item.json.toRecipients[0].emailAddress.address }}"
},
{
"id": "cc262634-2470-4524-8319-abe2518a6335",
"name": "textBody",
"type": "string",
"value": "={{ $('Retrieve Headers of Email').item.json.body.content }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "7a3622c0-6949-4ea3-ae13-46a1ee26de7b",
"name": "Gmail 변수 설정",
"type": "n8n-nodes-base.set",
"position": [
2020,
120
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "38bd3db2-1a8d-4c40-a2dd-336e0cc84224",
"name": "htmlBody",
"type": "string",
"value": "={{ $json.html }}"
},
{
"id": "18fbcf78-6d3c-4036-b3a2-fb5adf22176a",
"name": "headers",
"type": "string",
"value": "={{ $json.headers }}"
},
{
"id": "1d690098-be2a-4604-baf8-62f314930929",
"name": "subject",
"type": "string",
"value": "={{ $json.subject }}"
},
{
"id": "8009f00a-547f-4eb1-b52d-2e7305248885",
"name": "recipient",
"type": "string",
"value": "={{ $json.to.text }}"
},
{
"id": "1932e97d-b03b-4964-b8bc-8262aaaa1f7a",
"name": "textBody",
"type": "string",
"value": "={{ $json.text }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "4b4c6b34-f74c-4402-91a1-4d002e02a3bd",
"name": "이메일 헤더 검색",
"type": "n8n-nodes-base.httpRequest",
"position": [
1700,
680
],
"parameters": {
"url": "=https://graph.microsoft.com/v1.0/me/messages/{{ $json.id }}?$select=internetMessageHeaders,body",
"options": {},
"sendHeaders": true,
"authentication": "predefinedCredentialType",
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/json"
},
{
"name": "Prefer",
"value": "outlook.body-content-type=\"text\""
}
]
},
"nodeCredentialType": "microsoftOutlookOAuth2Api"
},
"credentials": {
"microsoftOutlookOAuth2Api": {
"id": "vTCK0oVQ0WjFrI5H",
"name": " Outlook Credential"
}
},
"typeVersion": 4.2
},
{
"id": "0c9883b5-3eb7-45db-9803-d1b30166a3b5",
"name": "헤더 형식 지정",
"type": "n8n-nodes-base.code",
"position": [
1880,
680
],
"parameters": {
"jsCode": "const input = $('Retrieve Headers of Email').item.json.internetMessageHeaders;\n\nconst result = input.reduce((acc, { name, value }) => {\n if (!acc[name]) acc[name] = [];\n acc[name].push(value);\n return acc;\n}, {});\n\nreturn result;"
},
"typeVersion": 2
},
{
"id": "c21a976c-00e5-4823-bd94-4c95a7d60438",
"name": "ChatGPT로 이메일 분석",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
3000,
420
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4o",
"cachedResultName": "GPT-4O"
},
"options": {},
"messages": {
"values": [
{
"content": "=Describe the following email using the HTML body and headers. Determine if the email could be a phishing email. \n\nHere is the HTML body:\n{{ $('Set Email Variables').item.json.htmlBody }}\n\nThe message headers are as follows:\n{{ $('Set Email Variables').item.json.headers }}\n\n"
},
{
"role": "system",
"content": "Please make sure to output all responses using the following structured JSON output:\n{\n \"malicious\": false,\n \"summary\": \"The email appears to be a legitimate communication from a known sender. It contains no suspicious links, attachments, or language that indicates phishing or malicious intent.\"\n}\n\nFormat the response for Jira who uses a wiki-style renderer. Do not include ``` around your response. Make the summary as verbose as possible including a full breakdown of why the email is benign or malicious."
}
]
},
"jsonOutput": true
},
"credentials": {
"openAiApi": {
"id": "76",
"name": "OpenAi account"
}
},
"typeVersion": 1.6
},
{
"id": "a91f4095-9245-4276-b21f-f415de22df62",
"name": "잠재적 악성 티켓 생성",
"type": "n8n-nodes-base.jira",
"position": [
3640,
400
],
"parameters": {
"project": {
"__rl": true,
"mode": "list",
"value": "10001",
"cachedResultName": "Support"
},
"summary": "=Potentially Malicious - Phishing Email Reported: \"{{ $('Set Email Variables').item.json.subject }}\"",
"issueType": {
"__rl": true,
"mode": "list",
"value": "10008",
"cachedResultName": "Task"
},
"additionalFields": {
"description": "=A phishing email was reported by {{ $('Set Email Variables').item.json.recipient }} with the subject line \"{{ $('Set Email Variables').item.json.subject }}\"\n\\\\\nh2. Here is ChatGPT's analysis of the email:\n{{ $json.message.content.summary }}"
}
},
"credentials": {
"jiraSoftwareCloudApi": {
"id": "BZmmGUrNIsgM9fDj",
"name": "New Jira Cloud"
}
},
"typeVersion": 1
},
{
"id": "a5a66a0e-9d8a-45a9-b1ae-aec78ddfec27",
"name": "잠재적 양호 티켓 생성",
"type": "n8n-nodes-base.jira",
"position": [
3640,
580
],
"parameters": {
"project": {
"__rl": true,
"mode": "list",
"value": "10001",
"cachedResultName": "Support"
},
"summary": "=Potentially Benign - Phishing Email Reported: \"{{ $('Set Email Variables').item.json.subject }}\"",
"issueType": {
"__rl": true,
"mode": "list",
"value": "10008",
"cachedResultName": "Task"
},
"additionalFields": {
"description": "=A phishing email was reported by {{ $('Set Email Variables').item.json.recipient }} with the subject line \"{{ $('Set Email Variables').item.json.subject }}\"\n\\\\\nh2. Here is ChatGPT's analysis of the email:\n{{ $json.message.content.summary }}"
}
},
"credentials": {
"jiraSoftwareCloudApi": {
"id": "BZmmGUrNIsgM9fDj",
"name": "New Jira Cloud"
}
},
"typeVersion": 1
},
{
"id": "5af0d60b-d021-4dd9-98f7-b2842800764a",
"name": "스크린샷 이름 변경",
"type": "n8n-nodes-base.code",
"position": [
4020,
480
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "$('Retrieve Screenshot').item.binary.data.fileName = 'emailScreenshot.png'\n\nreturn $('Retrieve Screenshot').item;"
},
"typeVersion": 2
},
{
"id": "441c4cbb-bd93-4213-bd34-e18f2a49389f",
"name": "Jira ID 설정",
"type": "n8n-nodes-base.set",
"position": [
3860,
480
],
"parameters": {
"options": {},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "4c71188c-011d-4f8e-a36c-87900bfab59a",
"name": "이메일 스크린샷을 Jira에 업로드",
"type": "n8n-nodes-base.jira",
"position": [
4220,
480
],
"parameters": {
"issueKey": "={{ $('Set Jira ID').item.json.key }}",
"resource": "issueAttachment"
},
"credentials": {
"jiraSoftwareCloudApi": {
"id": "BZmmGUrNIsgM9fDj",
"name": "New Jira Cloud"
}
},
"typeVersion": 1
},
{
"id": "3c031c34-8306-44e1-8e0e-a584c5323112",
"name": "이메일 본문을 Jira에 업로드",
"type": "n8n-nodes-base.jira",
"position": [
4620,
480
],
"parameters": {
"issueKey": "={{ $('Set Jira ID').item.json.key }}",
"resource": "issueAttachment"
},
"credentials": {
"jiraSoftwareCloudApi": {
"id": "BZmmGUrNIsgM9fDj",
"name": "New Jira Cloud"
}
},
"typeVersion": 1
},
{
"id": "d033dcbd-7ccb-451f-ab81-cc6d32d2e01f",
"name": "이메일 본문을 파일로 변환",
"type": "n8n-nodes-base.convertToFile",
"position": [
2420,
420
],
"parameters": {
"options": {
"fileName": "emailBody.txt"
},
"operation": "toText",
"sourceProperty": "textBody"
},
"typeVersion": 1.1
},
{
"id": "bda5e2fe-d8c0-456b-975a-35e82ff02816",
"name": "이메일 변수 설정",
"type": "n8n-nodes-base.set",
"position": [
2240,
420
],
"parameters": {
"options": {},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "54ecd8ab-ac4a-4b6b-bd1b-bf8c70082a33",
"name": "이메일 본문 스크린샷 이름 변경",
"type": "n8n-nodes-base.code",
"position": [
4420,
480
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "$('Convert Email Body to File').item.binary.data.fileName = 'emailBody.txt'\n\nreturn $('Convert Email Body to File').item;"
},
"typeVersion": 2
},
{
"id": "fe5b82cc-b4bb-4c97-9477-075d5a280e9f",
"name": "스티커 노트2",
"type": "n8n-nodes-base.stickyNote",
"position": [
2574.536755825029,
0
],
"parameters": {
"color": 7,
"width": 376.8280004374956,
"height": 595.590013880477,
"content": "\n## Email Body Screenshot Creation\n\nThe **Screenshot HTML** node sends the email's HTML body to the **hcti.io** API, generating a screenshot that visually represents the email's layout. The **Retrieve Screenshot** node then fetches this image, making it available for attachment or review in subsequent steps. This dual-format processing ensures both clarity and flexibility in email analysis workflows."
},
"typeVersion": 1
},
{
"id": "86b21049-f65e-4c6a-a854-c4376f870da9",
"name": "스티커 노트",
"type": "n8n-nodes-base.stickyNote",
"position": [
1380,
-149.99110983560342
],
"parameters": {
"color": 7,
"width": 814.4556539379754,
"height": 444.5525554815556,
"content": "\n## Gmail Integration and Data Extraction\n\nThis section of the workflow connects to a Gmail account using the **Gmail Trigger** node, capturing incoming emails in real-time, with checks performed every minute. Once an email is detected, its key components—such as the subject, recipient, body, and headers—are extracted and assigned to variables using the **Set Gmail Variables** node. These variables are structured for subsequent analysis and processing in later steps."
},
"typeVersion": 1
},
{
"id": "b1a786cf-7a8d-49e1-90ed-31f3d0e65b13",
"name": "스티커 노트1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1380,
308
],
"parameters": {
"color": 7,
"width": 809.7918597571277,
"height": 602.9002284617277,
"content": "\n## Microsoft Outlook Integration and Email Header Processing\n\nThis section enables the integration of Microsoft Outlook to monitor and capture incoming emails. The Microsoft Outlook Trigger node checks for new messages every minute. Once an email is detected, the Retrieve Headers of Email node fetches detailed header and body content via the Microsoft Graph API. The Format Headers node organizes the email headers into a structured format using a JavaScript function, ensuring clarity and readiness for further processing. Finally, the Set Outlook Variables node extracts and assigns key details—such as the email subject, recipient, body, and formatted headers—to variables for use in subsequent workflow steps. This section is essential for processing Outlook emails and preparing them for analysis and reporting.\n\n\n\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "e7ace035-b5f5-4ef3-a117-22c7c938868d",
"name": "스티커 노트3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2958.4325220284563,
24.744924120002338
],
"parameters": {
"color": 7,
"width": 593.0990401534098,
"height": 573.1750519720028,
"content": "\n## AI-Powered Email Analysis and Threat Detection\n\nThis section leverages ChatGPT for advanced email content and header analysis to determine potential phishing threats. The **Analyze Email with ChatGPT** node processes the email's HTML body and headers, generating a detailed JSON response that categorizes the email as malicious or benign. The response includes a verbose explanation, formatted for Jira, outlining the reasons for the classification. The **Check if Malicious** node evaluates the AI output to determine the next steps based on the email's threat status. If flagged as malicious, subsequent actions like reporting and ticket creation are triggered. This section ensures precise, AI-driven analysis to enhance email security workflows."
},
"typeVersion": 1
},
{
"id": "02c1ad8e-f952-42d2-ae9f-cf3a77e49e52",
"name": "스티커 노트4",
"type": "n8n-nodes-base.stickyNote",
"position": [
3562.4948140707697,
-125.79607719303533
],
"parameters": {
"color": 7,
"width": 1251.7025543502837,
"height": 891.579206098173,
"content": "\n## Automated Jira Ticket Creation and Email Attachment\n\nThis section streamlines the process of logging phishing email reports in Jira, complete with detailed analysis and attachments. The workflow creates two distinct Jira tickets depending on the AI classification of the email:\n\n1. **Potentially Malicious**: The **Create Potentially Malicious Ticket** node generates a ticket if the email is flagged as a phishing attempt, including a summary of ChatGPT's analysis and the email’s details.\n2. **Potentially Benign**: If the email is classified as safe, the **Create Potentially Benign Ticket** node logs a ticket with similar details but under a non-malicious category.\n\n\nThe **Set Jira ID** node ensures the generated ticket's ID is tracked for subsequent operations. Attachments are handled efficiently:\n\n- **Rename Screenshot** prepares the email screenshot for upload.\n- **Upload Screenshot of Email to Jira** adds the screenshot to the Jira ticket for visual context.\n- **Rename Email Body Screenshot** and **Upload Email Body to Jira** manage the attachment of the email's text body as a `.txt` file.\n\n\nThis section enhances reporting by automating ticket creation, ensuring all relevant email data is readily available for review by security teams."
},
"typeVersion": 1
},
{
"id": "597ef23e-c61c-4e27-8c14-74ec20079c96",
"name": "악성 여부 확인",
"type": "n8n-nodes-base.if",
"position": [
3400,
420
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "493f412c-5f11-4173-8940-90f5bc7f5fab",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.message.content.malicious }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "af512af9-924b-4019-bdf9-62aac9cd0dac",
"name": "스티커 노트5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2200,
39.041733604283195
],
"parameters": {
"color": 7,
"width": 365.6458805720866,
"height": 559.8072303111675,
"content": "\n## Email Body Conversion\n\nThis section processes the email body into both text and visual formats for detailed analysis and reporting. The **Set Email Variables** node organizes the email's data, including its HTML body and text content, to prepare it for further steps. The **Convert Email Body to File** node creates a `.txt` file containing the plain text version of the email body, useful for documentation or further analysis."
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"441c4cbb-bd93-4213-bd34-e18f2a49389f": {
"main": [
[
{
"node": "5af0d60b-d021-4dd9-98f7-b2842800764a",
"type": "main",
"index": 0
}
]
]
},
"94dd7f48-0013-4fb5-89c4-826ecd7f2d66": {
"main": [
[
{
"node": "7a3622c0-6949-4ea3-ae13-46a1ee26de7b",
"type": "main",
"index": 0
}
]
]
},
"0c9883b5-3eb7-45db-9803-d1b30166a3b5": {
"main": [
[
{
"node": "db707bd9-6abc-4ab7-8ffa-ad25c5e8adc4",
"type": "main",
"index": 0
}
]
]
},
"1f011214-91a0-4cfa-9d9e-29864937c0a3": {
"main": [
[
{
"node": "64f4789f-9de8-414f-af62-ddc339f0d0ac",
"type": "main",
"index": 0
}
]
]
},
"5af0d60b-d021-4dd9-98f7-b2842800764a": {
"main": [
[
{
"node": "4c71188c-011d-4f8e-a36c-87900bfab59a",
"type": "main",
"index": 0
}
]
]
},
"597ef23e-c61c-4e27-8c14-74ec20079c96": {
"main": [
[
{
"node": "a91f4095-9245-4276-b21f-f415de22df62",
"type": "main",
"index": 0
}
],
[
{
"node": "a5a66a0e-9d8a-45a9-b1ae-aec78ddfec27",
"type": "main",
"index": 0
}
]
]
},
"64f4789f-9de8-414f-af62-ddc339f0d0ac": {
"main": [
[
{
"node": "c21a976c-00e5-4823-bd94-4c95a7d60438",
"type": "main",
"index": 0
}
]
]
},
"bda5e2fe-d8c0-456b-975a-35e82ff02816": {
"main": [
[
{
"node": "d033dcbd-7ccb-451f-ab81-cc6d32d2e01f",
"type": "main",
"index": 0
}
]
]
},
"7a3622c0-6949-4ea3-ae13-46a1ee26de7b": {
"main": [
[
{
"node": "bda5e2fe-d8c0-456b-975a-35e82ff02816",
"type": "main",
"index": 0
}
]
]
},
"db707bd9-6abc-4ab7-8ffa-ad25c5e8adc4": {
"main": [
[
{
"node": "bda5e2fe-d8c0-456b-975a-35e82ff02816",
"type": "main",
"index": 0
}
]
]
},
"ca2023fa-ceca-4923-80e4-a3843803536c": {
"main": [
[
{
"node": "4b4c6b34-f74c-4402-91a1-4d002e02a3bd",
"type": "main",
"index": 0
}
]
]
},
"4b4c6b34-f74c-4402-91a1-4d002e02a3bd": {
"main": [
[
{
"node": "0c9883b5-3eb7-45db-9803-d1b30166a3b5",
"type": "main",
"index": 0
}
]
]
},
"c21a976c-00e5-4823-bd94-4c95a7d60438": {
"main": [
[
{
"node": "597ef23e-c61c-4e27-8c14-74ec20079c96",
"type": "main",
"index": 0
}
]
]
},
"d033dcbd-7ccb-451f-ab81-cc6d32d2e01f": {
"main": [
[
{
"node": "1f011214-91a0-4cfa-9d9e-29864937c0a3",
"type": "main",
"index": 0
}
]
]
},
"54ecd8ab-ac4a-4b6b-bd1b-bf8c70082a33": {
"main": [
[
{
"node": "3c031c34-8306-44e1-8e0e-a584c5323112",
"type": "main",
"index": 0
}
]
]
},
"a5a66a0e-9d8a-45a9-b1ae-aec78ddfec27": {
"main": [
[
{
"node": "441c4cbb-bd93-4213-bd34-e18f2a49389f",
"type": "main",
"index": 0
}
]
]
},
"4c71188c-011d-4f8e-a36c-87900bfab59a": {
"main": [
[
{
"node": "54ecd8ab-ac4a-4b6b-bd1b-bf8c70082a33",
"type": "main",
"index": 0
}
]
]
},
"a91f4095-9245-4276-b21f-f415de22df62": {
"main": [
[
{
"node": "441c4cbb-bd93-4213-bd34-e18f2a49389f",
"type": "main",
"index": 0
}
]
]
}
}
}이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
고급 - 인공지능, 보안 운영
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
Angel Menendez
@djangelicAngel Menendez is a Staff Developer Advocate at n8n.io, specializing in low-code tools for cybersecurity workflows. From Puerto Rico, Angel's tech journey began by helping his father translate technical books. He later started a web development business and transitioned from a career as a flight attendant to cybersecurity engineering. His workflows have saved companies significant time. Outside work, Angel enjoys time with his two sons, riding electric bikes, reading, and exploring new places.
이 워크플로우 공유