온보딩 통화 점수 시스템
중급
이것은자동화 워크플로우로, 14개의 노드를 포함합니다.주로 Code, Gmail, Slack, Webhook, GoogleSheets 등의 노드를 사용하며. Fireflies.ai, OpenAI 및 Google Sheets를 사용한 영업 통화 점수 매기기 자동화
사전 요구사항
- •Google 계정 및 Gmail API 인증 정보
- •Slack Bot Token 또는 Webhook URL
- •HTTP Webhook 엔드포인트(n8n이 자동으로 생성)
- •Google Sheets API 인증 정보
- •OpenAI API Key
카테고리
-
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
"id": "zKRrIwXzcv7QvepA",
"meta": {
"instanceId": "6615d3df9d365bf328b0a329fe952ab6b434c9ecc8f5a2517849ec1f68f0d9b0",
"templateCredsSetupCompleted": true
},
"name": "OnBoarding Call Grading System",
"tags": [
{
"id": "OYNCvrDxVQHw2gvR",
"name": "Upwork",
"createdAt": "2025-10-02T10:34:02.340Z",
"updatedAt": "2025-10-02T10:34:02.340Z"
}
],
"nodes": [
{
"id": "a9579f80-9bec-473c-83c4-66b1134194ee",
"name": "웹훅",
"type": "n8n-nodes-base.webhook",
"position": [
-16,
-176
],
"webhookId": "66273cf2-950f-42e5-a386-60d643868985",
"parameters": {
"path": "66273cf2-950f-42e5-a386-60d643868985",
"options": {},
"httpMethod": "POST",
"responseData": "allEntries",
"responseMode": "lastNode"
},
"typeVersion": 2.1
},
{
"id": "d7377f6f-c1cc-47a9-8d1d-99dadd47fb2f",
"name": "시트에 행 추가",
"type": "n8n-nodes-base.googleSheets",
"position": [
1280,
-176
],
"parameters": {
"columns": {
"value": {
"Pain Points": "={{ $json.pain_points }}",
"Overall Score %": "={{ $json.overall_score_percent }}",
"What I Did Well": "={{ $json.what_i_did_well }}",
"What to Improve": "={{ $json.what_to_improve }}",
"Discovery (1–5)": "={{ $json.scores_discovery }}",
"Feature requested": "={{ $json.feature_requested }}",
"Overall Score [25]": "={{ $json.overall_score_25 }}",
"Recording Share Url": "={{ $('Get a transcript').item.json.data.transcript_url }}",
"Meeting Invitees Name": "={{ $('Get a transcript').item.json.data.sentences[15].speaker_name }}",
"Meeting Invitees Email": "nico-wenck@clicklessai.de",
"Close/Next Steps (1–5)": "={{ $json.scores_close_next_steps }}",
"Engagement/Rapport (1–5)": "={{ $json.scores_engagement_rapport }}",
"Objection Handling (1–5)": "={{ $json.scores_objection_handling }}",
"Onboarding Clarity (1–5)": "={{ $json.scores_onboarding_clarity }}",
"Recording Duration In Minutes": "={{ $('Get a transcript').item.json.data.duration }}",
"What CRM have you used in the past?": "={{ $json.crm_used_past }}"
},
"schema": [
{
"id": "Meeting Invitees Name",
"type": "string",
"display": true,
"required": false,
"displayName": "Meeting Invitees Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Meeting Invitees Email",
"type": "string",
"display": true,
"required": false,
"displayName": "Meeting Invitees Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Recording Share Url",
"type": "string",
"display": true,
"required": false,
"displayName": "Recording Share Url",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Recording Duration In Minutes",
"type": "string",
"display": true,
"required": false,
"displayName": "Recording Duration In Minutes",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "What CRM have you used in the past?",
"type": "string",
"display": true,
"required": false,
"displayName": "What CRM have you used in the past?",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Pain Points",
"type": "string",
"display": true,
"required": false,
"displayName": "Pain Points",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Feature requested",
"type": "string",
"display": true,
"required": false,
"displayName": "Feature requested",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "What I Did Well",
"type": "string",
"display": true,
"required": false,
"displayName": "What I Did Well",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "What to Improve",
"type": "string",
"display": true,
"required": false,
"displayName": "What to Improve",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Discovery (1–5)",
"type": "string",
"display": true,
"required": false,
"displayName": "Discovery (1–5)",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Onboarding Clarity (1–5)",
"type": "string",
"display": true,
"required": false,
"displayName": "Onboarding Clarity (1–5)",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Objection Handling (1–5)",
"type": "string",
"display": true,
"required": false,
"displayName": "Objection Handling (1–5)",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Engagement/Rapport (1–5)",
"type": "string",
"display": true,
"required": false,
"displayName": "Engagement/Rapport (1–5)",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Close/Next Steps (1–5)",
"type": "string",
"display": true,
"required": false,
"displayName": "Close/Next Steps (1–5)",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Overall Score [25]",
"type": "string",
"display": true,
"required": false,
"displayName": "Overall Score [25]",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Overall Score %",
"type": "string",
"display": true,
"required": false,
"displayName": "Overall Score %",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "@dropdown",
"type": "string",
"display": true,
"required": false,
"displayName": "@dropdown",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "meeting_invitees_name",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "meeting_invitees_name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "meeting_invitees_email",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "meeting_invitees_email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "crm_used_past",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "crm_used_past",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "pain_points",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "pain_points",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "feature_requested",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "feature_requested",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "what_i_did_well",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "what_i_did_well",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "what_to_improve",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "what_to_improve",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "scores_discovery",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "scores_discovery",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "scores_onboarding_clarity",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "scores_onboarding_clarity",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "scores_objection_handling",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "scores_objection_handling",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "scores_engagement_rapport",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "scores_engagement_rapport",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "scores_close_next_steps",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "scores_close_next_steps",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "overall_score_25",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "overall_score_25",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "overall_score_percent",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "overall_score_percent",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "notes",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "notes",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1TcWkY4KVQfl7n5n9UyjiV590GdzgbCOrLvfb8d6FreA/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1TcWkY4KVQfl7n5n9UyjiV590GdzgbCOrLvfb8d6FreA",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1TcWkY4KVQfl7n5n9UyjiV590GdzgbCOrLvfb8d6FreA/edit?usp=drivesdk",
"cachedResultName": "Kopie von REsimpli Onboarding Call - Fathom Call Score"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "0mHUWOW7FKfeUt2J",
"name": "ClickLessAI Google Sheets"
}
},
"typeVersion": 4.7
},
{
"id": "072de8f9-05a4-4c32-a3a8-42726eefe3e3",
"name": "대본 가져오기",
"type": "@firefliesai/n8n-nodes-fireflies.fireflies",
"position": [
208,
-176
],
"parameters": {
"transcriptId": "={{ $json.body.meetingId }}"
},
"credentials": {
"firefliesApi": {
"id": "MaxlgNMFhzk3ltwr",
"name": "Fireflies account"
}
},
"typeVersion": 1
},
{
"id": "57c6c8ee-a89d-4fd1-b7ae-e5fcb54ebb34",
"name": "모델에 메시지 보내기",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
688,
-176
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4o",
"cachedResultName": "GPT-4O"
},
"options": {},
"messages": {
"values": [
{
"content": "=Transcript (raw):\n{{ $json.Transcript }}\n\nTask:\n- Extract facts and evaluate according to System Prompt.\n- Reply exclusively in JSON format according to Assistant Prompt."
},
{
"role": "assistant",
"content": "={\n \"meeting_invitees_name\": \"\",\n \"meeting_invitees_email\": \"\",\n \"crm_used_past\": \"\",\n \"pain_points\": \"\",\n \"feature_requested\": \"\",\n \"what_i_did_well\": \"\",\n \"what_to_improve\": \"\",\n \"scores\": {\n \"discovery\": 1,\n \"onboarding_clarity\": 1,\n \"objection_handling\": 1,\n \"engagement_rapport\": 1,\n \"close_next_steps\": 1\n },\n \"overall_score_25\": 0,\n \"overall_score_percent\": 0,\n \"notes\": \"\"\n}\n\n\nSpecific format requirements:\n- pain_points, feature_requested, what_i_did_well, what_to_improve are SEMICOLON-separated strings (not arrays).\n- Sort points alphabetically; deduplicate identical phrases.\n- Maximum 6 points per field; each phrase ≤ 12 words.\n- \"notes\" is optional, short (≤ 50 words)."
},
{
"role": "system",
"content": "=You are a sober analyzer for sales transcripts of a CRM provider.\n\nRules:\n- Extract ONLY information from the transcript (no hallucinations).\n- If info is missing/unclear: use empty string \"\".\n- meeting_invitees_name = main contact (prospect), NOT additional participants.\n- Emails/names only if explicitly mentioned.\n- Scores are whole numbers 1–5 (estimate conservatively, never 0).\n- Calculate overall_score_25 = sum of the 5 scores.\n- Calculate overall_score_percent = round((overall_score_25/25)*100).\n- Output must be STRICT JSON according to the Assistant schema. No explanations/Markdown.\n\nFormat rules for text fields:\n- Always use ONE string (no arrays).\n- Multiple points must be SEMICOLON-separated, with a space after each semicolon.\n- No duplicate points, no leading/trailing semicolons, everything trimmed.\n- Order: alphabetically sorted (German sorting) for consistency.\n\n“What to Improve” – generation rules (must NOT be empty):\n- Derive improvements from (a) explicit criticism from the prospect, (b) visible gaps in discovery (goals, stakeholders, timeline, budget),\n (c) unclear next steps, (d) weak objection handling, (e) missing proof of value.\n- Provide AT LEAST one, preferably 2–3 short, concrete points (semicolon-separated)."
}
]
}
},
"credentials": {
"openAiApi": {
"id": "WVC4WZ1lsahz1CtE",
"name": "OpenAi account"
}
},
"typeVersion": 1.8
},
{
"id": "2a5072fd-8895-41d6-b2bd-9c005b7d1b5d",
"name": "자바스크립트 코드",
"type": "n8n-nodes-base.code",
"position": [
1056,
-176
],
"parameters": {
"jsCode": "// Hilfsfunktionen\nfunction getRawContent(item) {\n const j = item.json || {};\n return (\n j?.message?.content ??\n j?.data?.[0]?.message?.content ??\n j?.choices?.[0]?.message?.content ??\n j?.content ??\n ''\n );\n}\n\nfunction extractJson(raw) {\n if (typeof raw !== 'string') return null;\n // Entferne Markdown-Fences ```json ... ```\n let cleaned = raw.replace(/```json/gi, '').replace(/```/g, '').trim();\n const m = cleaned.match(/\\{[\\s\\S]*\\}/);\n return m ? m[0] : null;\n}\n\nfunction clamp1to5(v) {\n const n = Math.round(Number(v));\n if (Number.isNaN(n)) return 1;\n return Math.max(1, Math.min(5, n));\n}\n\nfunction ensureString(val) {\n if (val === undefined || val === null) return \"\";\n return String(val).trim();\n}\n\n// Starte mit dem ersten Item (typisch: 1 LLM-Response)\nconst raw = getRawContent(items[0]);\nconst jsonStr = extractJson(raw);\nif (!jsonStr) {\n throw new Error('Kein JSON im LLM-Output gefunden.');\n}\n\nlet obj;\ntry {\n obj = JSON.parse(jsonStr);\n} catch (e) {\n throw new Error('JSON nicht parsebar: ' + e.message);\n}\n\n// Default-Werte einsetzen\nobj.meeting_invitees_name = ensureString(obj.meeting_invitees_name);\nobj.meeting_invitees_email = ensureString(obj.meeting_invitees_email);\nobj.crm_used_past = ensureString(obj.crm_used_past);\nobj.pain_points = ensureString(obj.pain_points);\nobj.feature_requested = ensureString(obj.feature_requested);\nobj.what_i_did_well = ensureString(obj.what_i_did_well);\nobj.what_to_improve = ensureString(obj.what_to_improve);\nobj.notes = ensureString(obj.notes);\n\n// Scores validieren\nconst s = obj.scores || {};\ns.discovery = clamp1to5(s.discovery);\ns.onboarding_clarity = clamp1to5(s.onboarding_clarity);\ns.objection_handling = clamp1to5(s.objection_handling);\ns.engagement_rapport = clamp1to5(s.engagement_rapport);\ns.close_next_steps = clamp1to5(s.close_next_steps);\nobj.scores = s;\n\n// Gesamtwerte neu berechnen\nconst overall = s.discovery + s.onboarding_clarity + s.objection_handling + s.engagement_rapport + s.close_next_steps;\nobj.overall_score_25 = overall;\nobj.overall_score_percent = Math.round((overall / 25) * 100);\n\n// Output flatten für Sheets/Airtable etc.\nconst out = {\n meeting_invitees_name: obj.meeting_invitees_name,\n meeting_invitees_email: obj.meeting_invitees_email,\n crm_used_past: obj.crm_used_past,\n pain_points: obj.pain_points,\n feature_requested: obj.feature_requested,\n what_i_did_well: obj.what_i_did_well,\n what_to_improve: obj.what_to_improve,\n scores_discovery: s.discovery,\n scores_onboarding_clarity: s.onboarding_clarity,\n scores_objection_handling: s.objection_handling,\n scores_engagement_rapport: s.engagement_rapport,\n scores_close_next_steps: s.close_next_steps,\n overall_score_25: obj.overall_score_25,\n overall_score_percent: obj.overall_score_percent,\n notes: obj.notes\n};\n\nreturn [{ json: out }];"
},
"typeVersion": 2
},
{
"id": "d15b7a46-5899-4cdb-b434-41da0b969f20",
"name": "자바스크립트 코드1",
"type": "n8n-nodes-base.code",
"position": [
448,
-176
],
"parameters": {
"jsCode": "// === Konfiguration ===\nconst GAP_BREAK_SECONDS = 15; // Neuer Block, wenn zwischen Sätzen > 15s liegen\nconst INCLUDE_TIMESTAMPS = true; // Zeitstempel am Block-Anfang zeigen (mm:ss)\nconst TIME_FORMAT = \"mm:ss\"; // mm:ss oder hh:mm:ss\n\n// === Hilfsfunktionen ===\nfunction toTimecode(sec) {\n if (sec == null || isNaN(sec)) return \"\";\n const s = Math.max(0, Math.floor(sec));\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const ss = s % 60;\n const pad = (n)=>String(n).padStart(2,\"0\");\n if (TIME_FORMAT === \"hh:mm:ss\" || h > 0) return `${pad(h)}:${pad(m)}:${pad(ss)}`;\n return `${pad(m)}:${pad(ss)}`;\n}\n\nfunction cleanText(t) {\n // Bewahre Original-Satz, aber trimme Leerzeichen\n return String(t ?? \"\").replace(/\\s+/g, \" \").trim();\n}\n\nfunction ensureEndPunctuation(t) {\n // Wenn der Block nicht auf . ! ? endet, füge einen Punkt hinzu\n if (!t) return t;\n return /[.!?…]$/.test(t) ? t : t + \".\";\n}\n\n// === Input robust finden ===\nconst root = items[0]?.json || {};\nconst data = root.data || root;\nconst sentences = Array.isArray(data.sentences) ? data.sentences.slice() : [];\nconst speakersArr = Array.isArray(data.speakers) ? data.speakers : [];\n\n// Map Speaker-ID -> Name\nconst speakerMap = new Map();\nfor (const s of speakersArr) {\n if (s && typeof s.id !== \"undefined\") {\n speakerMap.set(s.id, s.name || `Speaker ${s.id}`);\n }\n}\n\n// Fallback: wenn speakerMap leer ist, holen wir Namen aus den Sätzen\nif (speakerMap.size === 0) {\n for (const s of sentences) {\n if (typeof s.speaker_id !== \"undefined\") {\n speakerMap.set(s.speaker_id, s.speaker_name || `Speaker ${s.speaker_id}`);\n }\n }\n}\n\n// Sortieren nach index (falls vorhanden) sonst start_time\nsentences.sort((a, b) => {\n if (typeof a.index === \"number\" && typeof b.index === \"number\") return a.index - b.index;\n return (a.start_time ?? 0) - (b.start_time ?? 0);\n});\n\n// === Gruppierung zu Sprecher-Blöcken ===\nconst blocks = []; // [{ speakerName, startTime, endTime, text }]\nlet current = null;\n\nfor (const s of sentences) {\n const name = speakerMap.get(s.speaker_id) || s.speaker_name || \"Unbekannt\";\n const t = cleanText(s.text ?? s.raw_text ?? \"\");\n\n if (!t) continue;\n\n const st = Number(s.start_time ?? 0);\n const et = Number(s.end_time ?? st);\n\n const shouldBreak =\n !current ||\n current.speakerName !== name ||\n (typeof current.lastEnd === \"number\" && typeof st === \"number\" && st - current.lastEnd > GAP_BREAK_SECONDS);\n\n if (shouldBreak) {\n // neuen Block starten\n current = {\n speakerName: name,\n startTime: st,\n lastEnd: et,\n texts: [t]\n };\n blocks.push(current);\n } else {\n // an bestehenden Block anhängen\n current.texts.push(t);\n current.lastEnd = et;\n }\n}\n\n// === Formatierung ===\n\n// a) Formatiert mit Sprecher + Zeit (wie Beispiel)\nconst formattedLines = blocks.map(b => {\n let linePrefix = b.speakerName + \": \";\n if (INCLUDE_TIMESTAMPS) {\n linePrefix = `${b.speakerName}: ${toTimecode(b.startTime)} `;\n }\n const paragraph = ensureEndPunctuation(cleanText(b.texts.join(\" \")));\n return linePrefix + paragraph;\n});\nconst transcript_formatted = formattedLines.join(\"\\n\\n\");\n\n// b) Plain-Fließtext ohne Zeitstempel, Sprecher dennoch markiert\nconst plainLines = blocks.map(b => {\n const paragraph = ensureEndPunctuation(cleanText(b.texts.join(\" \")));\n return `${b.speakerName}: ${paragraph}`;\n});\nconst transcript_plain = plainLines.join(\"\\n\\n\");\n\n// Optional: Metadaten (Titel/Meeting-Link), falls vorhanden\nconst meta = {\n title: data.title || \"\",\n meeting_link: data.meeting_link || \"\",\n organizer_email: data.organizer_email || \"\",\n date_iso: data.dateString || \"\"\n};\n\nreturn [{\n json: {\n transcript_formatted,\n transcript_plain,\n meta\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "bd1bf746-bc2f-468e-95ca-8179aab21cec",
"name": "메시지 보내기",
"type": "n8n-nodes-base.gmail",
"position": [
2016,
-48
],
"webhookId": "394e352b-227d-446f-a204-0598524b0b09",
"parameters": {
"sendTo": "managment@clicklessai.de",
"message": "Hey, your google sheet is ready! Go check it out!",
"options": {},
"subject": "Grading Call System ",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"id": "2Rt5sYy8vHGty2lV",
"name": "ClickLessAI Gmail"
}
},
"typeVersion": 2.1
},
{
"id": "bb529931-6899-4905-9a14-a2322f5c8488",
"name": "메시지 보내기1",
"type": "n8n-nodes-base.slack",
"position": [
1536,
-48
],
"webhookId": "05be8f86-7174-452b-ad93-bacadaf8e927",
"parameters": {
"text": "Hey, your google sheet is ready! Go check it out!",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "C09HKQVAKB7",
"cachedResultName": "demo"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"id": "G4NvdInPNhmhP1e9",
"name": "Slack account"
}
},
"typeVersion": 2.3
},
{
"id": "d877f4be-78c0-4ea1-af64-6b296c35cd00",
"name": "스티커 메모",
"type": "n8n-nodes-base.stickyNote",
"position": [
-112,
-480
],
"parameters": {
"width": 464,
"height": 608,
"content": "## Setup fireflies.ai\n\n- Create an Account \n- Setup a Webhook in fireflies.ai \n- API Section: https://app.fireflies.ai/integrations/custom/n8n"
},
"typeVersion": 1
},
{
"id": "3253a5b7-fcb5-4652-8bda-2c18639969e9",
"name": "스티커 메모1",
"type": "n8n-nodes-base.stickyNote",
"position": [
384,
-480
],
"parameters": {
"color": 5,
"width": 816,
"height": 608,
"content": "## Connect Open AI Credential \nGo to https://platform.openai.com and log in.\n\t2.\tClick on your profile icon in the top-right corner.\n\t3.\tSelect View API keys.\n\t4.\tClick Create new secret key.\n\t5.\tCopy the generated key (it starts with sk-...) and save it securely — you won’t be able to see it again later."
},
"typeVersion": 1
},
{
"id": "42cc890a-7510-4dfa-b556-5125357cc34c",
"name": "스티커 메모2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1232,
-480
],
"parameters": {
"color": 4,
"width": 512,
"height": 608,
"content": "## Setup Slack\n\n🔹 Slack API Credentials\n\n\t1.\tGo to https://api.slack.com/apps and log in with your Slack account.\n\t2.\tClick Create New App.\n\t•\tChoose From scratch and select your workspace.\n\t3.\tIn the app dashboard, go to OAuth & Permissions.\n\t4.\tUnder Scopes, add the permissions your app needs.\n\t5.\tScroll up and click Install to Workspace.\n\t6.\tOnce installed, you’ll see your Bot User OAuth Token (xoxb-...). Copy and save it securely."
},
"typeVersion": 1
},
{
"id": "8d4db160-d9f7-4b8c-9744-f11f99375455",
"name": "스티커 메모3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1776,
-480
],
"parameters": {
"width": 464,
"height": 608,
"content": "## Setup Gmail \n\n1.\tGo to https://console.cloud.google.com/ and log in.\n2.\tCreate a New Project (or select an existing one).\n3.\tIn the left menu, go to APIs & Services → Library.\n4.\tSearch for Gmail API and click Enable.\n5.\tGo to APIs & Services → Credentials.\n6.\tClick Create Credentials → OAuth client ID.\n7. You’ll need to configure an OAuth consent screen first (name + scope).\n8.\tChoose the app type (e.g., Desktop, Web).\n9.\tOnce created, copy your Client ID and Client Secret.\n10.\tDownload the credentials.json file (you’ll need it for client apps)."
},
"typeVersion": 1
},
{
"id": "13ec4b49-ebed-4fa3-bc00-993951948242",
"name": "스티커 메모4",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
-848
],
"parameters": {
"color": 6,
"width": 368,
"height": 336,
"content": "## How it Works \n\n\t1.\tTrigger via Webhook – The workflow starts whenever a Fireflies.ai transcript is available.\n\t2.\tTranscript Analysis – The call transcript is passed to OpenAI, which evaluates and grades the quality of the call.\n\t3.\tLead Data Logging – The graded results and lead information are automatically appended into a Google Sheet for record-keeping and performance tracking.\n\t4.\tNotifications – You instantly receive a Slack or Gmail notification summarizing the evaluation, ensuring you never miss important insights."
},
"typeVersion": 1
},
{
"id": "9c4a4d5d-e559-4302-9c34-b762b95dbe22",
"name": "스티커 메모5",
"type": "n8n-nodes-base.stickyNote",
"position": [
608,
-848
],
"parameters": {
"color": 6,
"width": 368,
"height": 336,
"content": "## Why its Valuable \n\n\t•\t✅ Automated Call Reviews – No need to manually re-listen or grade calls.\n\t•\t✅ Consistent Scoring – Objective AI-powered evaluation improves training and feedback.\n\t•\t✅ Centralized Data – All call insights and lead details are stored neatly in Google Sheets.\n\t•\t✅ Instant Alerts – Sales teams get notified right away via Slack or Gmail, boosting response times.\n\t•\t✅ Scalable – Works out-of-the-box for onboarding, sales, or support teams, and reduces manual overhead.\n"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "e3db7392-05a2-4fca-abfd-7f442e5563b9",
"connections": {
"a9579f80-9bec-473c-83c4-66b1134194ee": {
"main": [
[
{
"node": "072de8f9-05a4-4c32-a3a8-42726eefe3e3",
"type": "main",
"index": 0
}
]
]
},
"57c6c8ee-a89d-4fd1-b7ae-e5fcb54ebb34": {
"main": [
[
{
"node": "2a5072fd-8895-41d6-b2bd-9c005b7d1b5d",
"type": "main",
"index": 0
}
]
]
},
"072de8f9-05a4-4c32-a3a8-42726eefe3e3": {
"main": [
[
{
"node": "d15b7a46-5899-4cdb-b434-41da0b969f20",
"type": "main",
"index": 0
}
]
]
},
"2a5072fd-8895-41d6-b2bd-9c005b7d1b5d": {
"main": [
[
{
"node": "d7377f6f-c1cc-47a9-8d1d-99dadd47fb2f",
"type": "main",
"index": 0
}
]
]
},
"d7377f6f-c1cc-47a9-8d1d-99dadd47fb2f": {
"main": [
[
{
"node": "bd1bf746-bc2f-468e-95ca-8179aab21cec",
"type": "main",
"index": 0
},
{
"node": "bb529931-6899-4905-9a14-a2322f5c8488",
"type": "main",
"index": 0
}
]
]
},
"d15b7a46-5899-4cdb-b434-41da0b969f20": {
"main": [
[
{
"node": "57c6c8ee-a89d-4fd1-b7ae-e5fcb54ebb34",
"type": "main",
"index": 0
}
]
]
}
}
}자주 묻는 질문
이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
중급
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
基于GPT-4의스마트招聘与候选人互动系统
基于GPT-4의AI招聘系统,用于简历筛选与자동外联
If
Code
Wait
+
If
Code
Wait
30 노드Marth
인사
자동화된 리드 캡처, AI 자격 평가 및 ElevenLabs 개인화 음성 후속 조치
OpenAI와 ElevenLabs 기반 자동 리드 캡처 및 AI 개인화 음성 후속 조치
If
Set
Code
+
If
Set
Code
22 노드Marth
리드 육성
보고서 자동화
GPT-4o와 Google Docs를 사용하여 주간 마케팅 성과 보고서 생성并보내기로 Slack
Code
Slack
Google Docs
+
Code
Slack
Google Docs
16 노드Emilio Loewenstein
AI 회의록 및 작업 항목 추적기: Notion, Slack 및 Gmail 통합
AI 회의록 및 작업 항목 추적기: Notion, Slack 및 Gmail 통합
Code
Gmail
Slack
+
Code
Gmail
Slack
25 노드Daniel Shashko
프로젝트 관리
회의록 및 액션 아이템 트래커
AI 기반 회의록: GPT-4, 작업 할당 및 다중 채널 배포 활용
If
Set
Code
+
If
Set
Code
38 노드Jitesh Dugar
콘텐츠 제작
청구서 첨부 파일이 있나요?
GPT-4o 사기 감지와 QuickBooks로 인voice 자동 추출 및 승인
If
Code
Gmail
+
If
Code
Gmail
22 노드Jitesh Dugar
워크플로우 정보
난이도
중급
노드 수14
카테고리-
노드 유형8
저자
Emilio Loewenstein
@emilio-loewensteinEmilio is an 18-year-old AI Engineer and Co-Founder of an AI agency, building intelligent systems that help businesses automate, innovate, and scale.
외부 링크
n8n.io에서 보기 →
이 워크플로우 공유