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/legal-holds/create.ts

48 lines · 2.7 KB · TypeScript
Raw
import { mutationSpec, parseWithAccount } from '../lib/api.ts'
import { granolaRequest } from '../lib/client.ts'
import { echo, mutationMode, preview } from '../lib/guard.ts'
import { custodianInputs } from '../lib/holds.ts'

/**
 * Place a named legal hold (`POST /v1/legal-holds`): Granola preserves the covered people's notes,
 * transcripts and attachments while the hold is active (custodians see no change). Scope is always
 * explicit: `coversEntireWorkspace` is required; custodians (emails or `usr_…` ids, ≤100) are optional.
 * WRITE: previews unless `confirm: true`. Needs a Legal hold API key (Enterprise).
 *
 * @param input.account - Account whose secret holds a Legal hold API key. Required. @param input.secretName - Secret override.
 * @param input.name - Unique among live holds (≤200 chars). Required.
 * @param input.description - ≤2000 chars.
 * @param input.coversEntireWorkspace - `true` = every current and future member. Required.
 * @param input.custodians - Member emails or user ids to name on the hold.
 * @param input.dryRun - Preview only. @param input.confirm - `true` to create.
 * @returns Preview `{ dryRun: true, request }`, or `{ executed: true, legalHold }`
 *
 * @example
 * import createHold from 'kody:@charlesmatte/granola/legal-holds/create'
 *
 * const preview = await createHold({ account: 'work-legal', name: 'Matter 2026-14', coversEntireWorkspace: false, custodians: ['person@example.com'] })
 */
export default async function createGranolaLegalHold(input: unknown) {
	const p = parseWithAccount<{ name: string; description?: string; coversEntireWorkspace: boolean; custodians?: Array<string>; dryRun?: boolean; confirm?: boolean }>(
		input,
		{
			name: { type: 'string', required: true, maxLength: 200 },
			description: { type: 'string', maxLength: 2000 },
			coversEntireWorkspace: { type: 'boolean', required: true },
			custodians: { type: 'stringArray', maxItems: 100 },
			...mutationSpec,
		},
		'legal-holds/create',
	)
	const body = {
		name: p.name,
		...(p.description !== undefined ? { description: p.description } : {}),
		covers_entire_workspace: p.coversEntireWorkspace,
		...(p.custodians?.length ? { custodians: custodianInputs(p.custodians) } : {}),
	}
	const mode = mutationMode(p)
	const target = `legal hold "${p.name}" (${p.coversEntireWorkspace ? 'entire workspace' : 'named custodians only'}${p.custodians?.length ? ` + ${p.custodians.length} custodian(s)` : ''})`
	if (!mode.execute) return preview(p.acct, 'legal-holds/create', target, mode, { method: 'POST', path: '/v1/legal-holds', body })
	const res = await granolaRequest(p.acct, { method: 'POST', path: '/v1/legal-holds', json: body, retries: 1 })
	return { dryRun: false, executed: true, echo: echo(p.acct, 'legal-holds/create', target, mode), legalHold: res.data }
}