Skip to main content
9 toolsAPI key
granola_example.py
Granola takes notes during a meeting and writes the summary afterwards. This API is how a program reads those notes back: the summary, the transcript, who attended, and the calendar event the meeting came from. Seven of the nine tools are GETs. Notes and folders are created in the Granola app, and the API has no endpoint that writes either one, so the only things a program can create, change or delete here are webhook endpoints.

Authenticating

This pack takes an API key in Authorization, and configure() is optional when $GRANOLA_API_KEY is set. There is no authorization server, no consent screen and no refresh — API keys is the whole story. The key is prefixed grn_… and goes out as a bearer token. It is created in the desktop app, under Settings → Connectors → API keys, and the notes it can reach are chosen at that moment: a key carries Personal notes, Public notes, or both. No parameter on any tool widens that, so a listing that comes back short is usually a key question rather than a filter question.

The client

api_key_tool_factory is the whole client: a thin wrapper over httpx that attaches your key and these endpoint constants to each request. No vendor SDK enters your dependency tree.

Paging through a list

A cursor belongs to the tool that returns it, so it is declared on that tool’s builder call:
granola_pagination.py
Pagination is declared on notes_list, notes_transcript_get, folders_list and audit_list. The other 5 take no cursor.
Granola versions in the path, not in a header. There is no Granola-Version to send, so v1 is written into every url_template and moving to a later version is an edit to this pack.

Tools

Each is a Tool, called with ainvoke as in the snippet above. The name links to its parameters, its response and what it costs.

What each tool is for

notes_list finds notes by date, and optionally inside one folder and its subfolders. It returns each note’s id, title, owner and timestamps, not its content. notes_get reads one note in full. include="transcript" adds the transcript inline. notes_transcript_get reads a transcript a page at a time. It is the right tool for a long meeting, and the only tool for a transcript notes_get refused to inline. folders_list returns a flat, alphabetical listing where each folder names its parent in parent_folder_id. Use it to find the folder_id that narrows a note listing, or the folder_ids that narrow a webhook endpoint. audit_list reads the workspace audit log. It is the one tool here that is not about a note: membership changes, note views and recordings, each with who did it and how the request reached Granola. The four webhook_endpoints_* tools register a delivery URL, list what is registered, change or pause one, and delete one.

Gotchas

webhook_endpoints_create is the only response that carries signing_secret. It is absent from every list and update response, and Granola cannot reissue it. A caller that discards it has to delete the endpoint and create another.
notes_get with include="transcript" returns 413 TRANSCRIPT_TOO_LARGE rather than a truncated transcript. Charter raises that as an APIError with status_code 413. Read the transcript with notes_transcript_get instead, which pages it.
The API only returns notes that already have a generated AI summary and transcript. One still processing is absent from notes_list and answers 404 from notes_get. That is a different thing from the note not existing, and a retry later finds it.
Parameters, bodies and response fields are snake_case throughout. The boolean that ends a paginated response is not. Pagination reads it as the authoritative end of the list, because cursor is null on the last page and the boolean answers the question directly.
It is the one list endpoint here with no cursor and no hasMore, so it declares no Pagination. The other four list endpoints all page the same way.
Granola says so outright: fewer events than page_size does not mean the end. The declared pagination reads hasMore, so a walk built on Pagination.next_page_args is correct. A walk that counts events is not.
api_key, user, system or anonymous, discriminated on object. system means no person was involved. anonymous means a person acted without signing in, and context.ip_address is then the only attribution there is. The data object is open, and its keys are camelCase because they are the names Granola records internally.
A transcript repeats a four-key speaker object on every line, so the pack collapses it to the one word it spells: the resolved name where there is one, otherwise me or them, otherwise the Speaker A bucket, otherwise the audio source. A note returns its summary twice, as summary_text and summary_markdown, and its private notes twice the same way. The markdown is kept and the plain-text twin dropped. Where the markdown is null, the text survives, so no note comes back with no summary.
Granola signs deliveries following the Standard Webhooks specification: an HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{body}, keyed with the base64-decoded signing secret. That is a computation your receiver runs on an inbound request, not a call to an API, so no tool can express it. Granola’s webhooks page writes out the algorithm.