One CLI for Granola: decrypt on-disk creds and drive the documented internal API (read/share/edit notes).
Project description
granola
One CLI for Granola on Windows & macOS — using Granola's own on-disk session, no API key, no password prompt. It covers two things:
- Credentials — decrypt the on-disk chain (Windows DPAPI / macOS Keychain → DEK → cred file), auto-refresh the token (single-use rotation, with safe write-back), print it, or export a portable session file for headless/Linux use.
- The documented internal API for a note — read, share, edit — plus a generic
callfor any of the ~392 internal endpoints.
Unofficial. Uses the internal
api.granola.aisurface the desktop app uses; it can change without notice. Credential decrypt works on Windows (DPAPI) and macOS (Keychain); the API layer is portable once you have a token.
Install
uv tool install . # or: pipx install .
granola auth status
Requires Python ≥ 3.10. Dependencies: httpx, cryptography.
Usage
# auth / engine
granola auth status # token status (no secrets; --include-secrets to show)
granola auth token # print a valid access token
granola auth refresh # force-refresh the selected source
granola auth export session.json # write a portable, refreshable session file (0600)
granola routes [filter] # endpoint name -> URL map
granola call <endpoint> --body '{"limit":5}' # any endpoint, raw
# read
granola notes --limit 20 # recent notes
granola get <id> # full ~50-field record (--json for all)
granola meta <id> # creator / attendees / conferencing
granola transcript <id> # transcript as markdown
granola panels <id> # AI summary panels
# share / access
granola who <id> # who has access (+ user_ids)
granola share <id> --email teammate@example.com --name "Teammate" # add collaborator
granola unshare <id> --email teammate@example.com # revoke (no email sent)
granola role <id> --user <user_id> --role viewer # change role
granola folder-who "Team Notes" # who has folder-level access
granola share-folder "Team Notes" --email teammate@example.com # folder ACL (existing users; inherited access)
granola share-folder "Team Notes" --email teammate@example.com --per-note # invite + direct access on each note
granola unshare-folder "Team Notes" --email teammate@example.com # revoke folder-level access
# edit
granola update <id> --title "New title"
granola delete <id> --yes # PERMANENT hard delete
Roles: owner · collaborator · viewer.
See docs/api-gotchas.md for endpoint quirks baked into the
typed verbs.
Headless / portable sessions
Credential decrypt needs the Keychain (macOS) or DPAPI (Windows), so it only runs on the machine you signed in on. To drive the API from a headless Linux box or CI, export a session file once and carry it over:
# On your macOS/Windows machine (where Granola is signed in):
granola auth export ~/session.json # minimal, refreshable, written 0600
# Sign OUT of the Granola desktop app, so only this session holds the refresh token.
# (Desktop + session sharing one single-use refresh token will fight and log each
# other out on rotation.)
# On the headless box:
export GRANOLA_SESSION=~/session.json
granola notes --limit 20 # refreshes + writes back to the file in place
Every command takes global auth options (before the command) that pick the token source:
--email EMAIL select an account from local desktop credentials
--session PATH use a refreshable session file
--access-token TOKEN use this bearer token directly (no refresh)
--no-refresh never auto-refresh
Environment equivalents: GRANOLA_SESSION, GRANOLA_ACCESS_TOKEN.
Precedence: a refreshable session (--session / GRANOLA_SESSION) beats a static
token (--access-token / GRANOLA_ACCESS_TOKEN); a flag beats its env var; the desktop
store is the fallback. If both a static token and a session are set, the session wins and
the CLI warns — a static token can't refresh and would silently go stale.
Use --access-token (or granola auth export --no-refresh-token) for short-lived CI where
you don't want a long-lived rotating secret on the box.
If a session's refresh token ever dies (the desktop rotated it, or it was revoked), you
can't re-bootstrap headlessly — re-run granola auth export on your macOS/Windows machine
and copy the file over.
Platforms
| Windows | macOS | |
|---|---|---|
| Data dir | %APPDATA%\Granola |
~/Library/Application Support/Granola |
| Key source | Local State → DPAPI (CurrentUser) |
login Keychain item Granola Safe Storage / Granola Key |
storage.dek unwrap |
AES-256-GCM (Chromium key) | AES-128-CBC (PBKDF2 saltysalt/1003 — Electron safeStorage) |
| Cred file | stored-accounts.json.enc (accounts[].tokens) |
supabase.json.enc (workos_tokens) |
| Final decrypt | AES-256-GCM(DEK) | AES-256-GCM(DEK) |
The decrypt must run as the logged-in user (DPAPI / Keychain are user-scoped). On macOS
the first run may show a Keychain access prompt for the Granola Safe Storage item —
allow it. The macOS crypto is verified against
harperreed/muesli's known-good vectors
(tests/test_macos_crypto.py).
Layout
granola/
config, crypto, _http, store # engine: decrypt the on-disk cred chain
auth.py token primitives: refresh exchange, expiry, status (persistence-free)
sources.py token sources (desktop / session-file / static) + precedence
routes, client # endpoint resolution + API client
notes.py read ops (list/get/meta/transcript/panels)
sharing.py collaborators (who/share/unshare/role/share-folder)
editing.py update-document / hard-delete
cli.py the `granola` command
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file granola_cli-0.1.0.tar.gz.
File metadata
- Download URL: granola_cli-0.1.0.tar.gz
- Upload date:
- Size: 54.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed0d7849b8553c2735e0fe0615a3e35e8c6014e96ececc1a22690ae9e2d63469
|
|
| MD5 |
2c680875d2ca26a6c35a7321607a3e1d
|
|
| BLAKE2b-256 |
73d377e15e0dff9d49895d3e2f9daf0ebe2471aa8cf8a80c8e73ea6695ddfd28
|
Provenance
The following attestation bundles were made for granola_cli-0.1.0.tar.gz:
Publisher:
release.yml on xuelongmu/granola-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
granola_cli-0.1.0.tar.gz -
Subject digest:
ed0d7849b8553c2735e0fe0615a3e35e8c6014e96ececc1a22690ae9e2d63469 - Sigstore transparency entry: 1743536026
- Sigstore integration time:
-
Permalink:
xuelongmu/granola-cli@ec40ea3b3763d57ddf456b2ff7b6753339f36262 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/xuelongmu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ec40ea3b3763d57ddf456b2ff7b6753339f36262 -
Trigger Event:
push
-
Statement type:
File details
Details for the file granola_cli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: granola_cli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 31.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
289cce2f8fbeb16b88ca5648e81868127f224f207d7c1a4a0b20d3999e02b1f0
|
|
| MD5 |
754e3e814125f4525a9f53125945b805
|
|
| BLAKE2b-256 |
7213aada5060da3c709cc45ca7433e8027f8035a8b2a33dbd23fd1897bfbd495
|
Provenance
The following attestation bundles were made for granola_cli-0.1.0-py3-none-any.whl:
Publisher:
release.yml on xuelongmu/granola-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
granola_cli-0.1.0-py3-none-any.whl -
Subject digest:
289cce2f8fbeb16b88ca5648e81868127f224f207d7c1a4a0b20d3999e02b1f0 - Sigstore transparency entry: 1743536372
- Sigstore integration time:
-
Permalink:
xuelongmu/granola-cli@ec40ea3b3763d57ddf456b2ff7b6753339f36262 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/xuelongmu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ec40ea3b3763d57ddf456b2ff7b6753339f36262 -
Trigger Event:
push
-
Statement type: