← 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 · TypeScriptimport { 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 }
}