{
  "name": "Score podcast guest leads and draft pitch emails with Apify, OpenAI, Google Sheets and Gmail",
  "nodes": [
    {
      "parameters": {
        "content": "## Score podcast guest leads against your expertise, then draft the pitch\n\nBeing a podcast guest is the cheapest distribution a founder, author or consultant has. The work\nis not the interview, it is the booking: finding shows that actually take guests, that are still\npublishing, that have a reachable host, and then writing a pitch that reads as though you listened\nfirst. That is an afternoon a week, and most of it is reading shows that were never a fit.\n\nThis workflow does the reading. Apify searches Apple Podcasts for your niche, keeps only active\nshows with interview signals and a real contact address in the RSS feed, and remembers every show\nit has already handed you. Then n8n's own OpenAI node scores each show against your written\ntalking points, says in one phrase where the overlap is, and names the episode you could deliver.\nOnly the shows above your fit score get a pitch written, and each pitch lands in your Gmail drafts\nfolder, addressed to the host, ready for you to read and send.\n\nEvery show that was scored is written to Google Sheets, including the ones it turned down and why,\nso the list is a record of who you have already looked at.\n\n**Read every draft before you send it.** The prompt forbids inventing numbers and only quotes the\nproof points you wrote, but a model can still phrase a claim more strongly than you would.\n\n### What you need\n- An [Apify account](https://apify.com/?fpr=youssef) and the\n  [Podcast Guest Finder Actor](https://apify.com/fayoussef/podcast-guest-finder?fpr=youssef), which\n  reads the Apple Podcasts index and every show's RSS feed for the host's email.\n- An OpenAI API key for the two AI steps.\n- Google Sheets, for the record of every show scored.\n- Gmail, where the drafts are created. Nothing is ever sent by the workflow.\n- Telegram, so you know drafts are waiting.\n",
        "height": 2000,
        "width": 520,
        "color": 1
      },
      "id": "pg44c901-0000-4000-8000-000000000100",
      "name": "Sticky Note - About",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -760,
        -240
      ]
    },
    {
      "parameters": {
        "content": "### 1. Who you are and what you can talk about\nEverything downstream is built from this one node, and the quality of the pitch is decided here.\n\n`niches`: the subjects your ideal show covers, comma separated. These are searched on Apple\nPodcasts, so write them as a listener would (`B2B SaaS`, `bootstrapped founders`), not as a job\ntitle.\n\n`talkingPoints`: three or four things you can genuinely carry a 40 minute conversation about,\nseparated by semicolons. The scorer matches these against the show, and the pitch is built only\nfrom these, so a vague line here produces a vague pitch.\n\n`proofPoints`: the facts you are allowed to claim. Numbers, years, company names, titles. The\nprompt may quote these verbatim and may not invent anything beyond them, so leave out anything you\nwould not want in a cold email.\n\n`minFit` 60 and `maxPitches` 5 are the two taps: how good a match has to be before it is worth an\nemail, and how many drafts you want to read in one sitting.\n",
        "height": 760,
        "width": 520,
        "color": 7
      },
      "id": "pg44c901-0000-4000-8000-000000000101",
      "name": "Sticky Note - Step 1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -200,
        -240
      ]
    },
    {
      "parameters": {
        "content": "### 2. Find shows that actually take guests\nThe Actor searches each of your niches across the Apple Podcasts index, then opens every show's\nRSS feed to pull the owner email, the last episode date and the show's own description.\n\nThree filters are on by default, and they are the reason this list is short and worth reading:\na contact email must exist, the show must have published inside `activeWithinDays`, and the show's\nown words must signal an interview format.\n\n`onlyNewPodcasts` is on, so the Actor remembers every show it has already returned to you. A weekly\nrun brings you shows you have not seen, not the same fifty every Monday. **Remove Duplicates** sits\nbehind it as a second guard, keyed on the Apple show id, which is what makes a repeat run cost\nnothing in AI tokens.\n",
        "height": 760,
        "width": 580,
        "color": 7
      },
      "id": "pg44c901-0000-4000-8000-000000000102",
      "name": "Sticky Note - Step 2",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        360,
        -240
      ]
    },
    {
      "parameters": {
        "content": "### 3. Score the fit, and name the episode\nThe model sees your talking points and the show's subject, categories, cadence and interview\nsignals, and returns four things: a fit score, one phrase saying where the overlap is, the title of\nthe episode you could deliver, and the one word from your talking point that appears in the show's\nown title.\n\nThat last field is the honesty check, and n8n does the arithmetic rather than the model: if the\nword is not really in the title, the score is **capped at 70** by the Set node, whatever the model\nclaimed. A show has to name your subject in its own words to score 80. Asking a model to apply its\nown cap does not work, so it is only asked to report the word, and the `Title match` column shows\nyou which shows earned their score.\n\nIt is also told that it cannot see audience size, that a shared category is a hint rather than a\nfit, and that it must not invent a case study or a number you did not write. When nothing fits, it\nsays what the mismatch is, which is why a 20 is as useful as an 80: you can see that a search term\nis pulling the wrong kind of show and fix the term, not the pitch.\n\n`gpt-4o-mini` at temperature 0.3 is enough here. Swap the model node for Anthropic, Gemini or a\nlocal model and the rest of the workflow is unchanged.\n",
        "height": 760,
        "width": 880,
        "color": 7
      },
      "id": "pg44c901-0000-4000-8000-000000000103",
      "name": "Sticky Note - Step 3",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        960,
        -240
      ]
    },
    {
      "parameters": {
        "content": "### 4. Every show, including the rejects\n`Save every show` appends one row per show per run to the `showsTab` tab: the show, the host, the\nemail, the fit score, the phrase explaining it, the proposed episode, the cadence, and the date.\n\nThat tab is the deliverable even before you open Gmail. It is the list you check before pitching\nanything by hand, and the rejected rows are the useful half: they tell you which niche keywords\nbring back the wrong shows.\n\nOnly rows at or above `minFit` that also carry an email continue to the pitch, capped by\n`maxPitches`, so the AI spend per run is bounded no matter how many shows the search returns.\n",
        "height": 760,
        "width": 580,
        "color": 7
      },
      "id": "pg44c901-0000-4000-8000-000000000104",
      "name": "Sticky Note - Step 4",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1860,
        -240
      ]
    },
    {
      "parameters": {
        "content": "### 5. A draft in your Gmail, not a notification\nThe second chain writes the real email: a subject line that names the episode idea, one line on\nwhy this show, the episode proposal, three takeaways taken from your talking points, one line of\ncredentials quoted from your proof points, and a closing question. Nothing about how much you love\nthe show.\n\nTwo things then happen in parallel. Telegram gets the full pitch text with the show, the fit score\nand the reason, so you can judge it on your phone. `Create the Gmail draft` puts the same email in\nyour Gmail drafts folder, addressed to the host. **The workflow never sends anything.** You open\nGmail, change the line that does not sound like you, and press send.\n\nTelegram comes first on purpose: if you have not connected Gmail yet, you still get every pitch in\nfull, and you can delete the Gmail node and lose nothing but the drafts folder.\n",
        "height": 760,
        "width": 660,
        "color": 7
      },
      "id": "pg44c901-0000-4000-8000-000000000105",
      "name": "Sticky Note - Step 5",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2460,
        -240
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "weeksInterval": 1,
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 7
            }
          ]
        }
      },
      "id": "pg44c901-0000-4000-8000-000000000501",
      "name": "Every Monday at 7am",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -120,
        120
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "pg44c901-0000-4000-8000-000000000200",
              "name": "niches",
              "value": "B2B SaaS, bootstrapped founders, product-led growth",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000201",
              "name": "country",
              "value": "US",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000202",
              "name": "activeWithinDays",
              "value": 90,
              "type": "number"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000203",
              "name": "maxShows",
              "value": 25,
              "type": "number"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000204",
              "name": "guestName",
              "value": "YOUR NAME",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000205",
              "name": "guestTitle",
              "value": "Founder of a pricing consultancy for B2B SaaS",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000206",
              "name": "yourExpertise",
              "value": "How small SaaS companies set and change prices: packaging, seat versus usage pricing, running a price increase without losing the base, and what breaks when a self-serve product starts selling to bigger companies.",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000207",
              "name": "talkingPoints",
              "value": "Why seat-based pricing stops working above a certain deal size; how to run a price increase on existing customers without churn; the three packaging mistakes that cap self-serve revenue; what changes in a pricing page when you start selling to procurement",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000208",
              "name": "proofPoints",
              "value": "Eleven years in B2B SaaS pricing. Ran pricing changes for 40 companies between 2019 and 2026. Wrote the pricing chapter in a book published in 2024. Former head of growth at a company that grew from 2 million to 9 million in annual recurring revenue.",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000209",
              "name": "audienceValue",
              "value": "Founders and operators of software companies between roughly 20,000 and 1 million dollars a month in revenue who have never deliberately changed their prices.",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000210",
              "name": "minFit",
              "value": 60,
              "type": "number"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000211",
              "name": "maxPitches",
              "value": 5,
              "type": "number"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000212",
              "name": "googleSheetUrl",
              "value": "PASTE_YOUR_GOOGLE_SHEET_URL_HERE",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000213",
              "name": "showsTab",
              "value": "Shows",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000214",
              "name": "telegramChatId",
              "value": "PASTE_YOUR_TELEGRAM_CHAT_ID_HERE",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "pg44c901-0000-4000-8000-000000000502",
      "name": "Set your pitch",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        120,
        120
      ]
    },
    {
      "parameters": {
        "operation": "Run actor and get dataset",
        "actorSource": "store",
        "actorId": {
          "__rl": true,
          "value": "9aFVadepmBddUhsGv",
          "mode": "id"
        },
        "customBody": "={{ (() => {\n  const s = $('Set your pitch').first().json;\n  const list = v => String(v || '').split(',').map(x => x.trim()).filter(Boolean);\n  return JSON.stringify({\n    keywords: list(s.niches),\n    requireEmail: true,\n    guestFriendlyOnly: true,\n    activeWithinDays: s.activeWithinDays,\n    country: s.country,\n    maxPodcasts: s.maxShows,\n    onlyNewPodcasts: true\n  });\n})() }}"
      },
      "id": "pg44c901-0000-4000-8000-000000000503",
      "name": "Find podcasts that book guests",
      "type": "@apify/n8n-nodes-apify.apify",
      "typeVersion": 1,
      "position": [
        460,
        120
      ]
    },
    {
      "parameters": {
        "operation": "removeItemsSeenInPreviousExecutions",
        "dedupeValue": "={{ $json.itunesId }}",
        "options": {}
      },
      "id": "pg44c901-0000-4000-8000-000000000504",
      "name": "Skip shows already seen",
      "type": "n8n-nodes-base.removeDuplicates",
      "typeVersion": 2,
      "position": [
        720,
        120
      ]
    },
    {
      "parameters": {
        "text": "=THE GUEST\nName: {{ $('Set your pitch').first().json.guestName }}\nTitle: {{ $('Set your pitch').first().json.guestTitle }}\nWhat they can speak about: {{ $('Set your pitch').first().json.yourExpertise }}\nTalking points: {{ $('Set your pitch').first().json.talkingPoints }}\nProof points, usable verbatim only: {{ $('Set your pitch').first().json.proofPoints }}\nWho gains from hearing them: {{ $('Set your pitch').first().json.audienceValue }}\n\nTHE SHOW\nShow: {{ $json.podcastName }}\nHost: {{ $json.hostName || $json.ownerName || 'not stated' }}\nGenre: {{ $json.primaryGenre || 'not stated' }}\nCategories: {{ ($json.categories || []).join(', ') || 'not stated' }}\nEpisodes published: {{ $json.episodeCount }}\nDays since the last episode: {{ $json.daysSinceLastEpisode === null ? 'not stated' : $json.daysSinceLastEpisode }}\nInterview-format signals found in the show's own words: {{ ($json.guestSignals || []).join(', ') || 'none found' }}\nFound by searching: {{ $json.keyword }}",
        "messages": {
          "messageValues": [
            {
              "message": "You are a podcast booker who decides whether one specific guest belongs on one specific show. You are strict, because a wrong pitch burns the only cold email that host will ever read from this guest. You can see what the show says it is about, how often it publishes and whether it interviews guests. You cannot see an episode list, so never pretend to know what any episode covered.\n\nHow to judge fit, 0 to 100:\n- If the interview-format signals line says none were found, the fit is below 40, whatever the subject is: a solo show does not book guests.\n- If days since the last episode is more than 120, take 20 points off whatever you were going to give.\n- Score 80 or above only when the subject this show names is the subject of one of the guest's talking points, not merely the same industry.\n- Score between 40 and 70 when the show and the talking point share only a broad field such as Business, Marketing, Technology, Entrepreneurship or Careers. A shared category is a hint, not a fit.\n- Score below 40 when nothing the show names meets any of the talking points. Audience size is not fit, and you cannot see audience size anyway.\n\nHow to fill titleWord: the single word from the talking point you are pitching that also appears in the show's title, copied exactly as it appears in the title. Ignore words like the, of, for, and, your, with, a, in, to. If no word from that talking point is in the title, return an empty string. Do not stretch this: a show called 'This Week in Startups' contains none of the words in 'running a price increase without churn', so the answer there is an empty string. This field is checked against the title afterwards, so a word that is not really there only costs the show points.\n\nHow to write whyItFits: one short phrase in two halves joined by 'and': what this show says it covers, then the subject of the talking point that meets it. The second half is a noun phrase of at most five words, the subject itself and not the whole talking point: 'price increases on existing customers' is right, 'how to run a price increase on existing customers without churn' is the talking point pasted back and is wrong. It must never be an argument that the subject matters: 'pricing strategies are essential for SaaS growth' is an opinion and is wrong. When the fit is low, name the mismatch instead, for example 'show covers true crime, guest speaks about pricing for software companies'.\n\nHow to write proposedEpisode: the title of the episode this guest could deliver on this show, at most 12 words, in sentence case as a person would type it and never in Title Case, built only from the guest's talking points and expertise as written above. Never invent a case study, a number, a client name or a result that the guest did not write. If nothing in the guest's material fits this show, return an empty string and score below 40.\n\nNever state or guess the show's download numbers, its chart position, its listener count, the host's employer, or anything about the host beyond the name given above. Use plain ASCII punctuation, and a plain hyphen for ranges.\n\nAnswer with the JSON object the schema asks for and nothing else: no prose before or after it, no code fences, and no labelled lines such as 'Fit:'."
            }
          ]
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "id": "pg44c901-0000-4000-8000-000000000505",
      "name": "Score the show fit",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.5,
      "position": [
        1060,
        120
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {
          "temperature": 0.3
        }
      },
      "id": "pg44c901-0000-4000-8000-000000000506",
      "name": "OpenAI model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [
        980,
        400
      ]
    },
    {
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"fit\": { \"type\": \"number\" },\n    \"titleWord\": { \"type\": \"string\" },\n    \"whyItFits\": { \"type\": \"string\" },\n    \"proposedEpisode\": { \"type\": \"string\" }\n  },\n  \"required\": [\"fit\", \"titleWord\", \"whyItFits\", \"proposedEpisode\"]\n}"
      },
      "id": "pg44c901-0000-4000-8000-000000000507",
      "name": "Fit format",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.2,
      "position": [
        1240,
        400
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "pg44c901-0000-4000-8000-000000000300",
              "name": "Show",
              "value": "={{ $('Skip shows already seen').item.json.podcastName }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000301",
              "name": "Host",
              "value": "={{ $('Skip shows already seen').item.json.hostName || $('Skip shows already seen').item.json.ownerName || '' }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000302",
              "name": "Email",
              "value": "={{ $('Skip shows already seen').item.json.email || '' }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000303",
              "name": "Fit",
              "value": "={{ (() => {\nconst o = $json.output;\nconst title = String($('Skip shows already seen').item.json.podcastName || '').toLowerCase();\nconst points = String($('Set your pitch').first().json.talkingPoints || '').toLowerCase();\nconst w = String(o.titleWord || '').trim().toLowerCase();\nconst inTitle = w.length > 2 && title.includes(w) && points.includes(w);\nconst fit = Number(o.fit) || 0;\nreturn inTitle ? fit : Math.min(fit, 70);\n})() }}",
              "type": "number"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000304",
              "name": "Title match",
              "value": "={{ (() => {\nconst o = $json.output;\nconst title = String($('Skip shows already seen').item.json.podcastName || '').toLowerCase();\nconst points = String($('Set your pitch').first().json.talkingPoints || '').toLowerCase();\nconst w = String(o.titleWord || '').trim().toLowerCase();\nconst inTitle = w.length > 2 && title.includes(w) && points.includes(w);\nreturn inTitle ? w : 'no shared word, capped at 70';\n})() }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000305",
              "name": "Why it fits",
              "value": "={{ (() => {\nconst ascii = t => String(t ?? '').replace(/[\\u2018\\u2019]/g, \"'\").replace(/[\\u201c\\u201d]/g, '\"').replace(/[\\u2013\\u2014]/g, '-').replace(/\\u2026/g, '...').replace(/\\u00a0/g, ' ');\nreturn ascii($json.output.whyItFits);\n})() }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000306",
              "name": "Proposed episode",
              "value": "={{ (() => {\nconst ascii = t => String(t ?? '').replace(/[\\u2018\\u2019]/g, \"'\").replace(/[\\u201c\\u201d]/g, '\"').replace(/[\\u2013\\u2014]/g, '-').replace(/\\u2026/g, '...').replace(/\\u00a0/g, ' ');\nreturn ascii($json.output.proposedEpisode);\n})() }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000307",
              "name": "Episodes",
              "value": "={{ $('Skip shows already seen').item.json.episodeCount }}",
              "type": "number"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000308",
              "name": "Days since last episode",
              "value": "={{ $('Skip shows already seen').item.json.daysSinceLastEpisode }}",
              "type": "number"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000309",
              "name": "Genre",
              "value": "={{ $('Skip shows already seen').item.json.primaryGenre || '' }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000310",
              "name": "Categories",
              "value": "={{ ($('Skip shows already seen').item.json.categories || []).join(', ') }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000311",
              "name": "Guest signals",
              "value": "={{ ($('Skip shows already seen').item.json.guestSignals || []).join(', ') }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000312",
              "name": "Apple link",
              "value": "={{ $('Skip shows already seen').item.json.appleUrl || '' }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000313",
              "name": "Website",
              "value": "={{ $('Skip shows already seen').item.json.websiteUrl || '' }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000314",
              "name": "Found by keyword",
              "value": "={{ $('Skip shows already seen').item.json.keyword }}",
              "type": "string"
            },
            {
              "id": "pg44c901-0000-4000-8000-000000000315",
              "name": "Checked",
              "value": "={{ $now.format('yyyy-MM-dd') }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "pg44c901-0000-4000-8000-000000000508",
      "name": "Format the show row",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1400,
        120
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Set your pitch').first().json.googleSheetUrl }}",
          "mode": "url"
        },
        "sheetName": {
          "__rl": true,
          "value": "={{ $('Set your pitch').first().json.showsTab }}",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "pg44c901-0000-4000-8000-000000000509",
      "name": "Save every show",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1960,
        20
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "pg44c901-0000-4000-8000-000000000400",
              "leftValue": "={{ Number($json.Fit) >= Number($('Set your pitch').first().json.minFit) && String($json.Email || '').includes('@') }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "id": "pg44c901-0000-4000-8000-000000000510",
      "name": "Keep the shows worth pitching",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [
        1960,
        240
      ]
    },
    {
      "parameters": {
        "maxItems": "={{ $('Set your pitch').first().json.maxPitches }}"
      },
      "id": "pg44c901-0000-4000-8000-000000000511",
      "name": "Cap the pitches per run",
      "type": "n8n-nodes-base.limit",
      "typeVersion": 1,
      "position": [
        2220,
        240
      ]
    },
    {
      "parameters": {
        "text": "=THE GUEST\nName: {{ $('Set your pitch').first().json.guestName }}\nTitle: {{ $('Set your pitch').first().json.guestTitle }}\nTalking points: {{ $('Set your pitch').first().json.talkingPoints }}\nProof points, quotable verbatim only: {{ $('Set your pitch').first().json.proofPoints }}\nWho gains from hearing them: {{ $('Set your pitch').first().json.audienceValue }}\n\nTHE SHOW\nShow: {{ $json.Show }}\nHost: {{ $json.Host || 'not stated' }}\nSubject the show covers: {{ $json.Categories || $json.Genre }}\n\nWHY IT FITS\n{{ $json['Why it fits'] }}\n\nTHE EPISODE IDEA\n{{ $json['Proposed episode'] }}",
        "messages": {
          "messageValues": [
            {
              "message": "You write the guest pitch email a person sends to a podcast host. It is cold, so it has to earn the next ten seconds, and the host has read a hundred pitches that all opened the same way.\n\nReturn a subject line on the first line, then a blank line, then the email body. Nothing else.\n\nThe subject line:\n- At most 10 words, sentence case, no colon-separated marketing phrase.\n- It names the episode idea, not the guest and not the show. 'Why seat pricing breaks at scale' is a subject. 'Podcast guest request' and 'Loved your show' are not.\n- Do not write the word Subject, or any other label, in front of it.\n\nThe body:\n- Between 110 and 150 words, plain text, addressed to the host by first name, or 'Hi there' when no host name is given above.\n- One line saying what the show is about and where you meet it. Use the text under WHY IT FITS as your source, but do not copy that line into the email: write it as a person would say it out loud. Never argue that the subject is important, and never praise the show. 'Your show covers pricing and packaging for software companies' is right. 'Pricing is essential for SaaS growth' is an opinion, and 'your show dives into the nuances of X' is filler.\n- One line proposing the episode under THE EPISODE IDEA, by name, with no quotation marks around it.\n- Then three lines, each starting with a plain hyphen and a space, each one thing a listener would walk away with. Every one of the three must come from the guest's talking points as written above. Each line starts with the takeaway itself: never begin a line with 'Listeners will learn', 'You will learn', 'Understand', 'Discover', 'Learn' or 'I will share'. Do not add a fourth.\n- One line of credentials, at most 30 words, drawn only from the proof points as written. Every number, date, company name and job title must appear verbatim in those proof points. If no number is there, do not produce one, and do not estimate, round or illustrate.\n- One closing line asking whether they are taking guests this quarter. No date, no deadline, no second ask.\n- Then the guest's name on its own last line.\n\nForbidden anywhere in the email: 'I hope this email finds you well', 'quick question', 'reach out', 'circle back', 'touch base', 'synergy', 'leverage', 'game-changer', 'thought leader', 'deep dive', 'dives into', 'nuances', 'perfect platform', 'perfect fit', 'huge fan', 'I came across', any praise of the show or the host, any claim about the show's audience, downloads or ranking, any statistic that is not in the proof points, exclamation marks, emoji, markdown, a sign-off block with links, and any sentence that tells the host what they want or need. Avoid those words in every form, so 'leveraging' and 'thought leadership' are out too.\n\nFormatting: one blank line between blocks, no blank line between the three hyphen lines, no space at the end of any line, plain ASCII punctuation only, and a plain hyphen for ranges. Never mention that this was drafted by a model or by an automation.\n\nWrite only the email: no notes, no alternatives, no commentary."
            }
          ]
        },
        "promptType": "define"
      },
      "id": "pg44c901-0000-4000-8000-000000000512",
      "name": "Write the pitch email",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.5,
      "position": [
        2560,
        240
      ]
    },
    {
      "parameters": {
        "resource": "draft",
        "operation": "create",
        "subject": "={{ (() => {\nconst out = String($json.text || '').trim();\nconst nl = out.indexOf('\\n');\nconst first = nl === -1 ? out : out.slice(0, nl);\nreturn first.replace(/^subject:\\s*/i, '').trim();\n})() }}",
        "emailType": "text",
        "message": "={{ (() => {\nconst ascii = t => String(t ?? '').replace(/[\\u2018\\u2019]/g, \"'\").replace(/[\\u201c\\u201d]/g, '\"').replace(/[\\u2013\\u2014]/g, '-').replace(/\\u2026/g, '...').replace(/\\u00a0/g, ' ');\nconst tidy = t => ascii(t).split('\\n').map(l => l.replace(/[ \\t]+$/, '')).join('\\n').replace(/\\n{3,}/g, '\\n\\n').trim();\nconst out = tidy($json.text);\nconst nl = out.indexOf('\\n');\nreturn tidy(nl === -1 ? out : out.slice(nl + 1));\n})() }}",
        "options": {
          "sendTo": "={{ $('Format the show row').item.json.Email }}"
        }
      },
      "id": "pg44c901-0000-4000-8000-000000000513",
      "name": "Create the Gmail draft",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        2860,
        380
      ]
    },
    {
      "parameters": {
        "chatId": "={{ $('Set your pitch').first().json.telegramChatId }}",
        "text": "={{ (() => {\nconst esc = s => String(s ?? '').replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');\nconst cut = (s, n) => String(s ?? '').length > n ? String(s).slice(0, n - 1).trimEnd() + '\\u2026' : String(s ?? '');\nconst r = $('Format the show row').item.json;\nconst ascii = t => String(t ?? '').replace(/[\\u2018\\u2019]/g, \"'\").replace(/[\\u201c\\u201d]/g, '\"').replace(/[\\u2013\\u2014]/g, '-').replace(/\\u2026/g, '...').replace(/\\u00a0/g, ' ');\nconst tidy = t => ascii(t).split('\\n').map(l => l.replace(/[ \\t]+$/, '')).join('\\n').replace(/\\n{3,}/g, '\\n\\n').trim();\nconst out = tidy($('Write the pitch email').item.json.text);\nconst nl = out.indexOf('\\n');\nconst subject = (nl === -1 ? '' : out.slice(0, nl)).replace(/^subject:\\s*/i, '').trim();\nconst body = tidy(nl === -1 ? out : out.slice(nl + 1));\nreturn '\\ud83c\\udf99 <b>' + esc(r['Show']) + '</b> \\u00b7 fit ' + r['Fit'] + '/100\\n'\n  + '\\ud83d\\udc64 ' + esc(r['Host'] || 'host not named') + ' \\u00b7 ' + esc(r['Email']) + '\\n'\n  + '\\ud83e\\udde9 ' + esc(r['Why it fits']) + '\\n'\n  + '\\ud83c\\udfac ' + esc(r['Proposed episode']) + '\\n'\n  + '\\ud83d\\udcc5 ' + r['Episodes'] + ' episodes \\u00b7 last one ' + r['Days since last episode'] + ' days ago\\n\\n'\n  + (subject ? '<b>Subject</b>\\n' + esc(subject) + '\\n\\n' : '')\n  + '<b>Draft in your Gmail, read it before you send</b>\\n<pre>' + esc(cut(body, 2600)) + '</pre>\\n\\n'\n  + '\\u27a1 <a href=\"' + r['Apple link'] + '\">The show on Apple Podcasts</a>';\n})() }}",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "HTML",
          "disable_web_page_preview": true
        }
      },
      "id": "pg44c901-0000-4000-8000-000000000514",
      "name": "Send the pitch to Telegram",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        2860,
        140
      ]
    }
  ],
  "connections": {
    "Every Monday at 7am": {
      "main": [
        [
          {
            "node": "Set your pitch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set your pitch": {
      "main": [
        [
          {
            "node": "Find podcasts that book guests",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find podcasts that book guests": {
      "main": [
        [
          {
            "node": "Skip shows already seen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip shows already seen": {
      "main": [
        [
          {
            "node": "Score the show fit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Score the show fit": {
      "main": [
        [
          {
            "node": "Format the show row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format the show row": {
      "main": [
        [
          {
            "node": "Save every show",
            "type": "main",
            "index": 0
          },
          {
            "node": "Keep the shows worth pitching",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Keep the shows worth pitching": {
      "main": [
        [
          {
            "node": "Cap the pitches per run",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cap the pitches per run": {
      "main": [
        [
          {
            "node": "Write the pitch email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write the pitch email": {
      "main": [
        [
          {
            "node": "Send the pitch to Telegram",
            "type": "main",
            "index": 0
          },
          {
            "node": "Create the Gmail draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI model": {
      "ai_languageModel": [
        [
          {
            "node": "Score the show fit",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Write the pitch email",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Fit format": {
      "ai_outputParser": [
        [
          {
            "node": "Score the show fit",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": false
  }
}