Connectors let you link external data sources to a Nebula collection. Once connected, files and messages sync automatically into your memories on a recurring schedule.
Connectors use OAuth, so users authorize access through the provider’s consent screen. Nebula never sees raw passwords.
For self-hosted and enterprise deployments, each Nebula deployment uses one OAuth application per provider. Tenants and users authorize through that deployment-level app, and Nebula stores each connection’s user- or workspace-scoped refresh credentials separately. Use separate OAuth apps only when you intentionally want separate redirect domains, consent screens, or provider-side policy boundaries for different deployments.
Supported Providers
| Provider | What Syncs | Config | Setup Flow |
|---|
| Google Drive | Documents from selected folders or files | folder_ids, file_ids | Two-phase (pick folders/files after connecting) |
| Slack | Messages from selected channels | channel_ids | Two-phase (pick channels after connecting) |
| Gmail | Emails from the authorized inbox | — | One-phase (syncs immediately) |
| Notion | Pages from the authorized workspace | — | One-phase (syncs immediately) |
| Outlook | Emails from Microsoft 365 Outlook | folder_ids | One-phase (syncs immediately) |
| OneDrive | Documents from selected folders or files | folder_ids, file_ids | Two-phase (pick folders/files after connecting) |
| SharePoint | Files from selected document libraries | drive_ids, folder_ids, file_ids | Two-phase (pick libraries after connecting) |
| Teams | Messages from selected Teams channels and chats | team_channel_ids, chat_ids, include_channel_replies | Two-phase (pick sources after connecting) |
Use the List Providers endpoint to check which connectors are enabled on your Nebula instance.
from nebula import Nebula
client = Nebula(api_key="YOUR_API_KEY")
providers = client.connectors.list_providers().results
print(providers) # ["gmail", "google_drive", "notion", "onedrive", "outlook", "sharepoint", "slack", "teams"]
Connecting a Data Source
Step 1: Start the OAuth Flow
Provide the collection_id where synced content should land. For Google Drive and Slack, you can optionally include config with pre-selected folder, file, or channel IDs. If omitted, you’ll select them after authorization.
Google Drive
Slack
Notion
Gmail
result = client.connectors.connect("google_drive", collection_id="YOUR_COLLECTION_ID").results
print(result.auth_url) # Redirect user here
Response:{
"auth_url": "https://accounts.google.com/o/oauth2/v2/auth?...",
"state": "signed-state-token"
}
result = client.connectors.connect("slack", collection_id="YOUR_COLLECTION_ID").results
print(result.auth_url)
result = client.connectors.connect("notion", collection_id="YOUR_COLLECTION_ID").results
print(result.auth_url)
result = client.connectors.connect("gmail", collection_id="YOUR_COLLECTION_ID").results
print(result.auth_url)
Step 2: User Authorizes
Redirect the user to the auth_url from the response. After they approve access on the provider’s consent screen, Nebula handles the OAuth callback automatically.
- Gmail, Notion, and Outlook: Becomes
active and syncing starts immediately. Outlook syncs Inbox, Sent Items, and Archive by default; pass folder_ids at connect time to use a custom folder set.
- Google Drive, Slack, OneDrive, SharePoint, and Teams: Created with status
pending. Proceed to step 3.
Step 3: Complete Provider Selection
Google Drive, Slack, OneDrive, SharePoint, and Teams connections require a folder, file, library, channel, or chat selection before syncing. Complete that selection in the Nebula app’s connector settings UI after OAuth returns.
Once the selection is saved, the connection becomes active and the initial sync starts automatically.
Listing Connections
View all connections for a collection. Connections with status revoked are excluded.
connections = client.connectors.list(collection_id="YOUR_COLLECTION_ID").results
for conn in connections:
print(conn.provider, conn.status, conn.health)
Response:
[
{
"id": "CONNECTION_UUID",
"provider": "google_drive",
"external_account_id": "user@gmail.com",
"status": "active",
"collection_id": "COLLECTION_UUID",
"items_synced": 47,
"last_synced_at": "2025-06-15T10:30:00Z",
"last_error": null,
"config": {"folder_ids": ["1ABC..."], "file_ids": ["1XYZ..."]},
"created_at": "2025-06-01T08:00:00Z",
"health": "ok",
"error_detail": null,
"next_sync_at": "2025-06-15T14:30:00Z"
}
]
Connection Statuses
| Status | Meaning |
|---|
pending | OAuth complete, waiting for required content selection (for example Google Drive, OneDrive, SharePoint, Slack, or Teams) |
active | Connected and syncing on schedule |
revoked | Disconnected, hidden from list results |
Enriched Fields
| Field | Type | Description |
|---|
health | "ok" | "error" | null | "ok" if active with no errors, "error" if active with last_error, null for non-active statuses |
error_detail | {message, retryable} | null | Structured version of last_error. retryable is true for all current error types |
next_sync_at | string | null | Estimated next sync time. null for non-active connections. Equal to created_at if never synced |
Get Connection Details
Fetch a single connection by ID.
conn = client.connectors.retrieve("CONNECTION_ID").results
print(conn.health, conn.next_sync_at)
Response:
{
"id": "CONNECTION_UUID",
"provider": "google_drive",
"external_account_id": "user@gmail.com",
"status": "active",
"collection_id": "COLLECTION_UUID",
"items_synced": 47,
"last_synced_at": "2025-06-15T10:30:00Z",
"last_error": null,
"config": {"folder_ids": ["1ABC..."], "file_ids": ["1XYZ..."]},
"created_at": "2025-06-01T08:00:00Z",
"health": "ok",
"error_detail": null,
"next_sync_at": "2025-06-15T14:30:00Z"
}
Disconnecting
Remove a connection and stop syncing. Nebula attempts to revoke the OAuth token with the provider. Previously synced memories remain in the collection unless delete_memories is set to true.
# Simple disconnect (keep memories)
client.connectors.disconnect("CONNECTION_ID")
# Disconnect and delete synced memories
result = client.connectors.disconnect("CONNECTION_ID", delete_memories=True).results
print(result.warnings) # [] on success
Success response:
{
"message": "Connection CONNECTION_UUID disconnected",
"warnings": []
}
Partial cleanup response:
{
"message": "Connection CONNECTION_UUID disconnected",
"warnings": [
{
"code": "cleanup_partial",
"message": "Some memories could not be deleted. For guaranteed cleanup, delete the entire collection."
}
]
}
Memory deletion is best-effort. Memories created before connection tracking was added,
or whose mapping was overwritten by content updates, may not be captured. If a lock is held
(for example, sync or config update in progress), memory cleanup is skipped. In rare cases,
an unusually slow deletion operation may cause the internal lock to expire, allowing concurrent
operations to proceed before cleanup finishes. Metadata-based attribution may also affect
engrams with manually crafted matching metadata in the same collection. For guaranteed
cleanup, delete the entire collection.
Sync Behavior
- Schedule: Active connections sync automatically every 4 hours (configurable by the instance admin).
- Incremental: After the initial full sync, only new or changed items are fetched.
- Deduplication: Items are tracked by their provider-specific ID and content hash. Unchanged items are skipped.
- Retries: Failed items are retried up to 3 times. After that, they enter a cooldown period before being re-attempted.
Manual Sync
Trigger an immediate sync for a connection without waiting for the next scheduled run.
result = client.connectors.sync("CONNECTION_ID").results
print(result.message) # "Sync triggered"
The endpoint returns immediately. The sync runs asynchronously in the background.
Error responses:
| Status | Condition |
|---|
| 400 | Connection is not active (e.g. pending or revoked) |
| 403 | User lacks EDIT permission on the collection |
| 409 | Sync already in progress for this connection |
Config Reference
Google Drive
| Key | Type | Constraints | Description |
|---|
folder_ids | string[] | Max 10 items, 64 chars each, alphanumeric only | Drive folder IDs to sync |
file_ids | string[] | Max 20 items, 128 chars each, alphanumeric plus _ and - | Drive file IDs to sync |
Slack
| Key | Type | Constraints | Description |
|---|
channel_ids | string[] | Max 50 items, must start with C or G, uppercase alphanumeric | Slack channel IDs to sync |
Outlook
| Key | Type | Constraints | Description |
|---|
folder_ids | string[] | Max 100 items, 512 chars each | Optional Microsoft Graph mail folder IDs to sync instead of Inbox, Sent Items, and Archive |
API Reference
| Method | Endpoint | Description |
|---|
GET | /v1/connectors/providers | List available connector providers |
POST | /v1/connectors/{provider}/connect | Start OAuth flow |
GET | /v1/connectors | List connections for a collection |
GET | /v1/connectors/{connection_id} | Get a single connection |
POST | /v1/connectors/{connection_id}/sync | Trigger manual sync |
DELETE | /v1/connectors/{connection_id} | Disconnect a data source |
All endpoints require authentication via Authorization: Bearer YOUR_API_KEY.
Permissions
| Endpoint | Permission |
|---|
GET /connectors/providers | Authenticated (no collection check) |
GET /connectors | Read access to collection |
GET /connectors/{connection_id} | Read access to collection |
POST /connectors/{provider}/connect | EDIT on collection |
POST /connectors/{connection_id}/sync | EDIT on collection |
DELETE /connectors/{connection_id} | EDIT on collection |
Next Steps