mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-26 06:04:08 +00:00
docs(connectors): align connector docs with the v5 API (#1812)
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)
This commit is contained in:
parent
2197c04ac1
commit
2a546ff654
12 changed files with 112 additions and 102 deletions
|
|
@ -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)}")
|
||||
|
|
|
|||
|
|
@ -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 <team@company.com>`) |
|
||||
| `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
|
||||
|
||||
<Note>
|
||||
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.
|
||||
</Note>
|
||||
|
||||
## Permissions & scopes
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
<Note>
|
||||
Drive documents are converted to markdown before ingestion. This conversion is lossy — some formatting may not be preserved.
|
||||
</Note>
|
||||
|
|
@ -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 }
|
||||
# }
|
||||
```
|
||||
</Tab>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ curl "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?include=pi
|
|||
```
|
||||
|
||||
<Note>
|
||||
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`.
|
||||
</Note>
|
||||
|
||||
## 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
|
||||
|
||||
<Note>
|
||||
**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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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_<property name>`, 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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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"}}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="cURL">
|
||||
|
|
@ -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}`)
|
||||
})
|
||||
```
|
||||
</Tab>
|
||||
|
|
@ -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}")
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="cURL">
|
||||
|
|
@ -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 }
|
||||
# }
|
||||
```
|
||||
</Tab>
|
||||
|
|
|
|||
|
|
@ -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" \
|
|||
</CodeGroup>
|
||||
|
||||
<Note>
|
||||
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`).
|
||||
</Note>
|
||||
|
||||
See the [connector guides](/connectors/overview) for per-provider setup, selection rules, and troubleshooting.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue