Tutorial5 min read

Build a Meeting Summarizer in Under 100 Lines of Code

A complete, production-ready meeting summarizer using Gregnote webhooks and Claude or GPT-4. Receive a webhook, summarise the transcript, send to email or Slack.

Gregnote Team

26 July 2026

The complete implementation

Here's a meeting summarizer that receives a Gregnote webhook, summarises the transcript with an LLM, and emails the summary to all participants. Under 100 lines.

import express from "express";
import crypto from "crypto";
import Anthropic from "@anthropic-ai/sdk";
import nodemailer from "nodemailer";

const app = express(); const claude = new Anthropic();

const mailer = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: 587, auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS }, });

function verify(req) { const [tPart, vPart] = (req.headers["x-gregnote-signature"] || "").split(","); const ts = tPart?.split("=")[1]; const sig = vPart?.split("=")[1]; const expected = crypto .createHmac("sha256", process.env.GREGNOTE_SECRET) .update(ts + "." + req.body) .digest("hex"); return sig === expected; }

app.post("/webhook", express.raw({ type: "*/*" }), async (req, res) => { if (!verify(req)) return res.sendStatus(401);

const { event, transcript } = JSON.parse(req.body.toString()); if (event !== "meeting.completed") return res.sendStatus(200);

const text = transcript.segments .filter(s => !s.is_bot) .map(s => ${s.speaker}: ${s.text}) .join("\n");

const { content } = await claude.messages.create({ model: "claude-3-5-haiku-20241022", max_tokens: 512, messages: [{ role: "user", content: Summarise this meeting in 3–5 bullet points covering key decisions and action items:\n\n${text}, }], });

const summary = content[0].text; const participants = transcript.speakers .filter(s => !s.is_bot) .map(s => s.display_name);

await mailer.sendMail({ from: "[email protected]", to: participants.join(", "), subject: Meeting summary — ${new Date().toLocaleDateString()}, text: summary, });

res.sendStatus(200); });

app.listen(3000); ```

What each part does

  • `verify()` — checks the HMAC signature. Never skip this.
  • `text` — concatenates only human segments, filtering out the bot's own audio.
  • Claude summarise — produces 3–5 bullets covering decisions and actions.
  • `mailer.sendMail` — sends the summary to every meeting participant by name.

Deploy it

Wrap in a Dockerfile, deploy to any VPS, set four environment variables. Done. Every meeting your team takes now emails itself a summary automatically.

Try it yourself

API key in 30 seconds. Free credit on sign-up. No card required.