MemorySync
API Reference

Granola Configuration

Choose which Granola folders a connection reads, declare whether each is a team space or a private one, and map meeting participants to MemorySync users so a note becomes the right person's memory.

Operations on this page

OperationMethod and pathPythonNode.js
List selectable foldersGET …/{connection_id}/granola/available-foldersconnections.granola.available_foldersconnections.granola.availableFolders
List selected foldersGET …/{connection_id}/granola/foldersconnections.granola.foldersconnections.granola.folders
Select foldersPOST …/{connection_id}/granola/foldersconnections.granola.add_foldersconnections.granola.addFolders
Deselect a folderDELETE …/{connection_id}/granola/folders/{folder_id}connections.granola.remove_folderconnections.granola.removeFolder
Read the exclusion policyGET …/{connection_id}/granola/exclusion-policyconnections.granola.exclusion_policyconnections.granola.exclusionPolicy
Replace the exclusion policyPUT …/{connection_id}/granola/exclusion-policyconnections.granola.set_exclusion_policyconnections.granola.setExclusionPolicy
List identity mappingsGET …/{connection_id}/granola/identitiesconnections.granola.identitiesconnections.granola.identities
Link an identityPOST …/{connection_id}/granola/identities/linkconnections.granola.link_identityconnections.granola.linkIdentity
Re-run identity matchingPOST …/{connection_id}/granola/identities/relinkconnections.granola.relink_identityconnections.granola.relinkIdentity
Read settingsGET …/{connection_id}/granola/settingsconnections.granola.settingsconnections.granola.settings
Update settingsPUT …/{connection_id}/granola/settingsconnections.granola.set_settingsconnections.granola.setSettings

Authentication and scope

RequirementContract
CredentialAn API key sent as X-API-Key. Connector operations are not end-user scoped.
Read scopeintegrations:read for every GET.
Write scopeintegrations:write for every POST, PUT, PATCH and DELETE.
TenantDerived from the authenticated key. There is no tenant parameter to pass or to get wrong.
X-End-User-IDNot used. A connection belongs to the organization, not to one end user.

Before you start

Granola is connected with an API key: use create_with_api_key on Connection Lifecycle. Everything here assumes connection["status"] == "connected".

1. See which folders are available

Lists the folders the key can see, annotated for a picker. It also reports whether private notes are enabled and how much of the participant roster is already matched — both of which decide what is safe to select.

import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
)
available = client.connections.granola.available_folders("conn_8f21a4")
print(available["total"], "folders")
print("private notes enabled:", available["private_notes_enabled"])
print("identity coverage:", available["identity_coverage"])
for folder in available["folders"]:
print(folder["id"], folder["name"], folder["selectable"], folder["reason"])
GET/api/v2/integrations/connections/{connection_id}/granola/available-folders
200 OK
Response fieldMeaning
foldersEach with id, name, path, parent_folder_id, approved, approved_visibility, selectable and reason.
totalHow many were found.
private_notes_enabledWhether this deployment permits indexing private folders at all.
identity_coverageA map of counts describing how much of the roster is mapped.

2. Select folders, and declare their visibility

Each selected folder carries a visibility. This is the most consequential field on the page: it tells MemorySync whether a folder is a team space everyone can see or a restricted one shared with a few people, and that decides who the notes inside it can be surfaced to.

import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
)
result = client.connections.granola.add_folders(
"conn_8f21a4",
folders=[
{"id": "fold_a1", "name": "Product Team", "visibility": "workspace"},
{"id": "fold_b2", "name": "Leadership", "visibility": "private"},
],
)
print(result["sync_triggered"], result["webhook_registered"])
for approved in result["approved"]:
print(approved["folder_id"], approved["visibility"], approved["state"])
for rejected in result["rejected"]:
print("rejected:", rejected)
POST/api/v2/integrations/connections/{connection_id}/granola/folders
200 OK
Request fieldContract
foldersRequired. A list of objects, each with id, and optionally name, parent_folder_id and visibility.
visibilityDefaults to workspace. Use private for a folder shared with a subset of people.
Response fieldMeaning
approvedThe folder records that were stored.
rejectedEntries refused, with a reason. Surface these.
sync_triggeredWhether selecting started a sync.
webhook_registeredWhether a webhook was registered so later notes arrive without polling.

3. Read back what is selected

Another bare-array response — iterate it directly.

import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
)
for folder in client.connections.granola.folders("conn_8f21a4"):
print(folder["folder_id"], folder["folder_name"], folder["visibility"], folder["state"])
print(" notes synced:", folder["notes_synced"], "indexed:", folder["notes_indexed"])
GET/api/v2/integrations/connections/{connection_id}/granola/folders
200 OK
FieldMeaning
idThe approval record. folder_id is Granola's own id.
folder_name, folder_path, parent_folder_idWhere it sits. All nullable.
visibilityWorkspace or private, as declared.
state, state_detail, is_activeApproval state and whether future syncs read it.
notes_syncedLifetime tally of notes read across every run.
notes_indexedDistinct notes currently stored. This is the real note count.
last_updated_at, last_synced_at, authorized_atTimestamps.

4. Block folders by name pattern

The same pattern mechanism as Slack, applied to folder names: globs, case-insensitive, a leading # tolerated, up to 200 patterns.

import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
)
client.connections.granola.set_exclusion_policy(
"conn_8f21a4",
tenant_patterns=["1:1*", "interview*", "personal*"],
)
policy = client.connections.granola.exclusion_policy("conn_8f21a4")
print(policy["tenant_patterns"], policy["effective_patterns"])
PUT/api/v2/integrations/connections/{connection_id}/granola/exclusion-policy
200 OK
GET/api/v2/integrations/connections/{connection_id}/granola/exclusion-policy
200 OK
FieldMeaning
tenant_patternsRequest and response. What you set; the only field PUT reads.
deployment_patternsResponse only. The operator-set floor.
effective_patternsResponse only. Both combined.

5. Map participants to MemorySync users

A Granola note is a meeting with participants. Without a mapping, a note is not attributed to a person, so querying as that person will not find it. Mapping is by email.

import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
)
roster = client.connections.granola.identities("conn_8f21a4")
print(roster["total"], "matched:", roster["matched"], "unmatched:", roster["unmatched"])
for identity in roster["identities"]:
print(identity["granola_email"], identity["is_external"], identity["match_method"])
client.connections.granola.link_identity(
"conn_8f21a4",
granola_email="sam@example.com",
memorysync_user_id=4821,
)
GET/api/v2/integrations/connections/{connection_id}/granola/identities
200 OK
POST/api/v2/integrations/connections/{connection_id}/granola/identities/link
200 OK
FieldMeaning
granola_emailRequest and response. The participant's email in Granola. Required on link.
memorysync_user_idThe numeric MemorySync user to attribute notes to. Omit or send null to unlink.
granola_name, memorysync_emailResponse only. For display.
is_externalResponse only. Whether the participant is outside your organization.
match_methodResponse only. How the mapping was arrived at — automatic or explicit.
Roster fieldMeaning
identitiesThe participants.
total, matched, unmatchedCoverage. Drive an onboarding checklist from unmatched.

6. Re-run the matching

Re-matches the participant roster so newly added people are picked up. It takes no arguments and creates no mappings by itself — explicit links stay explicit.

import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
)
roster = client.connections.granola.relink_identity("conn_8f21a4")
print(roster["matched"], "of", roster["total"])
POST/api/v2/integrations/connections/{connection_id}/granola/identities/relink
200 OK

7. Decide whether transcripts are indexed

The settings surface is one switch plus three read-only values. index_transcripts is the significant one: a transcript is far longer than a summary and produces far more content, most of it conversational filler.

import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
)
settings = client.connections.granola.settings("conn_8f21a4")
print(settings["index_transcripts"], settings["history_days"])
print(settings["max_notes_per_sync"], settings["webhook_registered"])
client.connections.granola.set_settings("conn_8f21a4", index_transcripts=False)
GET/api/v2/integrations/connections/{connection_id}/granola/settings
200 OK
PUT/api/v2/integrations/connections/{connection_id}/granola/settings
200 OK
FieldMeaning
index_transcriptsThe only writable field. PUT requires it.
history_daysRead-only. How far back a first sync reaches.
max_notes_per_syncRead-only. The ceiling on one run.
webhook_registeredRead-only. Whether new notes arrive by webhook rather than polling.

8. Deselect a folder

Stops future syncs reading it. Notes and memories already imported are kept.

import os
from memorysync import MemorySyncClient
client = MemorySyncClient(
api_key=os.environ["MEMORYSYNC_API_KEY"],
base_url="https://api.memorysync.io",
)
client.connections.granola.remove_folder("conn_8f21a4", "fold_b2")
DELETE/api/v2/integrations/connections/{connection_id}/granola/folders/{folder_id}
204 No Content

Errors and next action

StatusMeaningNext action
401Missing, malformed or inactive API key.Check server configuration without printing the key.
403The key lacks integrations:read or integrations:write.Grant the scope on the key, or use a key that has it.
404The connection, object or job is not visible to this tenant.Confirm the identifier belongs to this organization.
409The connection is in a state that forbids the operation.Read the connection status first and act on it.
429Rate limited, either by MemorySync or by the upstream provider.Back off; do not tighten a polling loop in response.
5xxService failure.Treat a write outcome as uncertain and reconcile by reading the connection back.