This workflow corresponds to n8n.io template #14717 — we link there as the canonical source.
This workflow follows the Gmail → Google Docs recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "ySNUgic8v2IM1UGH",
"name": "Fireflies Meeting Transcript to Auto-Proposal with Telegram Approval and Gmail Delivery",
"tags": [],
"nodes": [
{
"id": "node-1",
"name": "When Transcript Ready",
"type": "n8n-nodes-base.webhook",
"position": [
0,
288
],
"parameters": {
"path": "fireflies-transcript",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "node-2",
"name": "Respond 200 OK",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
224,
288
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\"status\": \"received\", \"message\": \"Transcript processing started\"}"
},
"typeVersion": 1
},
{
"id": "node-3",
"name": "Get Full Transcript",
"type": "n8n-nodes-base.httpRequest",
"maxTries": 3,
"position": [
448,
288
],
"parameters": {
"url": "https://api.fireflies.ai/graphql",
"method": "POST",
"options": {
"timeout": 30000
},
"jsonBody": "={\n \"query\": \"query GetTranscript($transcriptId: String!) { transcript(id: $transcriptId) { id title date duration organizer_email participants { name email } sentences { text speaker_name } action_items summary { overview shorthand_bullet } } }\",\n \"variables\": { \"transcriptId\": \"{{ $json.body.meetingId || $json.body.transcriptId }}\" }\n}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"retryOnFail": true,
"typeVersion": 4.2,
"waitBetweenTries": 2000
},
{
"id": "node-4",
"name": "If Transcript Exists",
"type": "n8n-nodes-base.if",
"position": [
672,
288
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true
},
"combinator": "and",
"conditions": [
{
"id": "cond-1",
"operator": {
"type": "object",
"operation": "exists"
},
"leftValue": "={{ $json.data.transcript }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "node-5",
"name": "Classify Meeting Type",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
896,
192
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4o"
},
"options": {
"temperature": 0.2
},
"messages": {
"values": [
{
"role": "system",
"content": "You are a meeting analyst. Analyze the meeting transcript and determine if this meeting requires a follow-up proposal to be sent to the client.\n\nA proposal is needed when:\n- Services, pricing, or project scope was discussed\n- The client expressed interest in hiring or engaging for work\n- Next steps involve sending a quote, estimate, or proposal\n- There was a discovery/sales call\n\nA proposal is NOT needed when:\n- Internal team meetings\n- Status updates or check-ins\n- Social/casual conversations\n- Support/troubleshooting calls\n\nRespond in this exact JSON format:\n{\n \"needs_proposal\": true/false,\n \"reason\": \"Brief explanation\",\n \"client_name\": \"Name or null\",\n \"client_email\": \"Email or null\",\n \"client_company\": \"Company or null\",\n \"meeting_summary\": \"2-3 sentence summary of what was discussed\",\n \"key_points\": [\"point 1\", \"point 2\"],\n \"services_discussed\": [\"service 1\", \"service 2\"],\n \"client_pain_points\": [\"pain 1\", \"pain 2\"],\n \"estimated_scope\": \"Brief scope description or null\",\n \"follow_up_notes\": \"Any specific things the client mentioned wanting\"\n}"
},
{
"content": "=Meeting Title: {{ $json.data.transcript.title }}\nDate: {{ $json.data.transcript.date }}\nParticipants: {{ JSON.stringify($json.data.transcript.participants) }}\nSummary: {{ $json.data.transcript.summary?.overview || 'No summary available' }}\n\nFull Transcript:\n{{ $json.data.transcript.sentences?.map(s => s.speaker_name + ': ' + s.text).join('\\n') }}"
}
]
},
"jsonOutput": true
},
"typeVersion": 1.8
},
{
"id": "node-6",
"name": "If Proposal Needed",
"type": "n8n-nodes-base.if",
"position": [
1248,
192
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true
},
"combinator": "and",
"conditions": [
{
"id": "cond-2",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.needs_proposal }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "node-7",
"name": "Generate Proposal Content",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1472,
96
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4o"
},
"options": {
"temperature": 0.4
},
"messages": {
"values": [
{
"role": "system",
"content": "You are a professional proposal writer. Based on the meeting analysis provided, generate structured proposal content to fill a document template.\n\nReturn a JSON object with these exact keys. Each value should be well-written, professional but conversational text:\n\n{\n \"CLIENT_NAME\": \"The client's full name\",\n \"CLIENT_COMPANY\": \"The client's company name or 'your team' if unknown\",\n \"MEETING_DATE\": \"The date of the meeting in a readable format\",\n \"MEETING_RECAP\": \"2-3 sentence recap of what was discussed in the meeting\",\n \"UNDERSTANDING_OF_NEEDS\": \"A paragraph showing clear understanding of the client's pain points and what they need solved. Be specific to what was discussed.\",\n \"PROPOSED_SOLUTION\": \"A clear description of the proposed approach, broken into phases if applicable. Be specific, not generic.\",\n \"DELIVERABLES\": \"A bullet-point list of specific deliverables (use newlines between each)\",\n \"TIMELINE\": \"Estimated timeline for delivery with key milestones\",\n \"INVESTMENT\": \"[PRICING TO BE CONFIRMED]\",\n \"NEXT_STEPS\": \"What happens after the client approves - onboarding, kickoff call, etc.\",\n \"summary_for_review\": \"3-4 sentence summary for the Telegram approval message\"\n}\n\nWrite in a professional but warm, conversational tone. Be specific based on the meeting content. Do not use generic filler."
},
{
"content": "=Meeting Analysis:\nClient: {{ $json.client_name }} ({{ $json.client_company || 'Company not specified' }})\nEmail: {{ $json.client_email || 'Not found' }}\nMeeting Date: {{ $json.meeting_date || 'Not specified' }}\n\nMeeting Summary: {{ $json.meeting_summary }}\n\nKey Points Discussed:\n{{ $json.key_points?.join('\\n- ') }}\n\nServices Discussed:\n{{ $json.services_discussed?.join('\\n- ') }}\n\nClient Pain Points:\n{{ $json.client_pain_points?.join('\\n- ') }}\n\nEstimated Scope: {{ $json.estimated_scope || 'To be determined' }}\n\nFollow-up Notes: {{ $json.follow_up_notes || 'None' }}"
}
]
},
"jsonOutput": true
},
"typeVersion": 1.8
},
{
"id": "node-8",
"name": "Prepare Proposal Data",
"type": "n8n-nodes-base.set",
"position": [
1824,
96
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "set-1",
"name": "client_name",
"type": "string",
"value": "={{ $('Classify Meeting Type').item.json.client_name }}"
},
{
"id": "set-2",
"name": "client_email",
"type": "string",
"value": "={{ $('Classify Meeting Type').item.json.client_email }}"
},
{
"id": "set-3",
"name": "client_company",
"type": "string",
"value": "={{ $('Classify Meeting Type').item.json.client_company }}"
},
{
"id": "set-4",
"name": "meeting_summary",
"type": "string",
"value": "={{ $('Classify Meeting Type').item.json.meeting_summary }}"
},
{
"id": "set-5",
"name": "services_discussed",
"type": "string",
"value": "={{ $('Classify Meeting Type').item.json.services_discussed?.join(', ') }}"
},
{
"id": "set-6",
"name": "summary_for_review",
"type": "string",
"value": "={{ $json.summary_for_review }}"
},
{
"id": "set-7",
"name": "CLIENT_NAME",
"type": "string",
"value": "={{ $json.CLIENT_NAME }}"
},
{
"id": "set-8",
"name": "CLIENT_COMPANY",
"type": "string",
"value": "={{ $json.CLIENT_COMPANY }}"
},
{
"id": "set-9",
"name": "MEETING_DATE",
"type": "string",
"value": "={{ $json.MEETING_DATE }}"
},
{
"id": "set-10",
"name": "MEETING_RECAP",
"type": "string",
"value": "={{ $json.MEETING_RECAP }}"
},
{
"id": "set-11",
"name": "UNDERSTANDING_OF_NEEDS",
"type": "string",
"value": "={{ $json.UNDERSTANDING_OF_NEEDS }}"
},
{
"id": "set-12",
"name": "PROPOSED_SOLUTION",
"type": "string",
"value": "={{ $json.PROPOSED_SOLUTION }}"
},
{
"id": "set-13",
"name": "DELIVERABLES",
"type": "string",
"value": "={{ $json.DELIVERABLES }}"
},
{
"id": "set-14",
"name": "TIMELINE",
"type": "string",
"value": "={{ $json.TIMELINE }}"
},
{
"id": "set-15",
"name": "INVESTMENT",
"type": "string",
"value": "={{ $json.INVESTMENT }}"
},
{
"id": "set-16",
"name": "NEXT_STEPS",
"type": "string",
"value": "={{ $json.NEXT_STEPS }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "node-9",
"name": "Copy Proposal Template",
"type": "n8n-nodes-base.googleDrive",
"position": [
2048,
96
],
"parameters": {
"name": "=Proposal - {{ $json.CLIENT_NAME }} - {{ $json.CLIENT_COMPANY }} - {{ $json.MEETING_DATE }}",
"fileId": {
"__rl": true,
"mode": "id",
"value": ""
},
"options": {},
"operation": "copy"
},
"typeVersion": 3
},
{
"id": "node-9b",
"name": "Fill Template Placeholders",
"type": "n8n-nodes-base.googleDocs",
"position": [
2272,
96
],
"parameters": {
"actionsUi": {
"actionFields": [
{
"text": "{{CLIENT_NAME}}",
"action": "replaceAll",
"matchCase": true,
"replaceText": "={{ $('Prepare Proposal Data').item.json.CLIENT_NAME }}"
},
{
"text": "{{CLIENT_COMPANY}}",
"action": "replaceAll",
"matchCase": true,
"replaceText": "={{ $('Prepare Proposal Data').item.json.CLIENT_COMPANY }}"
},
{
"text": "{{MEETING_DATE}}",
"action": "replaceAll",
"matchCase": true,
"replaceText": "={{ $('Prepare Proposal Data').item.json.MEETING_DATE }}"
},
{
"text": "{{MEETING_RECAP}}",
"action": "replaceAll",
"matchCase": true,
"replaceText": "={{ $('Prepare Proposal Data').item.json.MEETING_RECAP }}"
},
{
"text": "{{UNDERSTANDING_OF_NEEDS}}",
"action": "replaceAll",
"matchCase": true,
"replaceText": "={{ $('Prepare Proposal Data').item.json.UNDERSTANDING_OF_NEEDS }}"
},
{
"text": "{{PROPOSED_SOLUTION}}",
"action": "replaceAll",
"matchCase": true,
"replaceText": "={{ $('Prepare Proposal Data').item.json.PROPOSED_SOLUTION }}"
},
{
"text": "{{DELIVERABLES}}",
"action": "replaceAll",
"matchCase": true,
"replaceText": "={{ $('Prepare Proposal Data').item.json.DELIVERABLES }}"
},
{
"text": "{{TIMELINE}}",
"action": "replaceAll",
"matchCase": true,
"replaceText": "={{ $('Prepare Proposal Data').item.json.TIMELINE }}"
},
{
"text": "{{INVESTMENT}}",
"action": "replaceAll",
"matchCase": true,
"replaceText": "={{ $('Prepare Proposal Data').item.json.INVESTMENT }}"
},
{
"text": "{{NEXT_STEPS}}",
"action": "replaceAll",
"matchCase": true,
"replaceText": "={{ $('Prepare Proposal Data').item.json.NEXT_STEPS }}"
}
]
},
"operation": "update"
},
"typeVersion": 2
},
{
"id": "node-10",
"name": "Export Doc as PDF",
"type": "n8n-nodes-base.httpRequest",
"maxTries": 2,
"position": [
2496,
96
],
"parameters": {
"url": "=https://www.googleapis.com/drive/v3/files/{{ $json.documentId }}/export?mimeType=application/pdf",
"options": {
"response": {
"response": {
"responseFormat": "file"
}
}
},
"authentication": "oAuth2"
},
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "node-11",
"name": "Save PDF to Drive",
"type": "n8n-nodes-base.googleDrive",
"position": [
2720,
96
],
"parameters": {
"name": "=Proposal - {{ $('Prepare Proposal Data').item.json.CLIENT_NAME }} - {{ $('Prepare Proposal Data').item.json.CLIENT_COMPANY }}.pdf",
"driveId": {
"__rl": true,
"mode": "id",
"value": "MyDrive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "id",
"value": ""
}
},
"typeVersion": 3
},
{
"id": "node-12",
"name": "Send Approval Request",
"type": "n8n-nodes-base.telegram",
"position": [
2944,
96
],
"parameters": {
"text": "=\ud83d\udccb *NEW PROPOSAL READY FOR REVIEW*\n\n*Client:* {{ $('Prepare Proposal Data').item.json.CLIENT_NAME || 'Unknown' }}\n*Company:* {{ $('Prepare Proposal Data').item.json.CLIENT_COMPANY || 'Not specified' }}\n*Email:* {{ $('Prepare Proposal Data').item.json.client_email || '\u26a0\ufe0f NOT FOUND - you will be asked to provide it' }}\n\n*Services:* {{ $('Prepare Proposal Data').item.json.services_discussed || 'See proposal' }}\n\n*Summary:*\n{{ $('Prepare Proposal Data').item.json.summary_for_review }}\n\n\ud83d\udcc4 [View Proposal Doc](https://docs.google.com/document/d/{{ $('Copy Proposal Template').item.json.id }})\n\n---\nClick below to approve or reject:",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"typeVersion": 1.2
},
{
"id": "node-13",
"name": "Wait for Approval",
"type": "n8n-nodes-base.wait",
"position": [
3168,
96
],
"parameters": {
"resume": "webhook",
"options": {}
},
"typeVersion": 1.1
},
{
"id": "node-14",
"name": "If Approved",
"type": "n8n-nodes-base.if",
"position": [
3392,
96
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true
},
"combinator": "and",
"conditions": [
{
"id": "cond-3",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.query.action }}",
"rightValue": "approve"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "node-15",
"name": "If Client Email Exists",
"type": "n8n-nodes-base.if",
"position": [
3616,
0
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true
},
"combinator": "and",
"conditions": [
{
"id": "cond-4",
"operator": {
"type": "string",
"operation": "isNotEmpty"
},
"leftValue": "={{ $('Prepare Proposal Data').item.json.client_email }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "node-16",
"name": "Ask for Client Email",
"type": "n8n-nodes-base.telegram",
"position": [
3840,
80
],
"parameters": {
"text": "\u26a0\ufe0f Client email was not found in the transcript.\n\nPlease reply with the client's email address:",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"typeVersion": 1.2
},
{
"id": "node-17",
"name": "Wait for Email Reply",
"type": "n8n-nodes-base.wait",
"position": [
4064,
80
],
"parameters": {
"resume": "webhook",
"options": {}
},
"typeVersion": 1.1
},
{
"id": "node-18",
"name": "Prepare Email Data",
"type": "n8n-nodes-base.set",
"position": [
4288,
-16
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "set-email",
"name": "final_client_email",
"type": "string",
"value": "={{ $('If Client Email Exists').item ? $('Prepare Proposal Data').item.json.client_email : $json.body?.message?.text || $json.query?.email }}"
},
{
"id": "set-name",
"name": "final_client_name",
"type": "string",
"value": "={{ $('Prepare Proposal Data').item.json.CLIENT_NAME || 'there' }}"
},
{
"id": "set-title",
"name": "final_proposal_title",
"type": "string",
"value": "=Proposal - {{ $('Prepare Proposal Data').item.json.CLIENT_NAME }} - {{ $('Prepare Proposal Data').item.json.CLIENT_COMPANY }}"
},
{
"id": "set-summary",
"name": "final_meeting_summary",
"type": "string",
"value": "={{ $('Prepare Proposal Data').item.json.meeting_summary }}"
},
{
"id": "set-pdf-id",
"name": "pdf_file_id",
"type": "string",
"value": "={{ $('Save PDF to Drive').item.json.id }}"
},
{
"id": "set-doc-id",
"name": "doc_id",
"type": "string",
"value": "={{ $('Copy Proposal Template').item.json.id }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "node-19",
"name": "Download PDF from Drive",
"type": "n8n-nodes-base.googleDrive",
"position": [
4512,
-16
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.pdf_file_id }}"
},
"options": {},
"operation": "download"
},
"typeVersion": 3
},
{
"id": "node-20",
"name": "Send Proposal Email",
"type": "n8n-nodes-base.gmail",
"position": [
4736,
-16
],
"parameters": {
"sendTo": "={{ $('Prepare Email Data').item.json.final_client_email }}",
"message": "=Hi {{ $('Prepare Email Data').item.json.final_client_name }},\n\nIt was great speaking with you! I really enjoyed learning more about your goals and the challenges you are working through.\n\nFollowing up on our conversation, I have put together a proposal that outlines how I can help. It covers the key points we discussed, along with a clear breakdown of what the engagement would look like.\n\nI have attached the proposal as a PDF for your review.\n\nPlease take your time going through it. If anything needs adjusting or if you have questions, I am happy to hop on a quick call or chat through it.\n\nLooking forward to hearing your thoughts!\n\nWarm regards",
"options": {
"attachmentsUi": {
"attachmentsBinary": [
{}
]
}
},
"subject": "={{ $('Prepare Email Data').item.json.final_proposal_title }}",
"emailType": "text"
},
"typeVersion": 2.1
},
{
"id": "node-21",
"name": "Confirm Email Sent",
"type": "n8n-nodes-base.telegram",
"position": [
4960,
-16
],
"parameters": {
"text": "=\u2705 *PROPOSAL SENT SUCCESSFULLY*\n\n*To:* {{ $('Prepare Email Data').item.json.final_client_name }} ({{ $('Prepare Email Data').item.json.final_client_email }})\n*Subject:* {{ $('Prepare Email Data').item.json.final_proposal_title }}\n\nThe proposal has been emailed to the client with the PDF attached.\n\n\ud83d\udcc4 [View Doc](https://docs.google.com/document/d/{{ $('Prepare Email Data').item.json.doc_id }})",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"typeVersion": 1.2
},
{
"id": "node-22",
"name": "Notify Proposal Rejected",
"type": "n8n-nodes-base.telegram",
"position": [
3616,
192
],
"parameters": {
"text": "=\u274c *PROPOSAL REJECTED*\n\n*Client:* {{ $('Prepare Proposal Data').item.json.CLIENT_NAME }}\n*Reason:* You chose to reject this proposal.\n\nThe proposal doc is still available if you want to edit and resend manually:\n\ud83d\udcc4 [View Doc](https://docs.google.com/document/d/{{ $('Copy Proposal Template').item.json.id }})",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"typeVersion": 1.2
},
{
"id": "node-23",
"name": "Notify No Proposal Needed",
"type": "n8n-nodes-base.telegram",
"position": [
1536,
288
],
"parameters": {
"text": "=\u2139\ufe0f *NO PROPOSAL NEEDED*\n\n*Meeting:* {{ $('Get Full Transcript').item.json.data?.transcript?.title || 'Unknown' }}\n*Reason:* {{ $json.reason }}\n\nNo action required.",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"typeVersion": 1.2
},
{
"id": "node-24",
"name": "Notify Transcript Error",
"type": "n8n-nodes-base.telegram",
"position": [
960,
384
],
"parameters": {
"text": "=\u26a0\ufe0f *TRANSCRIPT FETCH FAILED*\n\nFireflies webhook received but the transcript could not be retrieved.\n\nPayload received:\n```\n{{ JSON.stringify($('When Transcript Ready').item.json.body, null, 2).substring(0, 500) }}\n```\n\nPlease check the Fireflies API key and transcript ID.",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"typeVersion": 1.2
},
{
"id": "sticky-main",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
-288
],
"parameters": {
"color": "#646B0B",
"width": 620,
"height": 816,
"content": "## Fireflies Transcript to Auto-Proposal\n\nListens for Fireflies transcript webhooks, qualifies the call with AI, drafts a proposal, fills a Google Doc template, converts to PDF, and sends it via Gmail after you approve in Telegram.\n\n### How it works\n1. Fireflies webhook triggers when a meeting transcript is ready\n2. Fetches the full transcript via GraphQL API\n3. AI classifies the call and extracts client details, pain points, and scope\n4. If a proposal is needed, AI generates structured content for your template\n5. Google Doc template is copied and populated, then exported as PDF\n6. Telegram sends you a summary with Approve/Reject buttons\n7. On approval, the PDF is emailed to the client via Gmail\n\n### Setup\n- [ ] Add your Fireflies API key (HTTP Header Auth credential) and configure the webhook in Fireflies Settings > Developer > Webhooks\n- [ ] Connect your OpenAI API key to both AI nodes\n- [ ] Create a Google Doc template with placeholders: {{CLIENT_NAME}}, {{CLIENT_COMPANY}}, {{MEETING_DATE}}, {{MEETING_RECAP}}, {{UNDERSTANDING_OF_NEEDS}}, {{PROPOSED_SOLUTION}}, {{DELIVERABLES}}, {{TIMELINE}}, {{INVESTMENT}}, {{NEXT_STEPS}}\n- [ ] Set the Doc ID and Drive folder ID in the Google Drive nodes\n- [ ] Connect Gmail OAuth2 credential\n- [ ] Set your Telegram bot token and chat ID in all Telegram nodes\n\n### Customization\n- Edit the AI Qualify prompt to match your sales call criteria\n- Edit the AI Draft prompt to match your brand voice and proposal structure\n- Swap Telegram for Slack if preferred"
},
"typeVersion": 1
},
{
"id": "sticky-intake",
"name": "Sticky Note - Transcript Intake",
"type": "n8n-nodes-base.stickyNote",
"position": [
-48,
-32
],
"parameters": {
"color": 4,
"width": 844,
"height": 560,
"content": "**Transcript Intake**\nReceives Fireflies webhook, responds 200 OK, fetches full transcript via GraphQL, and validates the response."
},
"typeVersion": 1
},
{
"id": "sticky-qualify",
"name": "Sticky Note - AI Qualification",
"type": "n8n-nodes-base.stickyNote",
"position": [
800,
-32
],
"parameters": {
"color": 5,
"width": 616,
"height": 560,
"content": "**AI Qualification**\nGPT-4o classifies the call type and extracts client details. Non-proposal meetings get a Telegram notification."
},
"typeVersion": 1
},
{
"id": "sticky-draft",
"name": "Sticky Note - Proposal Draft",
"type": "n8n-nodes-base.stickyNote",
"position": [
1424,
-32
],
"parameters": {
"color": 6,
"width": 1452,
"height": 560,
"content": "**Proposal Drafting & Document Generation**\nAI generates structured proposal content, populates a Google Doc template copy, exports as PDF, and saves to Drive."
},
"typeVersion": 1
},
{
"id": "sticky-approval",
"name": "Sticky Note - Approval Flow",
"type": "n8n-nodes-base.stickyNote",
"position": [
2880,
-128
],
"parameters": {
"color": 2,
"width": 948,
"height": 560,
"content": "**Telegram Approval**\nSends a summary with Approve/Reject buttons. Pauses until you respond. Prompts for client email if missing."
},
"typeVersion": 1
},
{
"id": "sticky-delivery",
"name": "Sticky Note - Email Delivery",
"type": "n8n-nodes-base.stickyNote",
"position": [
3840,
-128
],
"parameters": {
"color": 7,
"width": 1332,
"height": 560,
"content": "**Email Delivery**\nDownloads the PDF from Drive, emails it to the client via Gmail, and confirms delivery in Telegram."
},
"typeVersion": 1
},
{
"id": "sticky-warning",
"name": "Sticky Note - Warning",
"type": "n8n-nodes-base.stickyNote",
"position": [
2016,
288
],
"parameters": {
"color": 3,
"width": 676,
"height": 220,
"content": "**Google Doc Template Required**\nThis workflow will fail without a template. Create a Google Doc with the following placeholders {{CLIENT_NAME}}, {{CLIENT_COMPANY}}, {{MEETING_DATE}}, {{MEETING_RECAP}}, {{UNDERSTANDING_OF_NEEDS}}, {{PROPOSED_SOLUTION}}, {{DELIVERABLES}}, {{TIMELINE}}, {{INVESTMENT}}, {{NEXT_STEPS}}, then paste the Doc ID into the Copy Proposal Template node."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"timezone": "UTC",
"binaryMode": "separate",
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false,
"executionOrder": "v1",
"executionTimeout": 3600,
"saveManualExecutions": true,
"saveExecutionProgress": true,
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all"
},
"versionId": "341dc4e5-4a30-491c-839d-b399e5e2055e",
"connections": {
"If Approved": {
"main": [
[
{
"node": "If Client Email Exists",
"type": "main",
"index": 0
}
],
[
{
"node": "Notify Proposal Rejected",
"type": "main",
"index": 0
}
]
]
},
"Respond 200 OK": {
"main": [
[
{
"node": "Get Full Transcript",
"type": "main",
"index": 0
}
]
]
},
"Export Doc as PDF": {
"main": [
[
{
"node": "Save PDF to Drive",
"type": "main",
"index": 0
}
]
]
},
"Save PDF to Drive": {
"main": [
[
{
"node": "Send Approval Request",
"type": "main",
"index": 0
}
]
]
},
"Wait for Approval": {
"main": [
[
{
"node": "If Approved",
"type": "main",
"index": 0
}
]
]
},
"If Proposal Needed": {
"main": [
[
{
"node": "Generate Proposal Content",
"type": "main",
"index": 0
}
],
[
{
"node": "Notify No Proposal Needed",
"type": "main",
"index": 0
}
]
]
},
"Prepare Email Data": {
"main": [
[
{
"node": "Download PDF from Drive",
"type": "main",
"index": 0
}
]
]
},
"Get Full Transcript": {
"main": [
[
{
"node": "If Transcript Exists",
"type": "main",
"index": 0
}
]
]
},
"Send Proposal Email": {
"main": [
[
{
"node": "Confirm Email Sent",
"type": "main",
"index": 0
}
]
]
},
"Ask for Client Email": {
"main": [
[
{
"node": "Wait for Email Reply",
"type": "main",
"index": 0
}
]
]
},
"If Transcript Exists": {
"main": [
[
{
"node": "Classify Meeting Type",
"type": "main",
"index": 0
}
],
[
{
"node": "Notify Transcript Error",
"type": "main",
"index": 0
}
]
]
},
"Wait for Email Reply": {
"main": [
[
{
"node": "Prepare Email Data",
"type": "main",
"index": 0
}
]
]
},
"Classify Meeting Type": {
"main": [
[
{
"node": "If Proposal Needed",
"type": "main",
"index": 0
}
]
]
},
"Prepare Proposal Data": {
"main": [
[
{
"node": "Copy Proposal Template",
"type": "main",
"index": 0
}
]
]
},
"Send Approval Request": {
"main": [
[
{
"node": "Wait for Approval",
"type": "main",
"index": 0
}
]
]
},
"When Transcript Ready": {
"main": [
[
{
"node": "Respond 200 OK",
"type": "main",
"index": 0
}
]
]
},
"Copy Proposal Template": {
"main": [
[
{
"node": "Fill Template Placeholders",
"type": "main",
"index": 0
}
]
]
},
"If Client Email Exists": {
"main": [
[
{
"node": "Prepare Email Data",
"type": "main",
"index": 0
}
],
[
{
"node": "Ask for Client Email",
"type": "main",
"index": 0
}
]
]
},
"Download PDF from Drive": {
"main": [
[
{
"node": "Send Proposal Email",
"type": "main",
"index": 0
}
]
]
},
"Generate Proposal Content": {
"main": [
[
{
"node": "Prepare Proposal Data",
"type": "main",
"index": 0
}
]
]
},
"Fill Template Placeholders": {
"main": [
[
{
"node": "Export Doc as PDF",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Listens for completed Fireflies transcripts, qualifies whether a proposal is needed using OpenAI, drafts structured proposal content, populates a Google Doc template, converts to PDF, and sends it to the client via Gmail after you approve it in Telegram.
Source: https://n8n.io/workflows/14717/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Streamlit Logibot. Uses notion, ollamaChatModel, lmChatOllama, googleDrive. Event-driven trigger; 29 nodes.
💥 Automate YouTube thumbnail creation from video links -vide. Uses telegramTrigger, httpRequest, googleDrive, gmail. Event-driven trigger; 25 nodes.
💥 Automate YouTube thumbnail creation from video links -vide. Uses telegramTrigger, httpRequest, googleDrive, gmail. Event-driven trigger; 25 nodes.
This system meticulously guides each lead through a fully automated journey, from initial contact to a personalized follow-up and CRM integration.
Stop applying manually. This workflow acts as your personal AI recruiter, automating the end-to-end process of finding high-quality jobs, tailoring your resume, and preparing personalized outreach ema