AI와 NextCloud/Google/Zoho를 사용하여 이벤트 텍스트를 캘린더 항목으로 변환
고급
이것은Content Creation, Multimodal AI분야의자동화 워크플로우로, 19개의 노드를 포함합니다.주로 If, Switch, Webhook, HttpRequest, GoogleCalendar 등의 노드를 사용하며. AI와 NextCloud/Google/Zoho를 통해 이벤트 텍스트를 캘린더 항목으로 변환
사전 요구사항
- •HTTP Webhook 엔드포인트(n8n이 자동으로 생성)
- •대상 API의 인증 정보가 필요할 수 있음
- •OpenAI API Key
사용된 노드 (19)
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
"meta": {
"instanceId": "90f9a6ef38ec632934192a5de51518245cd649d4287258dedc9971969910cdb7"
},
"nodes": [
{
"id": "758f90ce-d94a-4c62-a793-7ddd088178f9",
"name": "to_UTC",
"type": "@n8n/n8n-nodes-langchain.toolCode",
"position": [
1120,
560
],
"parameters": {
"name": "to_UTC",
"jsCode": "function toICSDate(query) {\n let d;\n\n if (query instanceof Date) {\n d = query;\n\n } else if (typeof query === 'string') {\n if (/^\\d{8}T\\d{6}Z$/.test(query)) {\n // Already ICS UTC format\n return query;\n } else if (/^\\d{8}T\\d{6}$/.test(query)) {\n // Compact local format, parse manually\n const [datePart, timePart] = query.split('T');\n const year = +datePart.slice(0, 4);\n const month = +datePart.slice(4, 6) - 1; // 0‑based\n const day = +datePart.slice(6, 8);\n const hour = +timePart.slice(0, 2);\n const minute = +timePart.slice(2, 4);\n const second = +timePart.slice(4, 6);\n d = new Date(year, month, day, hour, minute, second);\n } else {\n // Assume ISO‑8601 string (\"2025-09-26T11:00:00-04:00\")\n d = new Date(query);\n }\n }\n\n if (!(d instanceof Date) || isNaN(d)) {\n throw new Error('Invalid input: must be Date, ISO, ICS UTC, or compact string');\n }\n\n // Always format UTC\n return d.toISOString()\n .replace(/[-:]/g, '') // strip separators\n .replace(/\\.\\d{3}Z$/, 'Z'); // remove ms\n}\n\n// Export\nreturn toICSDate(query);",
"description": "=Call this tool and provide a datetime object in *{{ $now.zone.zoneName }}* timezone and this will give you the UTC time, in string format \"yyyyMMdd'T'HHmmss'Z'\"."
},
"typeVersion": 1.1
},
{
"id": "6917181e-dfdc-4cec-b621-8ce68c774231",
"name": "Brain",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
1020,
560
],
"parameters": {
"options": {}
},
"typeVersion": 1
},
{
"id": "bbcd8973-12d9-4d85-b6f6-4cec224ad447",
"name": "메모",
"type": "n8n-nodes-base.stickyNote",
"position": [
480,
240
],
"parameters": {
"color": 4,
"height": 663.3731744745251,
"content": "## START\nSend a text block of event data (like an from an email body or text extracted from an image) to property **`eventInfo`**."
},
"typeVersion": 1
},
{
"id": "8b7d5184-0f36-4ae8-87d1-bbaf4b8133f0",
"name": "메모1",
"type": "n8n-nodes-base.stickyNote",
"position": [
740,
240
],
"parameters": {
"color": 7,
"height": 663.3731744745248,
"content": "## EXPANSION\n(Optional) If you wanted this workflow to receive an image, and send that directly to the language model to parse for event data, you can use this switch node to send the binary image down a different pathway.\n\nThat's up to you to build ;-)"
},
"typeVersion": 1
},
{
"id": "2c338370-8748-4c8f-8bdd-29f8b25f86fb",
"name": "스위치",
"type": "n8n-nodes-base.switch",
"disabled": true,
"position": [
780,
720
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "Text",
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "19b65514-2493-4128-8e9c-c1dd40454ac6",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.body.eventInfo }}",
"rightValue": ""
}
]
},
"renameOutput": true
},
{
"outputKey": "Image",
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "object",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $binary }}",
"rightValue": ""
}
]
},
"renameOutput": true
}
]
},
"options": {
"allMatchingOutputs": false
}
},
"typeVersion": 3.1
},
{
"id": "2f211450-6b40-4bac-8a1b-00592164d11d",
"name": "메모2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1000,
240
],
"parameters": {
"width": 358.7342715692612,
"height": 663.3731744745251,
"content": "## PARSE EVENT DETAILS\nThis agent takes in a block of unformatted text that contains info about an event (eg. time, place, event name...) and parses it to a structured output."
},
"typeVersion": 1
},
{
"id": "276e4820-bbab-48a2-9d9d-dd4275924052",
"name": "메모3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1380,
240
],
"parameters": {
"color": 5,
"height": 663.3731744745251,
"content": "## CREATE EVENT\nSends a request to `NextCloud` CalDAV to create an event in your calendar.\n\nYou'll need to add `Basic Auth` credentials (user/pass) from your NextCloud account.\n\nCreate an app-specific password here: [your.NC.url/index.php/settings/user/security]"
},
"typeVersion": 1
},
{
"id": "93d6676b-f337-4052-927a-d4a86e9d589d",
"name": "메모4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1640,
240
],
"parameters": {
"color": 3,
"height": 663.3731744745251,
"content": "## RESPOND\nSend your success response back to the source system. You can edit this to return a JSON object instead if required.\n\n**PS** - This workflow plugs in seamlessly with [this iCloud Shortcut](https://www.icloud.com/shortcuts/8a107ea08ec4471d877b019520a4802c)"
},
"typeVersion": 1
},
{
"id": "6b37e5dd-e26a-4920-a929-9a9749ad8cc1",
"name": "메모5",
"type": "n8n-nodes-base.stickyNote",
"position": [
480,
920
],
"parameters": {
"color": 7,
"width": 1400.3576539723263,
"height": 258.08325199688073,
"content": "## SUGGESTIONS\nIn production setting, you'll want to account for errors and bad input. What if the user just puts in random text with no event details? The Ai should be instructed to then output a specific fail response that goes back to the source system to tell it the input was no good.\n\nIf your NextCloud is self-hosted, and there's a chance it could be down, you may want to catch an error there as well, and announce that back to the source system, via webhook response.\n\nI use a version of this workflow with an iOS shortcut for my iPhone. I tell Siri \"Add this to my calendar\" and it opens the camera, I snap, and it sends the text in the photo to this workflow. Very easy to use. *If you have an iPhone*, here's the Shortcut for you to do the same:\n\n#### ⭐ https://www.icloud.com/shortcuts/8a107ea08ec4471d877b019520a4802c"
},
"typeVersion": 1
},
{
"id": "4e383a02-ece9-4c0f-a8a6-4090885e6c40",
"name": "Create Zoho Event (API)",
"type": "n8n-nodes-base.httpRequest",
"notes": "Documentation:\nhttps://www.zoho.com/calendar/help/api/post-create-event.html",
"disabled": true,
"position": [
1420,
720
],
"parameters": {
"url": "https://calendar.zoho.com/api/v1/calendars/INSERT_CAL_UID_HERE/events",
"method": "POST",
"options": {},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api",
"queryParameters": {
"parameters": [
{
"name": "eventdata",
"value": "={\n \"dateandtime\": {\n \"timezone\": {{ JSON.stringify($now.zone.zoneName) }},\n \"start\": {{ JSON.stringify($json.output.startTime) }},\n \"end\": {{ JSON.stringify($json.output.endTime) }}\n },\n \"title\": {{ JSON.stringify($json.output.eventTitle) }},\n \"location\": {{ JSON.stringify($json.output.location != null ? $json.output.location : \"\") }},\n \"url\": {{ JSON.stringify($json.output.url != null ? $json.output.url : \"\") }},\n \"transparency\": 1,\n \"attendees\": [\n {\n \"email\": \"INSERT_EMAIL_HERE\"\n }\n ],\n \"description\": {{ JSON.stringify($json.output.description != null ? $json.output.description : \"\") }}\n}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "06883e54-7548-448d-92d8-6d7079bdbf43",
"name": "Structured Output",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
1220,
560
],
"parameters": {
"jsonSchemaExample": "{\n \"eventTitle\": \"Marketing Conference\",\n \"description\": null,\n \"startTime\": \"20250912T160000Z\",\n \"endTime\": \"20250912T200000Z\",\n \"location\": \"15000 Park Ave, Suite 1234, New York, New York, United States\",\n \"url\": null\n}"
},
"typeVersion": 1.2
},
{
"id": "1102b5a1-932b-4ab9-a2d3-17950c4b8b99",
"name": "Success Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1700,
540
],
"parameters": {
"options": {
"responseCode": 201
},
"respondWith": "text",
"responseBody": "=Your calendar event was successfully created."
},
"typeVersion": 1.1
},
{
"id": "c162405c-ed1a-4177-8f9f-5110ffbeb426",
"name": "Fail Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1700,
740
],
"parameters": {
"options": {
"responseCode": 400
},
"respondWith": "text",
"responseBody": "=There was a problem with the event info. Try again."
},
"typeVersion": 1.1
},
{
"id": "7f11b9f4-526e-45fd-8e89-e89c8b9e2db4",
"name": "NextCloud Cal Event Creation",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"position": [
1440,
540
],
"parameters": {
"url": "https://your.nextcloudurl.com/remote.php/dav/calendars/YOUR_USER/personal/newEvent.ics",
"body": "=BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//n8n//CalDAV Connector//EN\nBEGIN:VEVENT\nUID:123456789@example.com\nDTSTAMP:{{ $now.setZone($now.zone.zoneName).toUTC().toFormat(\"yyyyMMdd'T'HHmmss'Z'\") }}\nDTSTART:{{ $json.output.startTime }}\nDTEND:{{ $json.output.endTime }}\nSUMMARY:{{ $json.output.eventTitle }}\nDESCRIPTION:{{ $json.output.description }}\nLOCATION:{{ $json.output.location }}\nURL:{{ $json.output.url }}\nEND:VEVENT\nEND:VCALENDAR",
"method": "PUT",
"options": {},
"sendBody": true,
"contentType": "raw",
"rawContentType": "text/calendar; charset=utf-8"
},
"typeVersion": 4.2
},
{
"id": "9a789e02-203a-40b8-a834-aa6fa6881ce7",
"name": "Good Parse",
"type": "n8n-nodes-base.if",
"position": [
1040,
760
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "3bd94ff5-bebb-43a4-ae06-7790de6df7a6",
"operator": {
"type": "string",
"operation": "notExists",
"singleValue": true
},
"leftValue": "={{ $json.output.error }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.1
},
{
"id": "c6bb115d-bf79-457d-a8db-fb4a8ef1bcf5",
"name": "메모6",
"type": "n8n-nodes-base.stickyNote",
"position": [
480,
-260
],
"parameters": {
"color": 5,
"width": 435.7280989463619,
"height": 478.57603298212047,
"content": "## Eric Knaus www.MarketingGuy.ai\nFind me on LinkedIn: https://linkedin.com/in/ericknaus\n\n\n\n## TTC - Text-to-Calendar event"
},
"typeVersion": 1
},
{
"id": "f10d8f8d-88b0-49be-8263-14c7a343bc1d",
"name": "Google 캘린더",
"type": "n8n-nodes-base.googleCalendar",
"disabled": true,
"position": [
1480,
740
],
"parameters": {
"end": "={{ $json.output.endTime }}",
"start": "={{ $json.output.startTime }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": ""
},
"additionalFields": {}
},
"typeVersion": 1.1
},
{
"id": "b4be10a7-0306-425e-9e24-f449dc198ae0",
"name": "Inbound Event Info",
"type": "n8n-nodes-base.webhook",
"position": [
540,
540
],
"webhookId": "2ee09e2c-6ee0-4e12-a941-a40a63442bb1",
"parameters": {
"path": "make-cal-event-xdt8gh4-rf3827",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "80fc0363-4991-4eb9-a019-17b8597e9bfe",
"name": "Parse Event Info",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1040,
380
],
"parameters": {
"text": "=Local time context: {{ $now.toString() }} \nServer timezone: {{ $now.zone.zoneName }} \n\nInstructions:\n1. Extract calendar event details from the text below. \n2. For event times, pass each local datetime through `to_UTC(Datetime)` to get UTC output values, in \"YYYYMMDDTHHmmssZ\" format. \n4. Apply special cases:\n - Case 1: If text has a date and at least one person/place but no clear details → infer event. If no time, use 08:00–22:00 local. \n - Case 2: If there is no date and no person/place/event → output exactly: {\"error\": \"BAD INPUT\"} \n\nText to parse:\n{{ JSON.stringify($json.body.eventInfo) }}",
"options": {
"systemMessage": "You are an assistant that extracts calendar event information from text.\n\nAlways respond ONLY in JSON.\n\nNormal Output Schema (when event is valid or inferable):\n{\n \"eventTitle\": \"string\",\n \"description\": \"string or null\",\n \"startTime\": \"YYYYMMDDTHHmmssZ\", // UTC, ISO8601\n \"endTime\": \"YYYYMMDDTHHmmssZ\", // UTC, ISO8601\n \"location\": \"string or null\",\n \"url\": \"string or null\"\n}\n\nSpecial Rules:\n1. If text includes at least one date and a name or place but lacks other details → infer a plausible event. \n - If no time is provided → assume 08:00 for start time and 22:00 for end time, local time. Convert using `to_UTC`. \n2. If text has no date and no usable event/person/place detail → output:\n {\n \"error\": \"BAD INPUT\"\n }\n\nFormatting Rules:\n- Output strictly valid JSON only.\n- Use null values for missing optional fields unless you are inferring details under rule (1).\n- Use the `to_UTC(Datetime)` tool to convert each local datetime to UTC."
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.6
}
],
"pinData": {},
"connections": {
"6917181e-dfdc-4cec-b621-8ce68c774231": {
"ai_languageModel": [
[
{
"node": "80fc0363-4991-4eb9-a019-17b8597e9bfe",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"758f90ce-d94a-4c62-a793-7ddd088178f9": {
"ai_tool": [
[
{
"node": "80fc0363-4991-4eb9-a019-17b8597e9bfe",
"type": "ai_tool",
"index": 0
}
]
]
},
"9a789e02-203a-40b8-a834-aa6fa6881ce7": {
"main": [
[
{
"node": "7f11b9f4-526e-45fd-8e89-e89c8b9e2db4",
"type": "main",
"index": 0
}
],
[
{
"node": "c162405c-ed1a-4177-8f9f-5110ffbeb426",
"type": "main",
"index": 0
}
]
]
},
"80fc0363-4991-4eb9-a019-17b8597e9bfe": {
"main": [
[
{
"node": "9a789e02-203a-40b8-a834-aa6fa6881ce7",
"type": "main",
"index": 0
}
]
]
},
"06883e54-7548-448d-92d8-6d7079bdbf43": {
"ai_outputParser": [
[
{
"node": "80fc0363-4991-4eb9-a019-17b8597e9bfe",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"b4be10a7-0306-425e-9e24-f449dc198ae0": {
"main": [
[
{
"node": "80fc0363-4991-4eb9-a019-17b8597e9bfe",
"type": "main",
"index": 0
}
]
]
},
"7f11b9f4-526e-45fd-8e89-e89c8b9e2db4": {
"main": [
[
{
"node": "1102b5a1-932b-4ab9-a2d3-17950c4b8b99",
"type": "main",
"index": 0
}
],
[
{
"node": "c162405c-ed1a-4177-8f9f-5110ffbeb426",
"type": "main",
"index": 0
}
]
]
}
}
}자주 묻는 질문
이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
고급 - 콘텐츠 제작, 멀티모달 AI
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
OpenAI, LangChain, API 통합을 사용한 작업 자동화 초보자 가이드
OpenAI, LangChain 및 API 통합을 사용한 작업 자동화 시작자 가이드
If
Set
Code
+
If
Set
Code
33 노드Meelioo
콘텐츠 제작
GPT-4o, Fal.ai 및 인공 감독을 사용하여 제품 AI 광고 영상 생성
GPT-4o, Fal.ai 및 인공지능 감독을 사용하여 제품 AI 광고 비디오 생성
If
Set
Code
+
If
Set
Code
72 노드gotoHuman
콘텐츠 제작
GPT-4와 DALL-E를 사용한 UI 기반 자동화 LinkedIn 콘텐츠 생성
AI 기반 LinkedIn 콘텐츠 생성기(OpenAI GPT-4 및 DALL-E)
Webhook
Http Request
Agent
+
Webhook
Http Request
Agent
23 노드WeWeb
콘텐츠 제작
트렌드 스프레드시트에서 SEO 콘텐츠를 스토리지(SharePoint/Drive/Dropbox)로 생성
GPT-4o, FAL AI 및 다중 저장소 지원을 사용하여 트렌드에서 SEO 콘텐츠 자동 생성
If
Set
Code
+
If
Set
Code
47 노드plemeo
콘텐츠 제작
✅ 바이럴 릴스 팩토리
Veo, Shotstack 및 Postiz를 사용한 ASMR 유리 과일 비디오 제작 및 게시 자동화
If
Jwt
Set
+
If
Jwt
Set
37 노드Ayoub Boutouil
콘텐츠 제작
OpenAI, RunwayML, ElevenLabs를 사용한 무면식 숏폼 비디오 자동화
OpenAI, RunwayML, ElevenLabs를 사용한 무면쇼트 비디오 자동화: 스크립트부터 소셜 미디어까지
Set
Code
Wait
+
Set
Code
Wait
56 노드LeeWei
콘텐츠 제작
워크플로우 정보
난이도
고급
노드 수19
카테고리2
노드 유형11
저자
Automation Wizard bzzt Years of experience as a digital marketer. Honed skill in JS, web dev, email mktg. All about the value to the client. Value, baby, it's everything.
외부 링크
n8n.io에서 보기 →
이 워크플로우 공유