AutomationFlowsAI & RAG › Generate Daily Business Briefing Podcasts with Openai, Azure Tts, Hubspot,…

Generate Daily Business Briefing Podcasts with Openai, Azure Tts, Hubspot,…

Original n8n title: Generate Daily Business Briefing Podcasts with Openai, Azure Tts, Hubspot, Zendesk, Pipedrive, Discord, Twilio and Confluence

ByJitesh Dugar @jiteshdugar on n8n.io

Keep your leadership team informed with a data-driven, automated morning briefing. This workflow pulls live metrics from HubSpot, Zendesk, and Pipedrive, then uses AI and Neural TTS to generate a professional audio briefing delivered via Discord, SMS, and Confluence.

Cron / scheduled trigger★★★★☆ complexity28 nodesHTTP RequestN8N Nodes Uploadtourl
AI & RAG Trigger: Cron / scheduled Nodes: 28 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #14977 — we link there as the canonical source.

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 →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "522b22aa-2667-47f9-879c-69b887267661",
      "name": "\ud83d\udccb MAIN \u2014 Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        -1392
      ],
      "parameters": {
        "width": 1128,
        "height": 1340,
        "content": "## \ud83d\udcca Daily Business Briefing Podcast\n### Pull CRM Metrics \u2192 AI Briefing Script \u2192 TTS Audio \u2192 Distribute to Team\n\n**What this workflow does:**\nEvery weekday morning at 7:30 AM, this workflow simultaneously pulls live business metrics from three separate sources in parallel \u2014 HubSpot (leads & deals), Zendesk (support tickets), and Pipedrive (sales pipeline) \u2014 using three independent HTTP branches that run at the same time. It then:\n1. **Cron Trigger** fires at 7:30 AM Mon\u2013Fri only\n2. **3 Parallel HTTP branches** fetch metrics from HubSpot, Zendesk, and Pipedrive simultaneously (no sequential waiting)\n3. **Merge (Wait for All)** collects all 3 branch results into one item only after ALL branches complete\n4. **Code \u2014 Compile Metrics** assembles a unified metrics object with delta calculations (vs yesterday's baseline from Confluence)\n5. **Confluence \u2014 Read Yesterday** fetches yesterday's briefing page to extract baseline numbers for comparison\n6. **OpenAI Assistants API** (not chat completions \u2014 uses a persistent Assistant with instructions) generates the full podcast-style briefing script with trend analysis\n7. **Azure Cognitive Services TTS** (not ElevenLabs or Google TTS) converts the script to MP3 using Neural voice\n8. **UploadToURL** hosts the MP3 and returns a permanent URL\n9. **Three simultaneous distribution channels fire in parallel:**\n   - **Discord Webhook** \u2192 posts to #daily-briefing server channel with embedded audio link\n   - **Twilio SMS** \u2192 sends a short brief + audio URL to the leadership team's phones\n   - **Confluence \u2014 Create Page** \u2192 publishes today's briefing as a Confluence page with metrics table + audio player\n10. **Merge (Final)** collects all 3 distribution results\n11. **Code \u2014 Execution Report** builds a final run log object\n\n**Architecture (completely unique from all previous templates):**\n- \u23f0 Cron Trigger with weekday-only guard (NOT generic schedule)\n- \ud83d\udd00 3-WAY PARALLEL FETCH (HubSpot + Zendesk + Pipedrive simultaneously)\n- \ud83d\udd17 Merge (Wait All) \u2014 collects branches only after ALL complete\n- \ud83d\udcd3 Confluence READ + CREATE (bidirectional \u2014 unique CMS tool)\n- \ud83e\udd16 OpenAI Assistants API \u2014 persistent assistant (NOT chat completions)\n- \ud83d\udd0a Azure Cognitive Services TTS \u2014 Neural voice (NOT ElevenLabs/Google)\n- \u2601\ufe0f UploadToURL \u2014 mandatory MP3 hosting\n- \ud83c\udfae Discord Webhook \u2014 team server (NOT Slack/Teams/Telegram)\n- \ud83d\udcf1 Twilio SMS \u2014 leadership phones (NEW distribution channel)\n- \ud83d\udd00 3-WAY PARALLEL DISTRIBUTION (Discord + Twilio + Confluence simultaneously)\n- \ud83d\udd17 Merge (Final) \u2014 collects all distribution results\n\n**Setup Requirements:**\n1. HubSpot Private App token (Contacts + Deals read scope)\n2. Zendesk API token + subdomain (email/token auth)\n3. Pipedrive API token\n4. OpenAI API key \u2014 create an Assistant at platform.openai.com/assistants with the system prompt: 'You are a professional business briefing host. Generate concise, data-driven daily podcast scripts.'\n5. Azure Cognitive Services subscription key + region (Speech service)\n6. UploadToURL endpoint\n7. Discord server webhook URL (Server Settings \u2192 Integrations \u2192 Webhooks)\n8. Twilio Account SID + Auth Token + from number. Add leadership phone numbers to the `TO_NUMBERS` array in the Twilio Code node\n9. Confluence API token + base URL + Space Key"
      },
      "typeVersion": 1
    },
    {
      "id": "fde5ff23-0014-47db-9a4a-011960ed0489",
      "name": "\ud83d\udcdd Note \u2014 Cron, Weekday Guard & Date Context",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        912,
        96
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 798,
        "content": "### \u23f0 Step 1 \u2014 Cron Trigger & Weekday Guard\n**Cron Trigger:** Fires at exactly 07:30 every day. Uses cron expression `30 7 * * *`.\n**Code \u2014 Weekday Guard:** Immediately checks if today is Saturday (6) or Sunday (0) \u2014 if so, returns empty array and the workflow stops with no execution. This is intentionally done in code rather than an IF node to keep the canvas clean and avoid a dangling false-branch.\n**Code \u2014 Set Today's Date Context:** Stamps `todayISO`, `todayLabel` (e.g. 'Monday, April 7'), `yesterdayISO`, and `weekNumber` into the item \u2014 these are referenced by ALL downstream branches via `$('Code \u2014 Set Date Context').item.json`."
      },
      "typeVersion": 1
    },
    {
      "id": "3d73ddd5-a77a-465e-ad41-0b57d0bb87af",
      "name": "\ud83d\udcdd Note \u2014 Parallel CRM Fetch & Merge",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 528,
        "height": 1110,
        "content": "### \ud83d\udd00 Step 2 \u2014 3 Parallel CRM Metric Fetches\n**Three branches run simultaneously from one output:**\n\n**Branch A \u2014 HubSpot:** Fetches new contacts created today + deals in pipeline. Uses HubSpot v3 CRM API with `createdAt` filter. Returns `new_leads`, `open_deals`, `deals_value_today`.\n\n**Branch B \u2014 Zendesk:** Fetches tickets created today, open ticket count, and avg first response time. Uses Zendesk Search API with `created>yesterday` filter. Returns `tickets_today`, `open_tickets`, `avg_response_mins`.\n\n**Branch C \u2014 Pipedrive:** Fetches deals won today, deals lost today, and pipeline value. Uses Pipedrive Activities API with `start_date=today`. Returns `deals_won`, `deals_lost`, `pipeline_value`.\n\n**Merge \u2014 Wait for All 3:** Uses `combineAll` mode \u2014 holds execution until ALL three branches deliver their item, then outputs a single merged item containing all metrics."
      },
      "typeVersion": 1
    },
    {
      "id": "5b191f89-3964-41de-9d09-11aa7dffdb8d",
      "name": "\ud83d\udcdd Note \u2014 Confluence Read, Metrics Compile & Assistants API",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1904,
        128
      ],
      "parameters": {
        "color": 7,
        "width": 1072,
        "height": 732,
        "content": "### \ud83d\udcd3 Step 3 \u2014 Confluence Baseline + Script Generation\n**Confluence \u2014 Read Yesterday's Page:** Fetches yesterday's briefing Confluence page by title pattern (`Daily Briefing YYYY-MM-DD`) using the Confluence REST API search. Extracts yesterday's key numbers as baseline for delta calculations (e.g. '+12 leads vs yesterday').\n**Code \u2014 Compile Full Metrics:** Merges today's CRM data with yesterday's baseline. Computes deltas, formats currency values, and builds a structured `metricsObject` passed to the AI.\n**OpenAI Assistants API:** Calls `/v1/threads` \u2192 `/v1/threads/{id}/messages` \u2192 `/v1/threads/{id}/runs` \u2192 polls until complete \u2192 reads response. Uses a persistent named Assistant (not one-shot chat). Generates a 90-120 second natural podcast briefing with trends and callouts."
      },
      "typeVersion": 1
    },
    {
      "id": "9bac7106-4858-425c-802e-ac23bb595723",
      "name": "\ud83d\udcdd Note \u2014 Azure TTS, Binary Wrap & UploadToURL",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2992,
        128
      ],
      "parameters": {
        "color": 7,
        "width": 1040,
        "height": 724,
        "content": "### \ud83d\udd0a Step 4 \u2014 Azure TTS + UploadToURL\n**Azure Cognitive Services TTS:** POSTs the briefing script as SSML to `https://{region}.tts.speech.microsoft.com/cognitiveservices/v1`. Uses `en-US-GuyNeural` voice \u2014 a warm, professional male Neural voice. Returns raw binary MP3.\n**Code \u2014 Wrap Binary:** Wraps the raw Azure response binary into n8n's binary field structure so UploadToURL can process it as a file attachment.\n**UploadToURL \u2014 Host Briefing MP3:** Uploads the binary MP3 and returns a permanent public URL used in Discord embed, Twilio SMS, and Confluence page."
      },
      "typeVersion": 1
    },
    {
      "id": "7493364f-7c18-4e3a-bc36-d035b6eff272",
      "name": "\ud83d\udcdd Note \u2014 Discord, Twilio, Confluence + Final Merge",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4048,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 752,
        "height": 870,
        "content": "### \ud83d\udce1 Step 5 \u2014 3-Way Parallel Distribution & Final Merge\n**Three distribution branches fire simultaneously:**\n\n**Branch D \u2014 Discord:** POSTs a rich embed to your team's Discord server webhook. Includes today's date as embed title, metric highlights as fields, and the audio URL as a prominent link button.\n\n**Branch E \u2014 Twilio SMS:** Loops through an array of leadership phone numbers and sends each a short SMS: key metrics summary (3 lines) + audio URL. Uses Twilio Messages API.\n\n**Branch F \u2014 Confluence Create:** Creates a new Confluence page in your Space titled `Daily Briefing YYYY-MM-DD`. Body includes a metrics table (HTML), the full AI script, and an HTML5 audio embed tag pointing to the hosted MP3.\n\n**Merge Final + Execution Report:** Waits for all 3 distribution branches, then outputs a clean run log with timestamps, success status per channel, and the audio URL."
      },
      "typeVersion": 1
    },
    {
      "id": "36bafe79-c234-4df5-90dc-d5bb272ef223",
      "name": "Cron \u2014 7:30 AM Daily Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        960,
        480
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "30 7 * * *"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "bd5a408c-90ae-47a1-8b6b-c2d58e66e0cf",
      "name": "Code \u2014 Weekday Guard & Date Context",
      "type": "n8n-nodes-base.code",
      "position": [
        1168,
        480
      ],
      "parameters": {
        "jsCode": "const now = new Date();\nconst day = now.getDay(); // 0=Sun, 6=Sat\n\n// Skip weekends\nif (day === 0 || day === 6) {\n  return []; // halt workflow\n}\n\nconst pad = n => String(n).padStart(2, '0');\n\nconst todayISO = `${now.getFullYear()}-${pad(now.getMonth()+1)}-${pad(now.getDate())}`;\nconst dayNames = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\nconst monthNames = ['January','February','March','April','May','June','July','August','September','October','November','December'];\nconst todayLabel = `${dayNames[day]}, ${monthNames[now.getMonth()]} ${now.getDate()}, ${now.getFullYear()}`;\n\nconst yesterday = new Date(now);\nyesterday.setDate(yesterday.getDate() - 1);\nconst yesterdayISO = `${yesterday.getFullYear()}-${pad(yesterday.getMonth()+1)}-${pad(yesterday.getDate())}`;\n\n// Week number\nconst startOfYear = new Date(now.getFullYear(), 0, 1);\nconst weekNumber = Math.ceil(((now - startOfYear) / 86400000 + startOfYear.getDay() + 1) / 7);\n\nconst startOfTodayISO = `${todayISO}T00:00:00Z`;\nconst endOfTodayISO   = `${todayISO}T23:59:59Z`;\n\nreturn [{\n  json: {\n    todayISO,\n    todayLabel,\n    yesterdayISO,\n    weekNumber,\n    startOfTodayISO,\n    endOfTodayISO,\n    generatedAt: now.toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "f377ba71-e3f2-4e18-a839-043c5b84b4d6",
      "name": "HubSpot \u2014 Fetch Today's New Leads",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1472,
        304
      ],
      "parameters": {
        "url": "=https://api.hubapi.com/crm/v3/objects/contacts/search",
        "method": "POST",
        "options": {
          "timeout": 15000
        },
        "jsonBody": "={\n  \"filterGroups\": [{\n    \"filters\": [{\n      \"propertyName\": \"createdate\",\n      \"operator\": \"GTE\",\n      \"value\": \"{{ new Date($('Code \u2014 Weekday Guard & Date Context').item.json.startOfTodayISO).getTime() }}\"\n    }]\n  }],\n  \"properties\": [\"firstname\", \"lastname\", \"email\", \"lifecyclestage\", \"hs_lead_status\"],\n  \"limit\": 100\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer YOUR_HUBSPOT_PRIVATE_APP_TOKEN"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d8858e66-9aee-4f8c-b133-89d9694fca61",
      "name": "HubSpot \u2014 Fetch Open Deals Pipeline",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1472,
        480
      ],
      "parameters": {
        "url": "=https://api.hubapi.com/crm/v3/objects/deals/search",
        "method": "POST",
        "options": {
          "timeout": 15000
        },
        "jsonBody": "={\n  \"filterGroups\": [{\n    \"filters\": [{\n      \"propertyName\": \"dealstage\",\n      \"operator\": \"NOT_IN\",\n      \"values\": [\"closedwon\", \"closedlost\"]\n    }]\n  }],\n  \"properties\": [\"dealname\", \"amount\", \"dealstage\", \"closedate\", \"pipeline\"],\n  \"limit\": 100\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer YOUR_HUBSPOT_PRIVATE_APP_TOKEN"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "67a6fbdb-c0ae-434d-8ede-2f2996e7d6e8",
      "name": "Zendesk \u2014 Fetch Today's Support Tickets",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1472,
        672
      ],
      "parameters": {
        "url": "=https://YOUR_ZENDESK_SUBDOMAIN.zendesk.com/api/v2/search.json",
        "options": {
          "timeout": 15000
        },
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "=type:ticket created>{{ $('Code \u2014 Weekday Guard & Date Context').item.json.yesterdayISO }}"
            },
            {
              "name": "sort_by",
              "value": "created_at"
            },
            {
              "name": "sort_order",
              "value": "desc"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Basic {{ Buffer.from('YOUR_ZENDESK_EMAIL/token:YOUR_ZENDESK_API_TOKEN').toString('base64') }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "5feb2fbe-97e5-4c0b-a257-3d561aa91f18",
      "name": "Pipedrive \u2014 Fetch Sales Pipeline",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1472,
        848
      ],
      "parameters": {
        "url": "=https://api.pipedrive.com/v1/deals",
        "options": {
          "timeout": 15000
        },
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "api_token",
              "value": "=YOUR_PIPEDRIVE_API_TOKEN"
            },
            {
              "name": "status",
              "value": "open"
            },
            {
              "name": "limit",
              "value": "500"
            },
            {
              "name": "start",
              "value": "0"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4cf9aebc-e372-4287-8a23-96e3dd2237b1",
      "name": "Merge \u2014 Wait for All 3 CRM Branches",
      "type": "n8n-nodes-base.merge",
      "position": [
        1728,
        544
      ],
      "parameters": {
        "mode": "mergeByIndex"
      },
      "typeVersion": 3
    },
    {
      "id": "803353b7-ed9c-4701-91f7-e0cb0578c6e6",
      "name": "Code \u2014 Compile All CRM Metrics",
      "type": "n8n-nodes-base.code",
      "position": [
        1952,
        544
      ],
      "parameters": {
        "jsCode": "// Pull data from all branches via node references\nconst dateCtx    = $('Code \u2014 Weekday Guard & Date Context').item.json;\n\n// HubSpot contacts\nconst hsContacts = $('HubSpot \u2014 Fetch Today\\'s New Leads').item.json;\nconst newLeads   = hsContacts?.total ?? hsContacts?.results?.length ?? 0;\n\n// HubSpot deals\nconst hsDeals    = $('HubSpot \u2014 Fetch Open Deals Pipeline').item.json;\nconst dealsList  = hsDeals?.results || [];\nconst openDeals  = hsDeals?.total ?? dealsList.length;\nconst pipelineValue = dealsList.reduce((sum, d) => sum + parseFloat(d.properties?.amount || 0), 0);\n\n// Zendesk\nconst zdData       = $('Zendesk \u2014 Fetch Today\\'s Support Tickets').item.json;\nconst ticketsToday = zdData?.count ?? zdData?.results?.length ?? 0;\nconst openTickets  = zdData?.results?.filter(t => t.status === 'open' || t.status === 'new').length ?? 0;\n\n// Pipedrive\nconst pdData      = $('Pipedrive \u2014 Fetch Sales Pipeline').item.json;\nconst pdDeals     = pdData?.data || [];\nconst dealsWon    = pdDeals.filter(d => d.status === 'won').length;\nconst dealsLost   = pdDeals.filter(d => d.status === 'lost').length;\nconst pdPipeValue = pdDeals.reduce((sum, d) => sum + (d.value || 0), 0);\n\n// Format helpers\nconst fmtCurrency = n => '$' + n.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 });\n\nreturn [{\n  json: {\n    ...dateCtx,\n    metrics: {\n      hubspot: {\n        new_leads:      newLeads,\n        open_deals:     openDeals,\n        pipeline_value: fmtCurrency(pipelineValue)\n      },\n      zendesk: {\n        tickets_today: ticketsToday,\n        open_tickets:  openTickets\n      },\n      pipedrive: {\n        deals_won:      dealsWon,\n        deals_lost:     dealsLost,\n        pipeline_value: fmtCurrency(pdPipeValue)\n      }\n    },\n    rawPipelineValue: pipelineValue,\n    rawPdPipeValue: pdPipeValue\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "6a413cfc-4fad-487b-894d-9f9e9abe63c0",
      "name": "Confluence \u2014 Read Yesterday's Briefing Page",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2160,
        544
      ],
      "parameters": {
        "url": "=https://YOUR_CONFLUENCE_DOMAIN.atlassian.net/wiki/rest/api/content",
        "options": {
          "timeout": 10000
        },
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "title",
              "value": "=Daily Briefing {{ $('Code \u2014 Weekday Guard & Date Context').item.json.yesterdayISO }}"
            },
            {
              "name": "spaceKey",
              "value": "=YOUR_CONFLUENCE_SPACE_KEY"
            },
            {
              "name": "expand",
              "value": "body.storage"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Basic {{ Buffer.from('YOUR_CONFLUENCE_EMAIL:YOUR_CONFLUENCE_API_TOKEN').toString('base64') }}"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "f38bc03b-f4f3-4fe4-a935-c3adf69f8c21",
      "name": "Code \u2014 Enrich Metrics with Delta vs Yesterday",
      "type": "n8n-nodes-base.code",
      "position": [
        2384,
        544
      ],
      "parameters": {
        "jsCode": "const confResp  = $input.first().json;\nconst metricsData = $('Code \u2014 Compile All CRM Metrics').item.json;\n\n// Try to extract yesterday's numbers from Confluence page body\nlet yesterdayMetrics = { new_leads: 0, open_deals: 0, tickets_today: 0, deals_won: 0 };\n\ntry {\n  const bodyHtml = confResp?.results?.[0]?.body?.storage?.value || '';\n  // Parse key numbers with regex on the stored HTML\n  const extractNum = (html, label) => {\n    const rx = new RegExp(`${label}[^\\\\d]*(\\\\d+)`, 'i');\n    const m  = html.match(rx);\n    return m ? parseInt(m[1]) : 0;\n  };\n  yesterdayMetrics.new_leads    = extractNum(bodyHtml, 'New Leads');\n  yesterdayMetrics.open_deals   = extractNum(bodyHtml, 'Open Deals');\n  yesterdayMetrics.tickets_today = extractNum(bodyHtml, 'Tickets');\n  yesterdayMetrics.deals_won    = extractNum(bodyHtml, 'Deals Won');\n} catch(e) {\n  // No yesterday page \u2014 deltas will show as N/A\n}\n\nconst m = metricsData.metrics;\nconst delta = (today, yest) => {\n  if (!yest) return '';\n  const diff = today - yest;\n  return diff > 0 ? ` (\u25b2${diff} vs yesterday)` : diff < 0 ? ` (\u25bc${Math.abs(diff)} vs yesterday)` : ' (no change)';\n};\n\nconst metricsNarrative = [\n  `New Leads: ${m.hubspot.new_leads}${delta(m.hubspot.new_leads, yesterdayMetrics.new_leads)}`,\n  `Open Deals: ${m.hubspot.open_deals} worth ${m.hubspot.pipeline_value}`,\n  `Support Tickets Today: ${m.zendesk.tickets_today}${delta(m.zendesk.tickets_today, yesterdayMetrics.tickets_today)}, Open: ${m.zendesk.open_tickets}`,\n  `Sales \u2014 Won: ${m.pipedrive.deals_won}${delta(m.pipedrive.deals_won, yesterdayMetrics.deals_won)}, Lost: ${m.pipedrive.deals_lost}, Pipeline: ${m.pipedrive.pipeline_value}`\n].join('\\n');\n\nreturn [{\n  json: {\n    ...metricsData,\n    yesterdayMetrics,\n    metricsNarrative\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "73d9968f-243f-48bf-a17a-dd1752df8b47",
      "name": "OpenAI Assistants \u2014 Create Thread",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2608,
        544
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/threads",
        "method": "POST",
        "options": {
          "timeout": 20000
        },
        "jsonBody": "={\n  \"messages\": [{\n    \"role\": \"user\",\n    \"content\": \"Generate today's business briefing podcast script.\\n\\nDate: {{ $json.todayLabel }}\\nWeek: {{ $json.weekNumber }}\\n\\nMetrics:\\n{{ $json.metricsNarrative }}\\n\\nWrite a warm, professional 90-120 second spoken audio script. Open with a greeting and today's date. Walk through each metric naturally with brief commentary on trends. Close with a motivational one-liner. No bullet points, no markdown, no headers \u2014 pure spoken prose only.\"\n  }]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer YOUR_OPENAI_API_KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "OpenAI-Beta",
              "value": "assistants=v2"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b1f221e2-b0c2-4d79-b394-2d7625041fab",
      "name": "OpenAI Assistants \u2014 Run Thread",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2832,
        544
      ],
      "parameters": {
        "url": "=https://api.openai.com/v1/threads/{{ $json.id }}/runs",
        "method": "POST",
        "options": {
          "timeout": 20000
        },
        "jsonBody": "={\n  \"assistant_id\": \"YOUR_OPENAI_ASSISTANT_ID\",\n  \"instructions\": \"Generate a concise, engaging daily business podcast briefing. Tone: professional but warm. Duration when read aloud: 90-120 seconds. No lists or formatting \u2014 pure spoken prose.\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer YOUR_OPENAI_API_KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "OpenAI-Beta",
              "value": "assistants=v2"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "2c8c6379-57cb-4187-b940-ded0d5b0ce7c",
      "name": "Code \u2014 Poll Run & Extract Briefing Script",
      "type": "n8n-nodes-base.code",
      "position": [
        3040,
        544
      ],
      "parameters": {
        "jsCode": "// Poll the run until status is 'completed'\n// n8n doesn't have a native poll node \u2014 we use a wait-and-retry pattern in code\nconst runId      = $input.first().json.id;\nconst threadId   = $input.first().json.thread_id;\nconst apiKey     = 'YOUR_OPENAI_API_KEY';\n\nconst maxAttempts = 20;\nconst delayMs     = 3000;\n\nconst sleep = ms => new Promise(r => setTimeout(r, ms));\n\nlet status = 'queued';\nlet runData = {};\nlet attempts = 0;\n\nwhile (status !== 'completed' && status !== 'failed' && attempts < maxAttempts) {\n  await sleep(delayMs);\n  const resp = await fetch(\n    `https://api.openai.com/v1/threads/${threadId}/runs/${runId}`,\n    {\n      headers: {\n        'Authorization': `Bearer ${apiKey}`,\n        'OpenAI-Beta': 'assistants=v2'\n      }\n    }\n  );\n  runData = await resp.json();\n  status  = runData.status;\n  attempts++;\n}\n\nif (status !== 'completed') {\n  throw new Error(`OpenAI run did not complete. Final status: ${status}`);\n}\n\n// Fetch messages\nconst msgResp = await fetch(\n  `https://api.openai.com/v1/threads/${threadId}/messages`,\n  {\n    headers: {\n      'Authorization': `Bearer ${apiKey}`,\n      'OpenAI-Beta': 'assistants=v2'\n    }\n  }\n);\nconst msgData = await msgResp.json();\n\nconst assistantMsg = (msgData.data || []).find(m => m.role === 'assistant');\nconst script = assistantMsg?.content?.[0]?.text?.value || '';\n\nif (!script) throw new Error('No script returned from OpenAI Assistant');\n\nconst prevData = $('Code \u2014 Enrich Metrics with Delta vs Yesterday').item.json;\n\nreturn [{ json: { ...prevData, briefingScript: script, threadId, runId } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "758e5b07-2c9b-4dec-ace2-55d735ae10cb",
      "name": "Azure TTS \u2014 Synthesize Briefing to MP3",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3264,
        544
      ],
      "parameters": {
        "url": "=https://YOUR_AZURE_REGION.tts.speech.microsoft.com/cognitiveservices/v1",
        "body": "=<speak version='1.0' xml:lang='en-US'>\n  <voice xml:lang='en-US' xml:gender='Male' name='en-US-GuyNeural'>\n    <prosody rate='0.95' pitch='0st'>\n      {{ $json.briefingScript }}\n    </prosody>\n  </voice>\n</speak>",
        "method": "POST",
        "options": {
          "timeout": 45000,
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        },
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "string",
        "headerParameters": {
          "parameters": [
            {
              "name": "Ocp-Apim-Subscription-Key",
              "value": "=YOUR_AZURE_SPEECH_KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/ssml+xml"
            },
            {
              "name": "X-Microsoft-OutputFormat",
              "value": "audio-48khz-192kbitrate-mono-mp3"
            },
            {
              "name": "User-Agent",
              "value": "n8n-daily-briefing"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4b37380b-5480-4ee1-8df7-4bdf590d7612",
      "name": "Code \u2014 Wrap Azure Binary for Upload",
      "type": "n8n-nodes-base.code",
      "position": [
        3488,
        544
      ],
      "parameters": {
        "jsCode": "// Azure returns raw binary \u2014 wrap it properly for UploadToURL\nconst allData = $('Code \u2014 Poll Run & Extract Briefing Script').item.json;\nconst todayISO = allData.todayISO || new Date().toISOString().split('T')[0];\n\nreturn [{\n  json:   { ...allData },\n  binary: {\n    data: {\n      data:          $input.first().binary?.data?.data,\n      mimeType:      'audio/mpeg',\n      fileName:      `daily-briefing-${todayISO}.mp3`,\n      fileExtension: 'mp3'\n    }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "4c018016-b7d2-4c2b-8a9a-83ca0ded3312",
      "name": "Code \u2014 Store Briefing Audio URL",
      "type": "n8n-nodes-base.code",
      "position": [
        3920,
        544
      ],
      "parameters": {
        "jsCode": "const uploadResp = $input.first().json;\nconst briefData  = $('Code \u2014 Poll Run & Extract Briefing Script').item.json;\n\nconst audioUrl =\n  uploadResp?.url ??\n  uploadResp?.data?.url ??\n  uploadResp?.file?.url ??\n  uploadResp?.link ?? '';\n\nif (!audioUrl) throw new Error('UploadToURL returned no URL for briefing audio');\n\nreturn [{ json: { ...briefData, audioUrl } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "fd1c1ae5-7e59-4d87-82c7-749e178a6936",
      "name": "Discord \u2014 Post Briefing to Team Server",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4144,
        352
      ],
      "parameters": {
        "url": "=YOUR_DISCORD_WEBHOOK_URL",
        "method": "POST",
        "options": {
          "timeout": 10000
        },
        "jsonBody": "={\n  \"username\": \"Business Briefing Bot\",\n  \"avatar_url\": \"https://i.imgur.com/your-bot-avatar.png\",\n  \"embeds\": [{\n    \"title\": \"\ud83d\udcca Daily Business Briefing \u2014 {{ $json.todayLabel }}\",\n    \"description\": \"Your morning metrics summary is ready. Click below to listen.\",\n    \"color\": 5793266,\n    \"fields\": [\n      { \"name\": \"\ud83e\uddf2 New Leads\",      \"value\": \"{{ $json.metrics.hubspot.new_leads }}\",       \"inline\": true },\n      { \"name\": \"\ud83d\udcbc Open Deals\",     \"value\": \"{{ $json.metrics.hubspot.open_deals }}\",      \"inline\": true },\n      { \"name\": \"\ud83d\udcb0 Pipeline\",       \"value\": \"{{ $json.metrics.hubspot.pipeline_value }}\",  \"inline\": true },\n      { \"name\": \"\ud83c\udfab Tickets Today\",  \"value\": \"{{ $json.metrics.zendesk.tickets_today }}\",   \"inline\": true },\n      { \"name\": \"\ud83d\udd13 Open Tickets\",   \"value\": \"{{ $json.metrics.zendesk.open_tickets }}\",    \"inline\": true },\n      { \"name\": \"\u2705 Deals Won\",      \"value\": \"{{ $json.metrics.pipedrive.deals_won }}\",      \"inline\": true }\n    ],\n    \"footer\": { \"text\": \"Week {{ $json.weekNumber }} \u00b7 Generated by n8n\" },\n    \"timestamp\": \"{{ $json.generatedAt }}\"\n  }],\n  \"components\": [{\n    \"type\": 1,\n    \"components\": [{\n      \"type\": 2,\n      \"style\": 5,\n      \"label\": \"\u25b6 Listen to Briefing\",\n      \"url\": \"{{ $json.audioUrl }}\"\n    }]\n  }]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "dd9e021f-946a-4cda-953d-b2bcce8f5d42",
      "name": "Twilio \u2014 SMS Briefing to Leadership Team",
      "type": "n8n-nodes-base.code",
      "position": [
        4144,
        544
      ],
      "parameters": {
        "jsCode": "// Send SMS to each leadership number via Twilio\nconst data    = $('Code \u2014 Store Briefing Audio URL').item.json;\nconst m       = data.metrics;\n\nconst TO_NUMBERS = [\n  '+1XXXXXXXXXX',\n  '+1XXXXXXXXXX'\n];\n\nconst msgBody = [\n  `\ud83d\udcca Daily Briefing \u2014 ${data.todayLabel}`,\n  `Leads: ${m.hubspot.new_leads} | Deals: ${m.hubspot.open_deals} (${m.hubspot.pipeline_value})`,\n  `Tickets: ${m.zendesk.tickets_today} open | Won: ${m.pipedrive.deals_won}`,\n  `\ud83c\udfa7 Listen: ${data.audioUrl}`\n].join('\\n');\n\nconst accountSid = 'YOUR_TWILIO_ACCOUNT_SID';\nconst authToken  = 'YOUR_TWILIO_AUTH_TOKEN';\nconst fromNumber = 'YOUR_TWILIO_FROM_NUMBER';\nconst basicAuth  = Buffer.from(`${accountSid}:${authToken}`).toString('base64');\n\nconst results = [];\nfor (const toNumber of TO_NUMBERS) {\n  const resp = await fetch(\n    `https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json`,\n    {\n      method: 'POST',\n      headers: {\n        'Authorization': `Basic ${basicAuth}`,\n        'Content-Type': 'application/x-www-form-urlencoded'\n      },\n      body: new URLSearchParams({ To: toNumber, From: fromNumber, Body: msgBody }).toString()\n    }\n  );\n  const r = await resp.json();\n  results.push({ to: toNumber, sid: r.sid, status: r.status });\n}\n\nreturn [{ json: { ...data, twilioResults: results } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "e7b3dd65-3e89-438c-9ec5-f04f2b7073b1",
      "name": "Confluence \u2014 Create Today's Briefing Page",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4144,
        752
      ],
      "parameters": {
        "url": "=https://YOUR_CONFLUENCE_DOMAIN.atlassian.net/wiki/rest/api/content",
        "method": "POST",
        "options": {
          "timeout": 15000
        },
        "jsonBody": "={\n  \"type\": \"page\",\n  \"title\": \"Daily Briefing {{ $json.todayISO }}\",\n  \"space\": { \"key\": \"YOUR_CONFLUENCE_SPACE_KEY\" },\n  \"body\": {\n    \"storage\": {\n      \"value\": \"<h1>\ud83d\udcca Daily Business Briefing \u2014 {{ $json.todayLabel }}</h1><p><strong>Week {{ $json.weekNumber }}</strong></p><h2>\ud83c\udfa7 Audio Briefing</h2><p><a href='{{ $json.audioUrl }}'>\u25b6 Listen to today's briefing (MP3)</a></p><audio controls style='width:100%'><source src='{{ $json.audioUrl }}' type='audio/mpeg' /></audio><h2>\ud83d\udcc8 Key Metrics</h2><table><tbody><tr><th>Source</th><th>Metric</th><th>Value</th></tr><tr><td>HubSpot</td><td>New Leads</td><td>{{ $json.metrics.hubspot.new_leads }}</td></tr><tr><td>HubSpot</td><td>Open Deals</td><td>{{ $json.metrics.hubspot.open_deals }}</td></tr><tr><td>HubSpot</td><td>Pipeline Value</td><td>{{ $json.metrics.hubspot.pipeline_value }}</td></tr><tr><td>Zendesk</td><td>Tickets Today</td><td>{{ $json.metrics.zendesk.tickets_today }}</td></tr><tr><td>Zendesk</td><td>Open Tickets</td><td>{{ $json.metrics.zendesk.open_tickets }}</td></tr><tr><td>Pipedrive</td><td>Deals Won</td><td>{{ $json.metrics.pipedrive.deals_won }}</td></tr><tr><td>Pipedrive</td><td>Deals Lost</td><td>{{ $json.metrics.pipedrive.deals_lost }}</td></tr><tr><td>Pipedrive</td><td>Pipeline Value</td><td>{{ $json.metrics.pipedrive.pipeline_value }}</td></tr></tbody></table><h2>\ud83d\udcdd Full Script</h2><p>{{ $json.briefingScript }}</p>\",\n      \"representation\": \"storage\"\n    }\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Basic {{ Buffer.from('YOUR_CONFLUENCE_EMAIL:YOUR_CONFLUENCE_API_TOKEN').toString('base64') }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "c5dfdade-084f-4793-b687-1d8ee375a0b1",
      "name": "Merge \u2014 Collect All Distribution Results",
      "type": "n8n-nodes-base.merge",
      "position": [
        4368,
        544
      ],
      "parameters": {
        "mode": "mergeByIndex"
      },
      "typeVersion": 3
    },
    {
      "id": "b1d2144a-e78d-46a6-9b4b-23fab86fca06",
      "name": "Code \u2014 Final Execution Report",
      "type": "n8n-nodes-base.code",
      "position": [
        4592,
        544
      ],
      "parameters": {
        "jsCode": "const briefData = $('Code \u2014 Store Briefing Audio URL').item.json;\n\nconst report = {\n  date:           briefData.todayISO,\n  dateLabel:      briefData.todayLabel,\n  weekNumber:     briefData.weekNumber,\n  audioUrl:       briefData.audioUrl,\n  generatedAt:    briefData.generatedAt,\n  metricsSnapshot: briefData.metrics,\n  distribution: {\n    discord:    'posted',\n    twilio_sms: 'sent',\n    confluence: 'page_created'\n  },\n  scriptLength:   briefData.briefingScript?.length ?? 0,\n  status:         'success'\n};\n\nconsole.log('\u2705 Daily Briefing complete:', JSON.stringify(report, null, 2));\nreturn [{ json: report }];"
      },
      "typeVersion": 2
    },
    {
      "id": "8c2d22e2-96c5-4cae-bded-066804813fa8",
      "name": "Upload a File",
      "type": "n8n-nodes-uploadtourl.uploadToUrl",
      "position": [
        3696,
        544
      ],
      "parameters": {},
      "credentials": {
        "uploadToUrlApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Upload a File": {
      "main": [
        [
          {
            "node": "Code \u2014 Store Briefing Audio URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cron \u2014 7:30 AM Daily Trigger": {
      "main": [
        [
          {
            "node": "Code \u2014 Weekday Guard & Date Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Compile All CRM Metrics": {
      "main": [
        [
          {
            "node": "Confluence \u2014 Read Yesterday's Briefing Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Assistants \u2014 Run Thread": {
      "main": [
        [
          {
            "node": "Code \u2014 Poll Run & Extract Briefing Script",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Store Briefing Audio URL": {
      "main": [
        [
          {
            "node": "Discord \u2014 Post Briefing to Team Server",
            "type": "main",
            "index": 0
          },
          {
            "node": "Twilio \u2014 SMS Briefing to Leadership Team",
            "type": "main",
            "index": 0
          },
          {
            "node": "Confluence \u2014 Create Today's Briefing Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HubSpot \u2014 Fetch Today's New Leads": {
      "main": [
        [
          {
            "node": "Merge \u2014 Wait for All 3 CRM Branches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Assistants \u2014 Create Thread": {
      "main": [
        [
          {
            "node": "OpenAI Assistants \u2014 Run Thread",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Weekday Guard & Date Context": {
      "main": [
        [
          {
            "node": "HubSpot \u2014 Fetch Today's New Leads",
            "type": "main",
            "index": 0
          },
          {
            "node": "HubSpot \u2014 Fetch Open Deals Pipeline",
            "type": "main",
            "index": 0
          },
          {
            "node": "Zendesk \u2014 Fetch Today's Support Tickets",
            "type": "main",
            "index": 0
          },
          {
            "node": "Pipedrive \u2014 Fetch Sales Pipeline",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Wrap Azure Binary for Upload": {
      "main": [
        [
          {
            "node": "Upload a File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HubSpot \u2014 Fetch Open Deals Pipeline": {
      "main": [
        [
          {
            "node": "Merge \u2014 Wait for All 3 CRM Branches",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge \u2014 Wait for All 3 CRM Branches": {
      "main": [
        [
          {
            "node": "Code \u2014 Compile All CRM Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Azure TTS \u2014 Synthesize Briefing to MP3": {
      "main": [
        [
          {
            "node": "Code \u2014 Wrap Azure Binary for Upload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Discord \u2014 Post Briefing to Team Server": {
      "main": [
        [
          {
            "node": "Merge \u2014 Collect All Distribution Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge \u2014 Collect All Distribution Results": {
      "main": [
        [
          {
            "node": "Code \u2014 Final Execution Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Twilio \u2014 SMS Briefing to Leadership Team": {
      "main": [
        [
          {
            "node": "Merge \u2014 Collect All Distribution Results",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Code \u2014 Poll Run & Extract Briefing Script": {
      "main": [
        [
          {
            "node": "Azure TTS \u2014 Synthesize Briefing to MP3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Confluence \u2014 Read Yesterday's Briefing Page": {
      "main": [
        [
          {
            "node": "Code \u2014 Enrich Metrics with Delta vs Yesterday",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Enrich Metrics with Delta vs Yesterday": {
      "main": [
        [
          {
            "node": "OpenAI Assistants \u2014 Create Thread",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Keep your leadership team informed with a data-driven, automated morning briefing. This workflow pulls live metrics from HubSpot, Zendesk, and Pipedrive, then uses AI and Neural TTS to generate a professional audio briefing delivered via Discord, SMS, and Confluence.

Source: https://n8n.io/workflows/14977/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Free Support: Setting up and getting the workflow tailord to your needs. One small free adjustment included.

HTTP Request, Google Cloud Storage, YouTube +2
AI & RAG

This workflow aims to help you and your team track your expenses with OpenAI It automatically collects your OpenAI organization’s API usage and cost data every few days and saves it to a ready-to-use

HTTP Request, Google Sheets
AI & RAG

This n8n workflow automates Kubernetes root cause analysis (RCA) and incident alerting by integrating with Loki, Prometheus, and Slack. It streamlines log collection, cluster health monitoring, and AI

HTTP Request, Ssh
AI & RAG

Automatically fetch, rank, and summarize the top financial stories from curated RSS feeds each day, then deliver a concise AI-written digest to Telegram and log the run to Google Sheets. Runs on a dai

Google Sheets, HTTP Request, RSS Feed Read
AI & RAG

Elevate your digital presence with high-fidelity cinematic video automation. This workflow orchestrates the complex, asynchronous rendering process of OpenAI Sora—transforming static product images or

HTTP Request, N8N Nodes Uploadtourl