Table of Contents 8 Sections
Overview
This guide demonstrates how to build an automated pipeline that takes raw meeting transcripts (from Zoom, Otter.ai, Google Meet, or Whisper), sends them to Claude via the Anthropic Messages API for structured summarization, and creates a formatted page with executive summaries, key decisions, and action items inside a Notion database. Every technical term is explained in the Glossary below before it is used.
Budget about 20–30 minutes to set up your Notion integration token, share your database, configure your Anthropic API key, and test a sample transcript payload. Ongoing costs depend on meeting volume: Claude 3.5 Sonnet costs fractions of a cent to a few cents per meeting summary depending on transcript token count. This guide has been written from technical documentation and general best practice, but — like every guide currently on Workflow Vault — it is UNVERIFIED: it has not yet been run end-to-end and confirmed working by Workflow Vault's own testing. Review all code before running it in production.
Glossary
- Anthropic Messages API
- The official REST API endpoint provided by Anthropic for sending prompts and receiving text or structured analysis from Claude AI models.
- Claude Sonnet
- Anthropic's balanced model tier offering high reasoning accuracy, strong summarization skills, and fast response times at a cost-effective price.
- Notion Integration Token
- A secret bearer token (starting with `secret_` or `ntn_`) generated in Notion's developer portal that grants your automation script permission to read and create pages in Notion.
- Notion Database ID
- The unique 32-character alphanumeric identifier found in the URL of your Notion database (between the workspace name and the question mark) specifying where new pages should be inserted.
- Notion Block
- The fundamental unit of content in Notion (such as headings, bullet lists, callouts, and paragraphs). The Notion API requires page content to be passed as an array of block objects.
- Token (LLM)
- The basic unit of text that large language models process. As a rule of thumb, 1,000 tokens corresponds to roughly 750 English words.
- Environment Variable
- A secure named setting configured on your server or hosting platform to hold sensitive API keys without committing them directly into code repositories.
- Webhook
- An automated HTTP POST request sent by a service (such as Zoom or a transcription provider) immediately when an event occurs, triggering downstream processing.
Prerequisites
- [You'll need this already] An Anthropic API key from console.anthropic.com with active credits or billing configured.
- [You'll need this already] A Notion workspace where you have admin permissions to create an internal integration and a target database with a Name/Title column.
- [You'll need this already] A Node.js environment (Node 18+ or Cloudflare Workers) capable of making fetch requests and reading environment variables.
- [You'll need this already] Basic familiarity with JSON and async JavaScript functions. You do not need deep AI or Notion API experience — all requests and payload formats are provided below.
- [Optional, not required] A sample transcript export file (VTT, TXT, or JSON) from Zoom, Otter.ai, or Google Meet to use during initial testing.
Where to Run This Automation
Where should you run this automation?
Don't have 24/7 hardware at home? Choose the setup that fits your budget and technical comfort.
Best for Freelancers, Agencies, and 24/7 Client Pipelines
If you don't have a dedicated server running at home, a Virtual Private Server (VPS) gives you a dedicated Linux server in the cloud that stays online 24/7 even when your personal laptop is closed.
- Cost: ~$4 to $6 / month (predictable flat fee, no per-execution surprises).
- Providers: Hetzner Cloud, DigitalOcean, Linode / Akamai, Railway, or OVH.
- Key advantage: You get a permanent public IP address and free SSL certificates, which are essential for receiving incoming Stripe, Airtable, or GitHub webhooks reliably.
curl -fsSL https://get.docker.com | sh && sudo usermod -aG docker $USER Best for Hobbyists, Testing & Zero-Cost Experimentation
You can run this automation completely free on your everyday computer (Mac, Windows, or Linux) or on low-cost home hardware like a Raspberry Pi 4/5 or repurposed mini PC.
- Cost: $0 / month (100% free).
- Software needed: Docker Desktop (free for personal use) or native Node.js / Python.
- Consideration: Automations will only execute while your computer is powered on and awake. To receive incoming webhooks locally, use tools like
ngrokorCloudflare Tunnelsfor secure local tunneling.
Best for Zero-Maintenance 1-Click Operations
If you don't want to manage Docker containers or terminal commands, you can run automations directly on managed serverless platforms or official SaaS plans:
- Cloudflare Workers: Free tier includes 100,000 requests per day with 0ms cold starts.
- n8n Cloud / Make / Zapier: Fully managed hosted platforms with automated backups and visual scenario builders.
- Railway / Render: 1-click Git container deploys with auto-healing and managed PostgreSQL databases.
Set up the Notion integration and database
Log into notion.so/profile/integrations and click 'New integration'. Name it 'Meeting Summarizer', select your workspace, and save it to obtain your Internal Integration Secret (starts with `secret_` or `ntn_`). Next, create or open your Notion database for meeting notes. Click the '...' menu at the top right of the database page, select 'Connections' or 'Add connections', and choose your 'Meeting Summarizer' integration to give it write access. Finally, copy the Database ID from the browser URL.
By default, Notion integrations have zero access to any page or database in your workspace. Explicitly sharing the database with the integration connection is required before the API can create pages inside it.
Your database settings should list 'Meeting Summarizer' under Connections, and you should have both your integration secret token and database ID ready.
Call Claude to generate a structured summary
Create a function that sends the raw transcript to Anthropic's Messages API (`https://api.anthropic.com/v1/messages`). Use `claude-3-5-sonnet-20241022` or `claude-3-haiku-20240307` with a structured system prompt instructing Claude to output cleanly demarcated sections: Executive Summary, Key Decisions, Action Items (with assignees if mentioned), and Open Questions.
async function summarizeTranscript(transcript, meetingTitle) {
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'x-api-key': process.env.ANTHROPIC_API_KEY,
'anthropic-version': '2023-06-01',
'content-type': 'application/json'
},
body: JSON.stringify({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 1500,
temperature: 0.2,
system: 'You are an executive assistant. Summarize meeting transcripts into structured sections: 1) Executive Summary, 2) Key Decisions, 3) Action Items, 4) Open Questions. Use clear bullet points.',
messages: [{
role: 'user',
content: `Please summarize this meeting transcript titled "${meetingTitle}":\n\n${transcript}`
}]
})
});
if (!response.ok) {
const err = await response.text();
throw new Error(`Claude API error (${response.status}): ${err}`);
}
const data = await response.json();
return data.content[0].text;
} Supplying a structured format prompt forces Claude to return consistent, predictable sections rather than free-form conversational prose, making it simple to parse into individual Notion blocks.
Running this function with a test transcript returns Claude's response text containing clearly formatted headers and bullet points without extraneous pleasantries.
Convert the markdown summary into Notion block objects
Write a lightweight converter function that turns the summary text into the block format required by Notion's API. Convert main section titles into `heading_2` blocks, bullet points into `bulleted_list_item` blocks, and standard paragraphs into `paragraph` blocks.
function textToNotionBlocks(summaryText) {
const lines = summaryText.split('\n').filter(line => line.trim() !== '');
const blocks = [];
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith('#') || trimmed.match(/^[0-9]+\)/) || trimmed.endsWith(':')) {
const cleanHeader = trimmed.replace(/^#+\s*/, '').replace(/^[0-9]+\)\s*/, '');
blocks.push({
object: 'block',
type: 'heading_2',
heading_2: {
rich_text: [{ type: 'text', text: { content: cleanHeader } }]
}
});
} else if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) {
blocks.push({
object: 'block',
type: 'bulleted_list_item',
bulleted_list_item: {
rich_text: [{ type: 'text', text: { content: trimmed.replace(/^[-*]\s+/, '') } }]
}
});
} else {
blocks.push({
object: 'block',
type: 'paragraph',
paragraph: {
rich_text: [{ type: 'text', text: { content: trimmed } }]
}
});
}
}
return blocks;
} Notion does not accept raw markdown strings for page content; it expects an array of typed block objects. Converting the text beforehand ensures the Notion page has native formatting, headings, and clean list indentation.
The helper function produces an array of objects shaped like `{ object: 'block', type: 'heading_2', heading_2: { ... } }` matching the Notion block specification.
Create the new page in your Notion database
Call the Notion API (`https://api.notion.com/v1/pages`) with `NOTION_TOKEN` in the Authorization header and `Notion-Version: 2022-06-28`. Pass the database ID as parent, the meeting title and date in properties, and the generated blocks as children.
async function createNotionMeetingPage(meetingTitle, meetingDate, summaryBlocks) {
const response = await fetch('https://api.notion.com/v1/pages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.NOTION_TOKEN}`,
'Notion-Version': '2022-06-28',
'content-type': 'application/json'
},
body: JSON.stringify({
parent: { database_id: process.env.NOTION_DATABASE_ID },
properties: {
Name: {
title: [{ type: 'text', text: { content: meetingTitle } }]
}
},
children: summaryBlocks
})
});
if (!response.ok) {
const err = await response.text();
throw new Error(`Notion API error (${response.status}): ${err}`);
}
return await response.json();
} This step writes the final data to Notion. By passing the summary blocks in the initial page creation request, the page is populated in a single atomic API call rather than requiring multiple roundtrips.
The Notion API returns an HTTP 200 with the newly created page object. Opening your Notion database shows a new entry with the title, timestamp, and formatted summary blocks inside.
Assemble the complete automation handler
Wire the steps together inside a single handler function that accepts an incoming transcript payload, invokes Claude for summarization, formats the blocks, and publishes to Notion with robust try/catch error logging.
async function processMeetingTranscript(transcript, meetingTitle = 'Untitled Meeting') {
console.log(`Starting summarization for: ${meetingTitle}`);
const summary = await summarizeTranscript(transcript, meetingTitle);
const blocks = textToNotionBlocks(summary);
const newPage = await createNotionMeetingPage(meetingTitle, new Date().toISOString(), blocks);
console.log(`Successfully created Notion page: ${newPage.url}`);
return newPage;
} Packaging the full flow into an isolated handler allows it to be plugged directly into an Express route, AWS Lambda handler, or Cloudflare Worker endpoint triggered whenever a new transcript is ready.
Calling `processMeetingTranscript(sampleText, 'Sprint Planning')` executes the entire pipeline and logs the URL of the created Notion page.
Workflow architecture
A transcript source (such as a Zoom webhook or recording uploader) invokes a serverless execution environment. The function sends the raw text to Claude via the Anthropic Messages API, which extracts structured summary sections. A lightweight parser converts the summary text into standard Notion block objects, and the Notion API writes the new page into the target database. The pipeline remains completely stateless and safe to retry on failures.
Final result
Structured meeting summaries appear inside your Notion database within 5 to 15 seconds of a transcript becoming available, containing clean headings, key decisions, and actionable tasks with no manual copy-pasting required. As noted above, this guide is unverified by Workflow Vault — treat the outcome as expected-but-not-yet-confirmed until tested with your own credentials.
Troubleshooting
Claude API call returns 401 Unauthorized
ANTHROPIC_API_KEY environment variable is missing, incorrect, or expired.
Generate a new API key in the Anthropic Console, update your deployment environment variables, and restart your server or worker.
Notion API returns 404 with error code 'object_not_found'
The Notion integration has not been shared with the target database, or NOTION_DATABASE_ID is incorrect.
Open your Notion database in the browser, click '...' -> Connections, and add your integration. Verify the 32-character database ID in your URL.
Notion API returns 400 'validation_error' on blocks array
A block object in the array exceeded Notion's 2000-character limit per rich_text item, or an invalid block type was passed.
Ensure the text splitter chunks long paragraphs into segments under 2000 characters before building the rich_text array.
Summary text arrives truncated or cuts off mid-sentence
max_tokens parameter in the Claude API request is set too low for the transcript length.
Increase max_tokens to 2000 or 4000 in your Anthropic API request configuration.