---
title: "Connectors"
description: "Sync content from Google Drive, Notion, Gmail, OneDrive, GitHub, S3, Granola, and the web into supermemory — and know each connector's limits before you commit."
sidebarTitle: "Overview"
icon: "layers"
---

Connectors pull content from the tools your users already work in and feed it into the same ingestion pipeline as everything else. A synced Notion page becomes a document, the pipeline derives memories from it, and those memories show up in [search](/search), [profiles](/concepts/user-profiles), and every other surface — exactly as if you'd added it through the API.

You create a connection, your user completes OAuth (or supplies credentials, for S3 and Granola), and sync starts. That's the whole integration.

## What you can connect

{/* CONFIRM: plan matrix — verify per-plan availability against current billing config before publish */}

| Connector | What syncs | Plan | Sync behavior |
|-----------|-----------|------|---------------|
| [Notion](/connectors/notion) | Pages, databases, blocks | All paid plans | Webhooks + every ~4h |
| [Google Drive](/connectors/google-drive) | Docs, Sheets, Slides, PDFs | All paid plans | Webhooks + every ~4h |
| [OneDrive](/connectors/onedrive) | Word, Excel, PowerPoint | All paid plans | Webhooks + every ~4h |
| [Gmail](/connectors/gmail) | Email threads | Scale and up | Pub/Sub webhooks + every ~4h |
| [GitHub](/connectors/github) | Documentation files in repos | Scale and up | Delayed — hours, not real-time |
| [S3](/connectors/s3) | Files in S3-compatible buckets | Scale and up | Scheduled |
| [Web Crawler](/connectors/web-crawler) | Web pages, docs sites | Scale and up | Scheduled recrawl |
| [Granola](/connectors/granola) | Meeting notes, transcripts | Pro and up | Manual only |

The default cadence: a full import on connect, a scheduled pass roughly every 4 hours, webhook-triggered updates where the provider supports them, and manual sync on demand. The table above notes where a connector deviates — Granola is manual-only, for example. The details — debounce windows, webhook renewal, monitoring endpoints — live in [sync lifecycle](/connectors/sync-lifecycle).

## Connect your first source

Create a connection to get an auth link, then send your user to it:

<CodeGroup>

```typescript TypeScript
import Supermemory from 'supermemory';

const client = new Supermemory({
  apiKey: process.env.SUPERMEMORY_API_KEY,
});

const connection = await client.connections.create('notion', {
  redirectUrl: 'https://yourapp.com/integrations/callback',
  containerTags: ['user_4f8a'],
});

// send the user here to authorize
console.log(connection.authLink);
console.log(connection.expiresIn); // "1 hour"
```

```python Python
from supermemory import Supermemory
import os

client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

connection = client.connections.create(
    'notion',
    redirect_url='https://yourapp.com/integrations/callback',
    container_tags=['user_4f8a'],
)

# send the user here to authorize
print(connection.auth_link)
print(connection.expires_in)  # "1 hour"
```

```bash cURL
# POST /v3/connections/{provider}
curl -X POST "https://api.supermemory.ai/v3/connections/notion" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "redirectUrl": "https://yourapp.com/integrations/callback",
    "containerTags": ["user_4f8a"]
  }'

# {
#   "id": "conn_9f2c1b",
#   "authLink": "https://api.notion.com/v1/oauth/authorize?...",
#   "expiresIn": "1 hour",
#   "redirectsTo": "https://yourapp.com/integrations/callback"
# }
```

</CodeGroup>

The auth link expires in one hour. Generate it when the user clicks "connect", not ahead of time — a stale link means a failed OAuth and a confused user.

Once they authorize, sync starts on its own. There's no second API call to make. Everything the connection imports is tagged with the `containerTags` you set at creation, so a per-user tag keeps each user's synced content inside their own boundary — the same [permissioning](/concepts/permissioning) model as the rest of supermemory.

<Note>
The connections API takes `containerTags` as an array — it's a v3 endpoint. Keep one tag per isolation boundary anyway; tags on a connection are fixed after creation, so pick the tag as carefully as you'd pick it for a document.
</Note>

## Check what synced

List connections to see what's active, and list documents to see what actually got imported:

<CodeGroup>

```typescript TypeScript
const connections = await client.connections.list({
  containerTags: ['user_4f8a'],
});

for (const conn of connections) {
  console.log(conn.provider, conn.email, conn.createdAt);
}

// then verify the imported documents themselves
const docs = await client.documents.list({
  containerTags: ['user_4f8a'],
});
```

```python Python
connections = client.connections.list(container_tags=['user_4f8a'])

for conn in connections:
    print(conn.provider, conn.email, conn.created_at)

# then verify the imported documents themselves
docs = client.documents.list(container_tags=['user_4f8a'])
```

```bash cURL
# POST /v3/connections/list
curl -X POST "https://api.supermemory.ai/v3/connections/list" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"containerTags": ["user_4f8a"]}'

# POST /v3/documents/list
curl -X POST "https://api.supermemory.ai/v3/documents/list" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"containerTags": ["user_4f8a"]}'
```

</CodeGroup>

Each imported document carries a processing status (`queued → extracting → chunking → embedding → indexing → done`, or `failed`), so polling the document list tells you exactly how far an import has gotten. The full verification recipe — and how long indexing should take — is in the [connectors FAQ](/connectors/faq).

## Know the limits before you commit

Every connector has edges. These are the ones that surprise people.

### Google Drive syncs what the user picks — not the whole Drive

By default, after OAuth your user completes a file-and-folder picker, and **only the items they select** sync. If they pick one folder out of twenty, you sync one folder out of twenty. Users who expect "connect Drive, get Drive" will report missing files — set that expectation in your UI before they hit connect.

Whole-Drive sync exists: pass `metadata.syncScope: "full"` when you create the connection and the picker is skipped. But broad Drive access runs into Google's rules, not ours. Google treats full-Drive scopes as restricted: consent screens show an unverified-app warning until your OAuth app passes Google's verification, and restricted scopes carry a recurring security assessment per client ID — a cost you own, annually. {/* CONFIRM: Google verification/annual security assessment specifics */}

The production path for whole-Drive sync is a [custom OAuth app](/connectors/google-drive#custom-oauth-application): register your own Google client, complete verification under your brand, then point supermemory at it with `client.settings.update({ googleDriveCustomKeyEnabled: true, ... })`. Your users see your name on the consent screen instead of a warning.

### The rest, briefly

- **GitHub** syncs on a delay — changes land in hours, not seconds. Don't build "push to repo, query immediately" flows on it.
- **Granola** has no automatic sync. You trigger imports manually.
- **Web Crawler** recrawls on a schedule, so page edits take time to appear. It respects robots.txt — pages it's told not to fetch won't sync.
- **Notion** connects to one workspace per authorization. To switch accounts, delete the connection and reconnect.
- **Large files**: connector files over 50MB are skipped. {/* CONFIRM: 50MB per-file connector limit */}

## What doesn't exist yet

Sources people often ask about that don't sync today:

- **Microsoft Teams, Outlook, and SharePoint** — no connectors today. OneDrive covers files in Microsoft 365, but Teams messages, Outlook mail, and SharePoint sites do **not** sync.
- **HubSpot** — in development, not shipped. {/* CONFIRM: HubSpot in development */}
- **Slack, Jira, Confluence, Linear** — not available as connectors.

If your source isn't listed, you don't have to wait. The document API is itself a connector surface: pull from the source's API, add each item with a `customId` (so re-adds update instead of duplicate), and you've built the sync yourself. [Ingestion best practices](/patterns/ingestion) walks through the pattern.

## Disconnect a source

Deleting a connection stops sync — and deletes the imported documents with it, because `deleteDocuments` defaults to `true` on `DELETE /v3/connections/{connectionId}`. To keep what's already imported, say so explicitly:

<CodeGroup>

```typescript TypeScript
// stop syncing and delete the imported documents (the default)
await client.connections.deleteByID('conn_9f2c1b');

// stop syncing, keep everything already imported
await client.connections.deleteByID('conn_9f2c1b', {
  query: { deleteDocuments: false },
});
```

```python Python
# stop syncing and delete the imported documents (the default)
client.connections.delete_by_id('conn_9f2c1b')

# stop syncing, keep everything already imported
client.connections.delete_by_id(
    'conn_9f2c1b',
    extra_query={'deleteDocuments': 'false'},
)
```

```bash cURL
# DELETE /v3/connections/{connectionId} — deletes imported documents too
curl -X DELETE "https://api.supermemory.ai/v3/connections/conn_9f2c1b" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

# keep the imported documents
curl -X DELETE "https://api.supermemory.ai/v3/connections/conn_9f2c1b?deleteDocuments=false" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```

</CodeGroup>

<Warning>
The default delete is permanent — the documents and the memories derived from them are gone, and deletion does **not** restore used quota. If you only need to pause sync, pass `deleteDocuments=false` so the documents stay, then reconnect later.
</Warning>

The SDK doesn't type `deleteDocuments` as a method parameter — it's a query parameter on the request, which is why the examples pass it through the request options. {/* CONFIRM: python — extra_query kwarg name against the published pypi package */}

That's the whole surface: connect, verify, disconnect. Everything a connector imports lives in the same engine as your API-added content — one search, one graph, one set of profiles.

## Where next

- [Sync lifecycle](/connectors/sync-lifecycle) — cadence, debounce, webhook renewal, and monitoring sync runs
- [Connectors FAQ](/connectors/faq) — expired auth links, immutable tags, verifying imports
- [Google Drive](/connectors/google-drive) — the custom OAuth app setup in full
- [Ingestion best practices](/patterns/ingestion) — build your own connector on the document API
