← Blog

How to Build a Meeting Notes Summarizer

2026-10-30 · 5 min read · SubToAPI Team

A meeting notes summarizer takes a raw transcript or recording-derived text and turns it into a short, structured summary: key decisions, action items, and owners. Building one is mostly a prompting and data-pipeline problem, not a model-training problem — you don't need to fine-tune anything. You feed a transcript into an LLM with a well-structured prompt, parse the structured output, and store or send it wherever your team already works (Slack, Notion, email).

This guide walks through the actual pieces: getting the transcript text, designing a prompt that produces consistent structured output, calling the API, and wiring it into a simple pipeline you can run after every meeting.

What you actually need to build

A meeting notes summarizer has four components:

  1. Transcript input — from a transcription service (Whisper, Otter, Zoom's built-in transcript, Fireflies, etc.) or a manually pasted text file.
  2. A summarization prompt — instructions that tell the model exactly what structure to return.
  3. An LLM call — the actual API request that generates the summary.
  4. Output handling — parsing the response and pushing it somewhere useful.

Most of the "hard" work is in step 2. Getting consistent, parseable output from a model requires more care than people expect on the first try.

Step 1: Get clean transcript text

If you're using a transcription tool, export the transcript as plain text with speaker labels if available. Speaker labels matter — they let the model attribute action items to the right person ("Sarah will send the proposal by Friday" is much more useful than an unattributed sentence).

If your transcript is long (over an hour of meeting), consider chunking it and summarizing in two passes: first summarize each chunk, then summarize the summaries. Most current Claude models handle long context well, so for meetings under roughly two hours you can usually send the whole transcript in one call.

Step 2: Design the prompt

The biggest mistake in building a summarizer is asking for a generic summary. You want structured output you can parse programmatically. Ask for JSON explicitly, and give the model a schema:

You are a meeting notes assistant. Given a meeting transcript, extract:

1. A 2-3 sentence overview of what the meeting was about.
2. Key decisions made (as a list).
3. Action items, each with: task, owner (if mentioned), and due date (if mentioned).
4. Open questions that were raised but not resolved.

Return ONLY valid JSON in this exact shape:
{
  "overview": string,
  "decisions": string[],
  "action_items": [{ "task": string, "owner": string | null, "due_date": string | null }],
  "open_questions": string[]
}

Transcript:
<transcript text here>

Being explicit about "owner: null if not mentioned" prevents the model from inventing names, which is a common failure mode when summarizing vague or cross-talking transcripts.

Step 3: Call the API

Here's a working example using SubToAPI, which lets you call Claude models through a standard HTTPS API with an application key (sub_live_...) instead of managing provider-specific auth:

const res = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: "claude-sonnet-4",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: `You are a meeting notes assistant... [prompt from Step 2]\n\nTranscript:\n${transcriptText}`
      }
    ]
  })
});

const data = await res.json();
const summary = JSON.parse(data.content[0].text);

If you want to guarantee valid JSON structure every time instead of relying on prompt instructions alone, use tool calling — define a tool schema for the summary shape and force the model to call it. This is more robust for production pipelines where a malformed JSON string would break downstream parsing. SubToAPI supports tool use the same way the underlying Claude API does — see /docs/tools for the request format.

Step 4: Handle the output

Once you have structured JSON, the rest is plumbing:

A minimal action-item formatter for Slack:

function formatActionItems(items) {
  return items
    .map(i => `• *${i.task}* — ${i.owner ?? "Unassigned"}${i.due_date ? ` (due ${i.due_date})` : ""}`)
    .join("\n");
}

Making it reliable in production

A few things matter more than people expect once this moves from a demo to something your team depends on:

Choosing where to run this

You can call Claude directly through Anthropic's API, or through a layer like SubToAPI that gives you an HTTPS endpoint, API keys scoped per application, and team seats if multiple people on your team are building or maintaining summarization workflows. For a single internal script, direct access is fine. For something more teams depend on — shared keys, usage visibility, multiple environments — a managed layer saves you from building that tooling yourself. Plans start at €9/month for solo use, with team and scale tiers for shared access; see /pricing. You can try it with a free trial at /signup, and the full request format is in /docs/quickstart and /docs/messages.

Questions

Do I need to fine-tune a model to build a meeting notes summarizer? No. Prompt-based summarization with a clear output schema works well for this use case. Fine-tuning adds cost and maintenance without meaningfully improving structured extraction tasks like this.

How do I handle very long meetings (2+ hours)? Chunk the transcript into segments, summarize each chunk separately, then run a second summarization pass over the combined chunk summaries to produce the final structured output.

How do I stop the model from inventing action item owners? Explicitly instruct the model to return null when an owner isn't stated in the transcript, and include that constraint in your JSON schema description — vague prompts are the main cause of invented names and dates.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →