appscript.dev
Automation Advanced Drive Docs

Summarize a folder of PDFs into a briefing

Digest Northwind's research PDFs into one structured briefing Doc.

Published Aug 27, 2025

Research lands at Northwind as a folder of PDFs — vendor reports, white papers, market notes — and nobody has time to read all of them before the meeting where they matter. The documents sit unread, and the decisions get made without them.

This script turns that folder into a single briefing Doc. It walks every PDF in a Drive folder, asks Claude to condense each one to a few bullets, and stitches the results into a dated Doc with a section per source. Instead of ten PDFs to open, you get one page to skim — and a record of what each document actually said.

What you’ll need

  • A Google Drive folder containing the PDFs you want digested.
  • An Anthropic API key saved as ANTHROPIC_API_KEY in Script Properties — see Store API keys and secrets securely.
  • Nothing else — the script creates the briefing Doc itself.

The script

// How much text to read from each PDF. Keeps every per-file prompt
// within a sensible token budget — see "Watch out for".
const PDF_CHAR_LIMIT = 6000;

// How many bullets Claude should write per source document.
const BULLETS_PER_PDF = 3;

/**
 * Reads every PDF in a Drive folder, summarises each one, and writes
 * the lot into a single dated briefing Doc. Returns the Doc URL.
 */
function briefFolder(folderId) {
  // 1. Get an iterator over the PDFs in the folder.
  const files = DriveApp.getFolderById(folderId)
    .getFilesByType('application/pdf');

  // 2. Summarise each PDF into its own section.
  const sections = [];
  while (files.hasNext()) {
    const f = files.next();
    // Read a slice of the PDF text — enough to summarise, not so much
    // it blows the token budget.
    const text = f.getBlob().getDataAsString().slice(0, PDF_CHAR_LIMIT);
    const prompt =
      'Summarise in ' + BULLETS_PER_PDF + ' bullets. ' +
      'Source: ' + f.getName() + '.\n\n' + text;
    const summary = callClaude(prompt);
    sections.push('### ' + f.getName() + '\n\n' + summary);
  }

  // 3. No PDFs in the folder — stop before creating an empty Doc.
  if (!sections.length) {
    Logger.log('No PDFs in that folder — nothing to brief.');
    return null;
  }

  // 4. Create a dated Doc and write every section into it.
  const today = new Date().toISOString().slice(0, 10);
  const doc = DocumentApp.create('Briefing — ' + today);
  doc.getBody().setText(sections.join('\n\n'));
  doc.saveAndClose();

  Logger.log('Briefed ' + sections.length + ' PDFs into ' + doc.getUrl());
  return doc.getUrl();
}

/**
 * Minimal Anthropic API call. The key lives in Script Properties — it
 * is never pasted into the code.
 */
function callClaude(prompt, model = 'claude-haiku-4-5-20251001', maxTokens = 400) {
  const key = PropertiesService.getScriptProperties()
    .getProperty('ANTHROPIC_API_KEY');
  const res = UrlFetchApp.fetch('https://api.anthropic.com/v1/messages', {
    method: 'post',
    contentType: 'application/json',
    headers: { 'x-api-key': key, 'anthropic-version': '2023-06-01' },
    payload: JSON.stringify({
      model,
      max_tokens: maxTokens,
      messages: [{ role: 'user', content: prompt }],
    }),
    muteHttpExceptions: true,
  });
  return JSON.parse(res.getContentText()).content[0].text.trim();
}

How it works

  1. briefFolder takes a folder ID and gets an iterator over just the PDF files inside it, so other file types in the folder are ignored.
  2. For each PDF it reads a slice of the document text — capped at PDF_CHAR_LIMIT to keep every prompt within a sensible token budget — and asks Claude to condense it to BULLETS_PER_PDF bullets, naming the source.
  3. Each summary becomes a section, prefixed with the file name as a heading, and is pushed onto the sections list.
  4. If the folder held no PDFs at all, the script logs that and returns without creating an empty Doc.
  5. It creates a Doc named with today’s date, writes every section into the body, and saves. The function returns the Doc’s URL so a caller can link to it.

Example run

Say the Drive folder holds three PDFs: Market Outlook Q1.pdf, Vendor Review.pdf, and Customer Survey Notes.pdf. After a run, the new briefing Doc reads:

Briefing — 2026-05-25

Market Outlook Q1.pdf

  • Demand in the core segment grew 8% year on year.
  • Two new competitors entered the mid-market tier.
  • Pricing pressure is expected to ease by Q3.

Vendor Review.pdf

  • Current vendor met SLA targets in 11 of 12 months.
  • Support response times slipped in the busy quarter.
  • Renewal terms are broadly unchanged.

Customer Survey Notes.pdf

  • Onboarding remains the most common friction point.
  • Satisfaction with support scored highest overall.
  • Several customers asked for better reporting.

Three PDFs nobody had time to open become one page that takes a minute to read.

Run it

This is an on-demand job — run it when a folder of research has built up:

  1. In the Apps Script editor, call briefFolder('your-folder-id') from a small wrapper function, or paste the folder ID into a test run.
  2. Approve the authorisation prompt the first time.
  3. Open the briefing Doc from the URL in the execution log.

To produce the briefing on a schedule, wrap the call in a no-argument function with the folder ID baked in, then add a time-driven trigger for it.

Watch out for

  • PDF text extraction is rough. getDataAsString works well for text-based PDFs but returns garbled output for scanned or image-only documents — those need OCR before this script can read them.
  • Only the first PDF_CHAR_LIMIT characters are read. A long report is summarised from its opening pages, which may miss conclusions buried at the end. Raise the cap for short folders, or summarise in chunks.
  • One API call per PDF. A folder of many documents takes time and tokens; for large folders, expect a longer run and watch the six-minute execution limit.
  • A new Doc is created every run. Repeated runs leave a trail of dated briefings in your Drive — tidy them up, or write into a fixed Doc instead.
  • The briefing is only as current as the folder. Old PDFs left in place will be re-summarised every time, so prune the folder between runs.

Related