From 2a546ff6542a1be94fa4edcbfb9a030a813e9e26 Mon Sep 17 00:00:00 2001 From: sohamd22 <85427822+sohamd22@users.noreply.github.com> Date: Thu, 15 Oct 2026 18:52:41 +0000 Subject: [PATCH] docs(connectors): align connector docs with the v5 API (#1812) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes connector docs. - **`authorization.url` examples.** They now show the URL the API actually returns (`/v3/connections/auth/redirect?state=…`), not provider URLs. - **Broken examples:** - TS `connectors.create` examples were missing commas. - Python dicts now use the SDK's snake_case keys. - `connector.createdAt` → `connector.system.createdAt`. - The list default `limit` is 10. - **Pending connectors.** They are visible through get (`pending_authorization`), just not in list. - **Gmail:** - It needs the Max plan. - Nothing syncs until labels are picked. - The watch covers the selected labels, not just INBOX. - `from` filters use `contains`. - **Smaller fixes:** - The OAuth redirect-URI troubleshooting. - Notion property metadata keys. - Picker error codes. Checked against the current API and the published SDKs (TS 5.0.1, Python 5.0.0). Legacy v3/v4 docs are unchanged apart from factual fixes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- apps/docs/connectors/github.mdx | 18 +++++++------ apps/docs/connectors/gmail.mdx | 28 +++++++++++---------- apps/docs/connectors/google-drive.mdx | 22 ++++++++-------- apps/docs/connectors/granola.mdx | 4 +-- apps/docs/connectors/managing-resources.mdx | 24 ++++++++++-------- apps/docs/connectors/notion.mdx | 22 ++++++++-------- apps/docs/connectors/onedrive.mdx | 16 ++++++------ apps/docs/connectors/overview.mdx | 26 ++++++++++--------- apps/docs/connectors/s3.mdx | 8 +++--- apps/docs/connectors/troubleshooting.mdx | 20 ++++++--------- apps/docs/connectors/web-crawler.mdx | 20 +++++++-------- apps/docs/v5/api-reference/connectors.mdx | 6 ++--- 12 files changed, 112 insertions(+), 102 deletions(-) diff --git a/apps/docs/connectors/github.mdx b/apps/docs/connectors/github.mdx index 25140f18..219b0637 100644 --- a/apps/docs/connectors/github.mdx +++ b/apps/docs/connectors/github.mdx @@ -24,7 +24,7 @@ The GitHub connector requires a **Scale Plan** or **Enterprise Plan**. const connector = await supermemory.connectors.create("user-123", { provider: "github", - redirectUrl: "https://yourapp.com/auth/github/callback" + redirectUrl: "https://yourapp.com/auth/github/callback", documentLimit: 5000, }) @@ -44,8 +44,8 @@ The GitHub connector requires a **Scale Plan** or **Enterprise Plan**. "user-123", request={ "provider": "github", - "redirectUrl": "https://yourapp.com/auth/github/callback", - "documentLimit": 5000, + "redirect_url": "https://yourapp.com/auth/github/callback", + "document_limit": 5000, }, ) @@ -68,7 +68,7 @@ The GitHub connector requires a **Scale Plan** or **Enterprise Plan**. # Response: { # "id": "PTzGiUYei7pgzg5buzZHgA", # "authorization": { - # "url": "https://github.com/login/oauth/authorize?...", + # "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...", # "expiresAt": "2024-01-15T11:30:00.000Z" # } # } @@ -85,7 +85,7 @@ The GitHub connector requires a **Scale Plan** or **Enterprise Plan**. ### 2. Handle OAuth callback -Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, GitHub redirects to your `redirectUrl`. A pending OAuth connector is not visible in list or get until the user finishes authorization. Once it is visible, the user can select which repositories to sync. +Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, Supermemory sends them to a hosted repository picker; once they choose repositories, they return to your `redirectUrl`. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with `system.status: "pending_authorization"`. ### 3. Select Repositories @@ -224,7 +224,9 @@ Webhooks are secured using HMAC-SHA256 signature validation with constant-time c print(f"Webhooks: {connector.capabilities.webhooks}") print(f"Last synced: {connector.system.last_successful_sync_at}") - print(f"Last sync: {connector.latest_run.system.status} {connector.latest_run.error}") + print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}") + if connector.latest_run and connector.latest_run.error: + print(connector.latest_run.error) repos = (connector.selection or {}).get("repos") or [] print(f"Repositories: {[r.name for r in repos]}") print(f"Recent runs: {connector.syncs}") @@ -255,7 +257,7 @@ Webhooks are secured using HMAC-SHA256 signature validation with constant-time c console.log(`Provider: ${connector.provider}`) console.log(`ID: ${connector.id}`) console.log(`Account: ${connector.account}`) - console.log(`Created: ${connector.createdAt}`) + console.log(`Created: ${connector.system.createdAt}`) console.log(`Document limit: ${connector.documentLimit}`) console.log(`Repositories: ${connector.selection?.repos?.length ?? 0}`) console.log("---") @@ -271,7 +273,7 @@ Webhooks are secured using HMAC-SHA256 signature validation with constant-time c print(f"Provider: {connector.provider}") print(f"ID: {connector.id}") print(f"Account: {connector.account}") - print(f"Created: {connector.created_at}") + print(f"Created: {connector.system.created_at}") print(f"Document limit: {connector.document_limit}") repos = (connector.selection or {}).get("repos") or [] print(f"Repositories: {len(repos)}") diff --git a/apps/docs/connectors/gmail.mdx b/apps/docs/connectors/gmail.mdx index d6967e7b..72c80c6c 100644 --- a/apps/docs/connectors/gmail.mdx +++ b/apps/docs/connectors/gmail.mdx @@ -24,7 +24,7 @@ Connect Gmail to automatically sync email threads into your supermemory knowledg const connector = await supermemory.connectors.create("user-123", { provider: "gmail", - redirectUrl: "https://yourapp.com/auth/gmail/callback" + redirectUrl: "https://yourapp.com/auth/gmail/callback", documentLimit: 5000, }) @@ -44,8 +44,8 @@ Connect Gmail to automatically sync email threads into your supermemory knowledg "user-123", request={ "provider": "gmail", - "redirectUrl": "https://yourapp.com/auth/gmail/callback", - "documentLimit": 5000, + "redirect_url": "https://yourapp.com/auth/gmail/callback", + "document_limit": 5000, }, ) @@ -68,7 +68,7 @@ Connect Gmail to automatically sync email threads into your supermemory knowledg # Response: { # "id": "PTzGiUYei7pgzg5buzZHgA", # "authorization": { - # "url": "https://accounts.google.com/o/oauth2/v2/auth?...", + # "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...", # "expiresAt": "2024-01-15T11:30:00.000Z" # } # } @@ -78,7 +78,9 @@ Connect Gmail to automatically sync email threads into your supermemory knowledg ### 2. Handle OAuth callback -Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, Google redirects to your `redirectUrl` and the initial sync begins. A pending OAuth connector is not visible in list or get until the user finishes authorization. +Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, Supermemory sends them to a hosted label picker; once they choose labels, they return to your `redirectUrl` and the initial sync begins. Nothing syncs until at least one label is selected. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with `system.status: "pending_authorization"`. + +To change the labels later, read the connector with `include: "picker"` and send the user to `picker.url`, or set label ids directly with `connectors.update(namespace, id, { selection: { labels: ["INBOX"] } })`. See [Managing Connector Selection](/connectors/managing-resources). ### 3. Check Connector Status @@ -106,7 +108,7 @@ Send the user to `authorization.url` before `authorization.expiresAt`. After the connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"]) print(f"Connected email: {connector.account}") - print(f"Last sync: {connector.latest_run.system.status}") + print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}") print(f"Threads synced: {connector.document_count}") # List synced email threads in the namespace @@ -155,8 +157,8 @@ Each synced thread includes searchable metadata: | `type` | Always `gmail_thread` | | `subject` | Email subject line | | `threadId` | Gmail thread ID | -| `from` | Sender email address | -| `to` | Recipient email addresses | +| `from` | Sender, as the raw `From` header (for example `Team `) | +| `to` | Recipient addresses, comma-separated | | `date` | Date of first message | | `messageCount` | Number of messages in thread | | `attachmentCount` | Number of attachments (if any) | @@ -171,7 +173,7 @@ const results = await supermemory.search("user-123", { operator: "and", operands: [ { field: "type", operator: "eq", value: "gmail_thread" }, - { field: "from", operator: "eq", value: "team@company.com" }, + { field: "from", operator: "contains", value: "team@company.com" }, ], }, searchMode: "chunks", @@ -192,7 +194,7 @@ const results = await supermemory.search("user-123", { console.log(`Provider: ${connector.provider}`) console.log(`ID: ${connector.id}`) console.log(`Account: ${connector.account}`) - console.log(`Created: ${connector.createdAt}`) + console.log(`Created: ${connector.system.createdAt}`) console.log(`Document limit: ${connector.documentLimit}`) console.log("---") }) @@ -207,7 +209,7 @@ const results = await supermemory.search("user-123", { print(f"Provider: {connector.provider}") print(f"ID: {connector.id}") print(f"Account: {connector.account}") - print(f"Created: {connector.created_at}") + print(f"Created: {connector.system.created_at}") print(f"Document limit: {connector.document_limit}") print("---") ``` @@ -308,13 +310,13 @@ Gmail connector supports multiple sync methods: ### How real-time sync works -1. When a connector is created, supermemory registers a Gmail API "watch" subscription +1. After labels are selected, supermemory registers a Gmail API "watch" subscription when Pub/Sub is configured 2. Gmail sends notifications to a Google Cloud Pub/Sub topic when emails change 3. supermemory receives these notifications and fetches updated threads 4. Watch subscriptions expire after 7 days and are automatically renewed -Real-time sync monitors the **INBOX** label. Emails in other labels are synced via scheduled/manual sync. +Real-time sync monitors the selected labels, including labels outside **INBOX**. If multiple connectors use the same mailbox, its watch covers their combined label selections. ## Permissions & scopes diff --git a/apps/docs/connectors/google-drive.mdx b/apps/docs/connectors/google-drive.mdx index e8a16732..0ac98bd2 100644 --- a/apps/docs/connectors/google-drive.mdx +++ b/apps/docs/connectors/google-drive.mdx @@ -32,7 +32,7 @@ If you use scoped sync and the user has not finished the picker yet, **scheduled const connector = await supermemory.connectors.create("user-123", { provider: "google-drive", - redirectUrl: "https://yourapp.com/auth/google-drive/callback" + redirectUrl: "https://yourapp.com/auth/google-drive/callback", documentLimit: 3000, config: { syncScope: "selected" }, }) @@ -53,9 +53,9 @@ If you use scoped sync and the user has not finished the picker yet, **scheduled "user-123", request={ "provider": "google-drive", - "redirectUrl": "https://yourapp.com/auth/google-drive/callback", - "documentLimit": 3000, - "config": {"syncScope": "selected"}, + "redirect_url": "https://yourapp.com/auth/google-drive/callback", + "document_limit": 3000, + "config": {"sync_scope": "selected"}, }, ) @@ -79,7 +79,7 @@ If you use scoped sync and the user has not finished the picker yet, **scheduled # Response: { # "id": "PTzGiUYei7pgzg5buzZHgA", # "authorization": { - # "url": "https://accounts.google.com/o/oauth2/v2/auth?...", + # "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...", # "expiresAt": "2024-01-15T11:30:00.000Z" # } # } @@ -93,7 +93,7 @@ For **whole Drive** sync, send `"config": { "syncScope": "full" }` on the same ` ### 2. Handle OAuth callback -Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, Google redirects through Supermemory to finish the connector. A pending OAuth connector is not visible in list or get until the user finishes authorization. +Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, Google redirects through Supermemory to finish the connector. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with `system.status: "pending_authorization"`. With **scoped** sync (`syncScope` omitted or `"selected"`), the user is sent to Supermemory’s **hosted file and folder picker**; they must complete that step before syncs run. With **`syncScope: "full"`**, Supermemory redirects to your `redirectUrl` **without** the picker. @@ -173,7 +173,7 @@ If you already know the Drive file and folder ids, set them directly. A new sele print(f"Account: {connector.account}") print(f"Selection: {connector.selection}") - print(f"Last sync: {connector.latest_run.system.status}") + print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}") # List synced documents in the namespace documents = client.list("user-123", "documents").documents @@ -205,6 +205,8 @@ Based on the API type definitions, Google Drive documents are identified with th - `google_slide` - Google Slides - `google_sheet` - Google Sheets +PDF and Markdown files stored in Drive are synced too. + Drive documents are converted to markdown before ingestion. This conversion is lossy — some formatting may not be preserved. @@ -223,7 +225,7 @@ Drive documents are converted to markdown before ingestion. This conversion is l console.log(`Provider: ${connector.provider}`) console.log(`ID: ${connector.id}`) console.log(`Account: ${connector.account}`) - console.log(`Created: ${connector.createdAt}`) + console.log(`Created: ${connector.system.createdAt}`) console.log(`Document limit: ${connector.documentLimit}`) console.log("---") }) @@ -238,7 +240,7 @@ Drive documents are converted to markdown before ingestion. This conversion is l print(f"Provider: {connector.provider}") print(f"ID: {connector.id}") print(f"Account: {connector.account}") - print(f"Created: {connector.created_at}") + print(f"Created: {connector.system.created_at}") print(f"Document limit: {connector.document_limit}") print("---") ``` @@ -263,7 +265,7 @@ Drive documents are converted to markdown before ingestion. This conversion is l # "system": { "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "lastSuccessfulSyncAt": "..." } # } # ], - # "pagination": { "currentPage": 1, "limit": 50, "totalItems": 1, "totalPages": 1 } + # "pagination": { "currentPage": 1, "limit": 10, "totalItems": 1, "totalPages": 1 } # } ``` diff --git a/apps/docs/connectors/granola.mdx b/apps/docs/connectors/granola.mdx index a0b81029..4b717fc8 100644 --- a/apps/docs/connectors/granola.mdx +++ b/apps/docs/connectors/granola.mdx @@ -61,9 +61,9 @@ The console limits connector setup to 500 documents. Use the API setup below for request={ "provider": "granola", "config": { - "apiKey": os.environ["GRANOLA_API_KEY"], + "api_key": os.environ["GRANOLA_API_KEY"], }, - "documentLimit": 1000, + "document_limit": 1000, }, ) diff --git a/apps/docs/connectors/managing-resources.mdx b/apps/docs/connectors/managing-resources.mdx index fa6f2d26..e4529f4c 100644 --- a/apps/docs/connectors/managing-resources.mdx +++ b/apps/docs/connectors/managing-resources.mdx @@ -83,7 +83,7 @@ curl "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?include=pi ``` -The picker link works once, in any browser. `picker` is `null` for connectors that sync everything. +The picker link works once, in any browser. `picker` is `null` for providers that do not support selection. A Google Drive connector with full-drive sync can still open the picker; saving a selection switches it to scoped sync. Because the link changes what syncs, `include=picker` needs an admin key with write access to the namespace; otherwise the request returns `403`. ## Set the Selection Directly @@ -121,7 +121,7 @@ connector = client.connectors.update( ) print(f"Selection: {connector.selection}") -print(f"Last sync: {connector.latest_run.system.status}") +print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}") ``` ```bash cURL @@ -173,9 +173,11 @@ The updated connector object, including the saved `selection` as `{ id, name }` ### Error Responses -- `400`: Connector does not support selection +- `400`: Connector does not support selection, the selection is empty or has unknown ids, or the connector needs to be reconnected - `401`: Unauthorized +- `403`: The key lacks admin role or write access to the connector's namespaces - `404`: Connector not found +- `409`: Connector is still waiting for the user to finish authorizing **Automatic Sync:** After a selection is saved, a sync starts for the connector. You don't need to call `connectors.sync` afterwards. @@ -193,10 +195,11 @@ const created = await supermemory.connectors.create("user-123", { redirectUrl: "https://yourapp.com/callback" }) -// 2. Send the user to GitHub; the connector becomes visible once they authorize +// 2. Send the user to GitHub. After they authorize, Supermemory shows the hosted +// repository picker, then returns them to redirectUrl if (created.authorization) window.location.href = created.authorization.url -// 3. After the callback, open the hosted picker so the user chooses repositories +// 3. To change repositories later, open the hosted picker again const connector = await supermemory.connectors.get("user-123", created.id, { include: "picker", returnUrl: "https://yourapp.com/settings/integrations" @@ -215,14 +218,15 @@ created = client.connectors.create( "user-123", request={ "provider": "github", - "redirectUrl": "https://yourapp.com/callback", + "redirect_url": "https://yourapp.com/callback", }, ) -# 2. Send the user to GitHub; the connector becomes visible once they authorize +# 2. Send the user to GitHub. After they authorize, Supermemory shows the hosted +# repository picker, then returns them to redirect_url print(f"Redirect to: {created.authorization.url}") -# 3. After the callback, open the hosted picker so the user chooses repositories +# 3. To change repositories later, open the hosted picker again connector = client.connectors.get( "user-123", created.id, @@ -247,9 +251,9 @@ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \ -H "Content-Type: application/json" \ -d '{"provider": "github", "redirectUrl": "https://yourapp.com/callback"}' -# 2. ... (user authorizes at authorization.url) ... +# 2. ... (user authorizes at authorization.url and picks repositories in the hosted picker) ... -# 3. Get a one-time hosted picker URL +# 3. To change repositories later, get a one-time hosted picker URL curl "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?include=picker" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" diff --git a/apps/docs/connectors/notion.mdx b/apps/docs/connectors/notion.mdx index f979bb44..ecd9ddad 100644 --- a/apps/docs/connectors/notion.mdx +++ b/apps/docs/connectors/notion.mdx @@ -19,7 +19,7 @@ Connect Notion workspaces to automatically sync pages, databases, and content bl const connector = await supermemory.connectors.create("user-123", { provider: "notion", - redirectUrl: "https://yourapp.com/auth/notion/callback" + redirectUrl: "https://yourapp.com/auth/notion/callback", documentLimit: 2000, }) @@ -38,8 +38,8 @@ Connect Notion workspaces to automatically sync pages, databases, and content bl "user-123", request={ "provider": "notion", - "redirectUrl": "https://yourapp.com/auth/notion/callback", - "documentLimit": 2000, + "redirect_url": "https://yourapp.com/auth/notion/callback", + "document_limit": 2000, }, ) @@ -61,7 +61,7 @@ Connect Notion workspaces to automatically sync pages, databases, and content bl # Response: { # "id": "PTzGiUYei7pgzg5buzZHgA", # "authorization": { - # "url": "https://api.notion.com/v1/oauth/authorize?...", + # "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...", # "expiresAt": "2024-01-15T11:30:00.000Z" # } # } @@ -71,7 +71,7 @@ Connect Notion workspaces to automatically sync pages, databases, and content bl ### 2. Handle OAuth flow -Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants workspace access, Notion redirects to your `redirectUrl` and the first sync starts. A pending OAuth connector is not visible in list or get until the user finishes authorization. +Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants workspace access, Notion redirects to your `redirectUrl` and the first sync starts. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with `system.status: "pending_authorization"`. ### 3. Monitor sync progress @@ -97,7 +97,7 @@ Send the user to `authorization.url` before `authorization.expiresAt`. After the connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"]) print(f"Connected workspace: {connector.account}") - print(f"Last sync: {connector.latest_run.system.status}") + print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}") print(f"Documents synced: {connector.document_count}") # List synced pages and databases @@ -268,15 +268,15 @@ Notion connector respects workspace permissions: ### Database properties -Notion database properties are mapped to metadata: +Notion database properties are mapped to metadata keys named `notion_prop_`, lowercased with spaces replaced by underscores (a `Status` property becomes `notion_prop_status`). The page title is stored as `title`, and every page has `type: "notion_doc"`. ```typescript // List documents in the namespace (there is no per-connector document list in v5) const { documents } = await supermemory.list("user-123", "documents") -// Find database entries +// Find database entries by a property value const projectEntries = documents.filter(doc => - doc.metadata?.database === "Projects" + doc.metadata?.notion_prop_status === "In Progress" ) // Database properties become searchable metadata @@ -285,8 +285,8 @@ const projectWithStatus = await supermemory.search("user-123", { filter: { operator: "and", operands: [ - { field: "status", operator: "eq", value: "In Progress" }, - { field: "priority", operator: "eq", value: "High" }, + { field: "notion_prop_status", operator: "eq", value: "In Progress" }, + { field: "notion_prop_priority", operator: "eq", value: "High" }, ], }, searchMode: "chunks", diff --git a/apps/docs/connectors/onedrive.mdx b/apps/docs/connectors/onedrive.mdx index 55b4d887..f513d392 100644 --- a/apps/docs/connectors/onedrive.mdx +++ b/apps/docs/connectors/onedrive.mdx @@ -21,13 +21,13 @@ const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY }) const connector = await supermemory.connectors.create("user-123", { provider: "onedrive", - redirectUrl: "https://yourapp.com/auth/onedrive/callback" + redirectUrl: "https://yourapp.com/auth/onedrive/callback", documentLimit: 1500, }) // Send the user to Microsoft to authorize if (connector.authorization) window.location.href = connector.authorization.url -// Output: Redirects to https://login.microsoftonline.com/oauth2/authorize?... +// Output: Redirects to https://api.supermemory.ai/v3/connections/auth/redirect?state=... ``` ```python Python @@ -40,14 +40,14 @@ connector = client.connectors.create( "user-123", request={ "provider": "onedrive", - "redirectUrl": "https://yourapp.com/auth/onedrive/callback", - "documentLimit": 1500, + "redirect_url": "https://yourapp.com/auth/onedrive/callback", + "document_limit": 1500, }, ) # Send the user to Microsoft to authorize print(f"Redirect to: {connector.authorization.url}") -# Output: Redirect to: https://login.microsoftonline.com/oauth2/authorize?... +# Output: Redirect to: https://api.supermemory.ai/v3/connections/auth/redirect?state=... ``` ```bash cURL @@ -63,7 +63,7 @@ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \ # Response: { # "id": "PTzGiUYei7pgzg5buzZHgA", # "authorization": { -# "url": "https://login.microsoftonline.com/oauth2/authorize?...", +# "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...", # "expiresAt": "2024-01-15T11:30:00.000Z" # } # } @@ -73,7 +73,7 @@ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \ ### 2. Handle Microsoft OAuth -Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, Microsoft redirects to your `redirectUrl` and the initial sync begins. A pending OAuth connector is not visible in list or get until the user finishes authorization. +Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, Microsoft redirects to your `redirectUrl` and the initial sync begins. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with `system.status: "pending_authorization"`. ### 3. Monitor sync status @@ -96,7 +96,7 @@ Send the user to `authorization.url` before `authorization.expiresAt`. After the connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"]) print(f"Account: {connector.account}") - print(f"Last sync: {connector.latest_run.system.status}") + print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}") print(f"Documents synced: {connector.document_count}") # List synced Office documents in the namespace diff --git a/apps/docs/connectors/overview.mdx b/apps/docs/connectors/overview.mdx index a8b31e7e..98fbadf9 100644 --- a/apps/docs/connectors/overview.mdx +++ b/apps/docs/connectors/overview.mdx @@ -7,7 +7,7 @@ icon: "/icons/hugeicons/layers-01.svg" Connect external platforms to automatically sync documents into supermemory. Supported connectors include Google Drive, Gmail, Notion, OneDrive, GitHub, Granola and Web Crawler with real-time synchronization and intelligent content processing. -Every connector belongs to one namespace. The namespace goes in the URL (`/ns/{namespace}/connectors`) and is a top-level `namespace` key in every SDK call. +New v5 connectors belong to one namespace. The namespace goes in the URL (`/ns/{namespace}/connectors`) and is the first argument of namespace-scoped SDK calls. Organization-wide listing uses `connectors.listAll()` in TypeScript or `connectors.list_all()` in Python without a namespace argument. Legacy connectors can span multiple namespaces or have no namespace. ## Supported connectors @@ -69,7 +69,7 @@ const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY }) const connector = await supermemory.connectors.create("user-123", { provider: "notion", - redirectUrl: "https://yourapp.com/callback" + redirectUrl: "https://yourapp.com/callback", documentLimit: 5000, }) @@ -77,7 +77,7 @@ const connector = await supermemory.connectors.create("user-123", { console.log("Connector:", connector.id) console.log("Auth URL:", connector.authorization?.url) console.log("Expires at:", connector.authorization?.expiresAt) -// Output: Auth URL: https://api.notion.com/v1/oauth/authorize?... +// Output: Auth URL: https://api.supermemory.ai/v3/connections/auth/redirect?state=... ``` ```python Python @@ -90,8 +90,8 @@ connector = client.connectors.create( "user-123", request={ "provider": "notion", - "redirectUrl": "https://yourapp.com/callback", - "documentLimit": 5000, + "redirect_url": "https://yourapp.com/callback", + "document_limit": 5000, }, ) @@ -99,7 +99,7 @@ connector = client.connectors.create( print(f"Connector: {connector.id}") print(f"Auth URL: {connector.authorization.url}") print(f"Expires at: {connector.authorization.expires_at}") -# Output: Auth URL: https://api.notion.com/v1/oauth/authorize?... +# Output: Auth URL: https://api.supermemory.ai/v3/connections/auth/redirect?state=... ``` ```bash cURL @@ -115,7 +115,7 @@ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \ # Response: { # "id": "PTzGiUYei7pgzg5buzZHgA", # "authorization": { -# "url": "https://api.notion.com/v1/oauth/authorize?...", +# "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...", # "expiresAt": "2024-01-15T11:30:00.000Z" # } # } @@ -125,7 +125,7 @@ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \ ### 2. Handle OAuth callback -Send the user to `authorization.url` before `authorization.expiresAt`. When the user authorizes, the provider returns them to your `redirectUrl` and the first sync starts. A pending OAuth connector is not visible in list or get until the user finishes authorization. +Send the user to `authorization.url` before `authorization.expiresAt`. When the user authorizes, the provider returns them to your `redirectUrl` and the first sync starts. GitHub, Gmail and Google Drive (with the default `syncScope: "selected"`) first send the user through Supermemory's hosted picker to choose what to sync. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with `system.status: "pending_authorization"`. Config providers (Web Crawler, S3, Granola) return `authorization: null` and start syncing immediately. @@ -167,7 +167,9 @@ connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include= print(f"Provider: {connector.provider}") print(f"Account: {connector.account}") -print(f"Last sync: {connector.latest_run.system.status} {connector.latest_run.error}") +print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}") +if connector.latest_run and connector.latest_run.error: + print(connector.latest_run.error) print(f"Last synced at: {connector.system.last_successful_sync_at}") print(f"Documents: {connector.document_count}") print(f"Recent runs: {connector.syncs}") @@ -210,9 +212,9 @@ curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \ ### Authentication flow -1. **Create Connector**: Call `POST /ns/{namespace}/connectors` with a `provider`. OAuth providers return an `authorization` link; config providers (Web Crawler, S3, Granola) take a `config` object and return `authUrl: null` +1. **Create Connector**: Call `POST /ns/{namespace}/connectors` with a `provider`. OAuth providers return an `authorization` link; config providers (Web Crawler, S3, Granola) take a `config` object and return `authorization: null` 2. **User Authorization**: Send the user to `authorization.url` when the provider requires it -3. **Automatic Setup**: Connector established, sync begins immediately +3. **Automatic Setup**: The connector is established. Save any required label, repository, or file selection before content sync can proceed; providers without a selection step start their initial sync after setup 4. **Continuous Sync**: Real-time updates via webhooks + scheduled sync every 4 hours (or scheduled recrawling for Web Crawler) ### Document processing pipeline @@ -301,7 +303,7 @@ curl "https://api.supermemory.ai/ns/org-123/connectors" \ # "system": { "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "lastSuccessfulSyncAt": "2024-01-15T10:45:00.000Z" } # } # ], -# "pagination": { "currentPage": 1, "limit": 50, "totalItems": 1, "totalPages": 1 } +# "pagination": { "currentPage": 1, "limit": 10, "totalItems": 1, "totalPages": 1 } # } # Connectors across every namespace in the organization diff --git a/apps/docs/connectors/s3.mdx b/apps/docs/connectors/s3.mdx index 79dc8bcf..d9a83d6c 100644 --- a/apps/docs/connectors/s3.mdx +++ b/apps/docs/connectors/s3.mdx @@ -48,8 +48,8 @@ The S3 connector requires a **Scale Plan** or higher. You can also create S3 con "config": { "bucket": "my-documents-bucket", "region": "us-east-1", - "accessKeyId": os.environ["AWS_ACCESS_KEY_ID"], - "secretAccessKey": os.environ["AWS_SECRET_ACCESS_KEY"], + "access_key_id": os.environ["AWS_ACCESS_KEY_ID"], + "secret_access_key": os.environ["AWS_SECRET_ACCESS_KEY"], }, }, ) @@ -270,7 +270,9 @@ Minimum required permissions: ## Error codes -| Code | Message | Solution | +Supermemory checks the credentials against the bucket before it creates the connector. If that check fails, `POST /ns/{namespace}/connectors` returns `400` with one of these messages. The code column is the status S3 returned. + +| S3 status | Message | Solution | |------|---------|----------| | 401 | Authentication failed | Verify access key and secret | | 403 | Access denied | Check IAM permissions and bucket policy | diff --git a/apps/docs/connectors/troubleshooting.mdx b/apps/docs/connectors/troubleshooting.mdx index 146e26e2..bcbf06e8 100644 --- a/apps/docs/connectors/troubleshooting.mdx +++ b/apps/docs/connectors/troubleshooting.mdx @@ -72,7 +72,7 @@ curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?i curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ - -d '{"filter": {"field": "source", "operator": "eq", "value": "notion"}}' + -d '{"filter": {"field": "type", "operator": "eq", "value": "notion_doc"}}' ``` @@ -85,30 +85,26 @@ There is no per-connector document list in v5. `supermemory.list(namespace, "doc ### Connector Missing From List -**Problem:** `connectors.create` returned an `id`, but `connectors.list` and `connectors.get` do not show it +**Problem:** `connectors.create` returned an `id`, but `connectors.list` does not show it -**Solution:** A pending OAuth connector is not visible in list or get until the user finishes authorization. Send the user to `authorization.url` before `authorization.expiresAt`. If the link expired, create the connector again. +**Solution:** A pending OAuth connector is not returned by list until the user finishes authorization. `connectors.get` still returns it with `system.status: "pending_authorization"`. Send the user to `authorization.url` before `authorization.expiresAt`. If the link expired, create the connector again. ### OAuth callback fails **Problem:** "Invalid redirect URI" error after user grants permissions -**Solution:** Ensure your redirect URL matches EXACTLY what's configured in your OAuth app: +**Solution:** The provider always redirects to Supermemory's callback, not to your `redirectUrl`. If you use a [custom OAuth application](/connectors/overview#custom-oauth-applications), its redirect URI must be exactly `https://api.supermemory.ai/v3/connections/auth/callback/{provider}`. Your `redirectUrl` is where Supermemory sends the user after it handles that callback, and must be an `http` or `https` URL: ```typescript -// correct - exact match with OAuth app settings const connector = await supermemory.connectors.create("user-123", { provider: "notion", redirectUrl: "https://yourapp.com/auth/notion/callback" }) - -// Wrong - URL doesn't match -// redirectUrl: "https://yourapp.com/callback" ``` **Prevention:** - Use HTTPS for production URLs -- Copy the exact URL from your OAuth app settings +- Register `https://api.supermemory.ai/v3/connections/auth/callback/{provider}` in your custom OAuth app settings - Test the flow in development first ### Documents not syncing @@ -159,7 +155,7 @@ if (next.authorization) window.location.href = next.authorization.url ```typescript const connector = await supermemory.connectors.create("user-123", { provider: "onedrive", - redirectUrl: "https://yourapp.com/callback" + redirectUrl: "https://yourapp.com/callback", documentLimit: 500, // Start with fewer documents }) @@ -247,9 +243,9 @@ const next = await supermemory.connectors.create("user-123", { }) ``` -**Scale Plan Required Error** +**Max Plan Required Error** -Gmail connector requires Scale Plan or Enterprise Plan. If you see access errors: +Gmail connector requires Max Plan or above. If you see access errors: - Verify your organization has the required plan - Contact support to upgrade your plan diff --git a/apps/docs/connectors/web-crawler.mdx b/apps/docs/connectors/web-crawler.mdx index f64838dc..d1280316 100644 --- a/apps/docs/connectors/web-crawler.mdx +++ b/apps/docs/connectors/web-crawler.mdx @@ -48,16 +48,16 @@ The web crawler connector requires a **Scale Plan** or **Enterprise Plan**. request={ "provider": "web-crawler", "config": { - "startUrl": "https://docs.example.com", - "crawlDepth": 3, + "start_url": "https://docs.example.com", + "crawl_depth": 3, }, - "documentLimit": 5000, + "document_limit": 5000, }, ) # Web crawler doesn't require OAuth; crawling starts immediately print(f"Connector ID: {connector.id}") - # Note: connector.authorization.url is None for web-crawler + # Note: connector.authorization is None for web-crawler ``` @@ -120,7 +120,7 @@ Unlike OAuth connectors, the web crawler doesn't require user authorization. `au connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"]) print(f"Start URL: {(connector.config or {}).get('startUrl')}") - print(f"Last sync: {connector.latest_run.system.status}") + print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}") print(f"Pages synced: {connector.document_count}") # List synced web pages in the namespace @@ -229,7 +229,7 @@ Start a crawl now. The call returns `409` while a sync for that connector is alr webCrawlerConnectors.forEach(connector => { console.log(`Start URL: ${connector.config?.startUrl}`) console.log(`Connector ID: ${connector.id}`) - console.log(`Created: ${connector.createdAt}`) + console.log(`Created: ${connector.system.createdAt}`) }) ``` @@ -241,7 +241,7 @@ Start a crawl now. The call returns `409` while a sync for that connector is alr for connector in connectors: print(f"Start URL: {(connector.config or {}).get('startUrl')}") print(f"Connector ID: {connector.id}") - print(f"Created: {connector.created_at}") + print(f"Created: {connector.system.created_at}") ``` @@ -256,12 +256,12 @@ Start a crawl now. The call returns `409` while a sync for that connector is alr # "id": "PTzGiUYei7pgzg5buzZHgA", # "provider": "web-crawler", # "namespace": "user-123", - # "createdAt": "2024-01-15T10:30:00.000Z", # "documentLimit": 5000, - # "config": {"startUrl": "https://docs.example.com", "crawlDepth": 3} + # "config": {"startUrl": "https://docs.example.com", "crawlDepth": 3}, + # "system": { "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "lastSuccessfulSyncAt": "..." } # } # ], - # "pagination": { "currentPage": 1, "limit": 50, "totalItems": 1, "totalPages": 1 } + # "pagination": { "currentPage": 1, "limit": 10, "totalItems": 1, "totalPages": 1 } # } ``` diff --git a/apps/docs/v5/api-reference/connectors.mdx b/apps/docs/v5/api-reference/connectors.mdx index 2bbc1e37..6394bc4b 100644 --- a/apps/docs/v5/api-reference/connectors.mdx +++ b/apps/docs/v5/api-reference/connectors.mdx @@ -13,7 +13,7 @@ icon: "/icons/hugeicons/book-open-01.svg" | `PATCH /ns/{namespace}/connectors/{id}` | Change what it syncs: `selection` and `documentLimit` | | `POST /ns/{namespace}/connectors/{id}/sync` | Trigger a sync now. Returns 409 while one is already running | | `DELETE /ns/{namespace}/connectors/{id}` | Disconnect, optionally deleting the documents it imported | -| `GET /connectors` | List every connector in the organization, across namespaces | +| `GET /connectors` | List connectors across every namespace this key can read | Providers: `notion`, `google-drive`, `onedrive`, `gmail`, `github` authenticate with OAuth. `web-crawler`, `s3`, and `granola` take their configuration in the create body and need no login. @@ -32,7 +32,7 @@ const { id, authorization } = await supermemory.connectors.create("user_123", { // Config provider: starts syncing immediately, authorization is null const crawler = await supermemory.connectors.create("user_123", { provider: "web-crawler", - config: { startUrl: "https://docs.example.com" } + config: { startUrl: "https://docs.example.com" }, documentLimit: 200, }) @@ -48,7 +48,7 @@ curl -X POST "https://api.supermemory.ai/ns/user_123/connectors" \ -A pending OAuth connector is not returned by list or get until the user completes authorization. The `authorization.url` expires after one hour (see `authorization.expiresAt`). +A pending OAuth connector is not returned by list until the user completes authorization. Get still returns it, with `system.status: "pending_authorization"`. The `authorization.url` expires after one hour (see `authorization.expiresAt`). See the [connector guides](/connectors/overview) for per-provider setup, selection rules, and troubleshooting.