Onboard-Anruf-Bewertungssystem
Dies ist ein Automatisierungsworkflow mit 14 Nodes. Hauptsächlich werden Code, Gmail, Slack, Webhook, GoogleSheets und andere Nodes verwendet. Automatisierung der Bewertung von Verkaufsgesprächen mit Fireflies.ai, OpenAI und Google Sheets
- •Google-Konto + Gmail API-Anmeldedaten
- •Slack Bot Token oder Webhook URL
- •HTTP Webhook-Endpunkt (wird von n8n automatisch generiert)
- •Google Sheets API-Anmeldedaten
- •OpenAI API Key
Verwendete Nodes (14)
Kategorie
{
"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": "Webhook",
"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": "Zeile in Tabelle anhängen",
"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": "Transkript abrufen",
"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": "Modell abfragen",
"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": "Code in JavaScript",
"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": "Code in JavaScript1",
"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": "Nachricht senden",
"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": "Nachricht senden1",
"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": "Haftnotiz",
"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": "Haftnotiz1",
"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": "Haftnotiz2",
"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": "Haftnotiz3",
"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": "Haftnotiz4",
"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": "Haftnotiz5",
"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
}
]
]
}
}
}Wie verwende ich diesen Workflow?
Kopieren Sie den obigen JSON-Code, erstellen Sie einen neuen Workflow in Ihrer n8n-Instanz und wählen Sie "Aus JSON importieren". Fügen Sie die Konfiguration ein und passen Sie die Anmeldedaten nach Bedarf an.
Für welche Szenarien ist dieser Workflow geeignet?
Fortgeschritten
Ist es kostenpflichtig?
Dieser Workflow ist völlig kostenlos. Beachten Sie jedoch, dass Drittanbieterdienste (wie OpenAI API), die im Workflow verwendet werden, möglicherweise kostenpflichtig sind.
Verwandte Workflows
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.
Diesen Workflow teilen