Skip to content
← Public packages

@charlesmatte/granola

Search and read Granola meeting notes, summaries and transcripts from any agent; manage webhooks and legal holds safely.

src/notes/transcript.ts

53 lines · 2.3 KB · TypeScript
Raw
import { listEndpoint, pageSpec, parseWithAccount } from '../lib/api.ts'
import { transcriptToText, type TranscriptItem } from '../lib/transcript.ts'
import { NOTE_ID_PATTERN } from '../lib/validate.ts'

/**
 * Fetch a meeting transcript in cursor pages (`GET /v1/notes/{note_id}/transcript`, up to 100
 * items per page) — works for transcripts too large for ./notes/get. By default fetches the whole
 * transcript and returns both the raw items and a readable text rendering.
 *
 * @param input.account - Account name (secret `granola-<account>`). Required. @param input.secretName - Secret override.
 * @param input.noteId - `not_…` id (from ./notes/list). Required.
 * @param input.format - `'text'` (default: `[m:ss] Speaker: text`, same-speaker lines merged), `'items'` (raw), or `'both'`.
 * @param input.timestamps - Include `[m:ss]` offsets in text (default true).
 * @param input.limit - Max items (1-50000, default 50000 = whole transcript). @param input.paginate - Follow cursors (default true).
 * @param input.cursor - Continue from a previous `cursor`.
 * @returns `{ account, noteId, count, text?, items?, hasMore, cursor, pages }`
 *
 * @example
 * import transcript from 'kody:@charlesmatte/granola/notes/transcript'
 *
 * const t = await transcript({ account: 'work', noteId: 'not_1d3tmYTlCICgjy' })
 */
export default async function getGranolaTranscript(input: unknown) {
	const p = parseWithAccount<{ noteId: string; format?: 'text' | 'items' | 'both'; timestamps?: boolean; limit?: number; cursor?: string; paginate?: boolean }>(
		input,
		{
			noteId: { type: 'string', required: true, pattern: NOTE_ID_PATTERN },
			format: { type: 'enum', values: ['text', 'items', 'both'] },
			timestamps: { type: 'boolean' },
			...pageSpec(50000),
		},
		'notes/transcript',
	)
	const r = await listEndpoint<TranscriptItem>(p.acct, {
		path: `/v1/notes/${p.noteId}/transcript`,
		itemsKey: 'transcript',
		limit: p.limit ?? 50000,
		maxPageSize: 100,
		cursor: p.cursor,
		paginate: p.paginate ?? true,
	})
	const format = p.format ?? 'text'
	return {
		account: p.acct.account,
		noteId: p.noteId,
		count: r.count,
		...(format !== 'items' ? { text: transcriptToText(r.items, { timestamps: p.timestamps }) } : {}),
		...(format !== 'text' ? { items: r.items } : {}),
		hasMore: r.hasMore,
		cursor: r.cursor,
		pages: r.pages,
	}
}