Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/docs/connectors/github.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ Deleting a GitHub connection will:
- Stop all future syncs from configured repositories
- Remove all webhooks from the repositories
- Revoke the OAuth authorization
- **Permanently delete all synced documents** from your Supermemory knowledge base
- **Permanently delete all synced documents** from your Supermemory knowledge base (unless you pass `deleteDocuments=false` as a query parameter to keep them)
</Warning>

### Manual Sync
Expand Down
27 changes: 25 additions & 2 deletions apps/docs/connectors/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,16 @@ curl -X POST "https://api.supermemory.ai/v3/connections/list" \

### Delete Connections

The `DELETE /v3/connections/:connectionId` endpoint accepts an optional `deleteDocuments` query parameter:

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `deleteDocuments` | boolean | `true` | When `true`, all documents imported by the connection are permanently deleted. When `false`, the connection is removed but documents are kept. |

<Note>
Setting `deleteDocuments=false` is useful when you want to disconnect an integration without losing the memories that were already imported.
</Note>

<CodeGroup>

```typescript Typescript
Expand All @@ -296,9 +306,14 @@ const client = new Supermemory({
apiKey: process.env.SUPERMEMORY_API_KEY!
});

// Delete by connection ID
// Delete connection and all imported documents (default)
const result = await client.connections.deleteByID(connectionId);

// Delete connection but keep imported documents
const result = await client.connections.deleteByID(connectionId, {
deleteDocuments: false
});

// Or delete by provider (requires container tags)
const result = await client.connections.deleteByProvider('notion', {
containerTags: ['user-123']
Expand All @@ -314,9 +329,12 @@ import os

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

# Delete by connection ID
# Delete connection and all imported documents (default)
result = client.connections.delete_by_id(connection_id)

# Delete connection but keep imported documents
result = client.connections.delete_by_id(connection_id, delete_documents=False)

# Or delete by provider (requires container tags)
result = client.connections.delete_by_provider(
provider='notion',
Expand All @@ -328,9 +346,14 @@ print(f"Deleted: {result.id} {result.provider}")
```

```bash cURL
# Delete connection and all imported documents (default)
curl -X DELETE "https://api.supermemory.ai/v3/connections/conn_abc123" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"

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

# Response: {
# "id": "conn_abc123",
# "provider": "notion"
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/connectors/s3.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ The regex must contain a named capture group `(?<userId>...)` and be less than 2
</Tabs>

<Warning>
Deleting a connection removes all synced documents from Supermemory.
By default, deleting a connection removes all synced documents from Supermemory. To keep documents, pass `deleteDocuments=false` as a query parameter: `DELETE /v3/connections/:id?deleteDocuments=false`
</Warning>

### Manual Sync
Expand Down
53 changes: 44 additions & 9 deletions apps/web/components/add-document/connections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { z } from "zod"
import { dmSansClassName } from "@/lib/fonts"
import { cn } from "@lib/utils"
import { Button } from "@ui/components/button"
import { RemoveConnectionDialog } from "@/components/remove-connection-dialog"

type Connection = z.infer<typeof ConnectionResponseSchema>

Expand Down Expand Up @@ -54,6 +55,10 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
const [connectingProvider, setConnectingProvider] =
useState<ConnectorProvider | null>(null)
const [isUpgrading, setIsUpgrading] = useState(false)
const [removeDialog, setRemoveDialog] = useState<{
open: boolean
connection: Connection | null
}>({ open: false, connection: null })

// Check Pro status
useEffect(() => {
Expand Down Expand Up @@ -159,15 +164,26 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
},
})

// Disconnect mutation
const deleteConnectionMutation = useMutation({
mutationFn: async (connectionId: string) => {
await $fetch(`@delete/connections/${connectionId}`)
mutationFn: async ({
connectionId,
deleteDocuments,
}: {
connectionId: string
deleteDocuments: boolean
}) => {
await $fetch(`@delete/connections/${connectionId}`, {
query: { deleteDocuments },
})
return { deleteDocuments }
},
onSuccess: () => {
onSuccess: (_data, variables) => {
toast.success(
"Connection removal has started. supermemory will permanently delete all documents related to the connection in the next few minutes.",
variables.deleteDocuments
? "Connection removal has started. supermemory will permanently delete all documents related to the connection in the next few minutes."
: "Connection removed. Your memories have been kept.",
)
setRemoveDialog({ open: false, connection: null })
queryClient.invalidateQueries({ queryKey: ["connections"] })
},
onError: (error) => {
Expand All @@ -182,8 +198,8 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
addConnectionMutation.mutate(provider)
}

const handleDisconnect = (connectionId: string) => {
deleteConnectionMutation.mutate(connectionId)
const handleDisconnect = (connection: Connection) => {
setRemoveDialog({ open: true, connection })
}

const hasConnections = connections.length > 0
Expand Down Expand Up @@ -278,7 +294,7 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
<Button
variant="ghost"
size="sm"
onClick={() => handleDisconnect(connection.id)}
onClick={() => handleDisconnect(connection)}
disabled={deleteConnectionMutation.isPending}
className="text-[#737373] hover:text-white hover:bg-[#1B1F24] h-8 w-8 p-0"
>
Expand Down Expand Up @@ -351,7 +367,7 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
<Button
variant="ghost"
size="sm"
onClick={() => handleDisconnect(connection.id)}
onClick={() => handleDisconnect(connection)}
disabled={deleteConnectionMutation.isPending}
className="text-[#737373] hover:text-white hover:bg-[#1B1F24] h-8 w-8 p-0 shrink-0"
>
Expand Down Expand Up @@ -428,6 +444,25 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
)}
</div>
)}
<RemoveConnectionDialog
open={removeDialog.open}
onOpenChange={(open) => {
if (!open) setRemoveDialog({ open: false, connection: null })
}}
provider={removeDialog.connection?.provider}
documentCount={
(removeDialog.connection?.metadata?.documentCount as number) ?? 0
}
onConfirm={(deleteDocuments) => {
if (removeDialog.connection) {
deleteConnectionMutation.mutate({
connectionId: removeDialog.connection.id,
deleteDocuments,
})
}
}}
isDeleting={deleteConnectionMutation.isPending}
/>
</div>
)
}
49 changes: 42 additions & 7 deletions apps/web/components/integrations/connections-detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { ConnectionResponseSchema } from "@repo/validation/api"
import type { z } from "zod"
import { analytics } from "@/lib/analytics"
import { AddDocumentModal } from "@/components/add-document"
import { RemoveConnectionDialog } from "@/components/remove-connection-dialog"
import { DEFAULT_PROJECT_ID } from "@lib/constants"
import type { Project } from "@lib/types"

Expand Down Expand Up @@ -168,6 +169,10 @@ export function ConnectionsDetail() {
const queryClient = useQueryClient()
const autumn = useCustomer()
const [isAddDocumentOpen, setIsAddDocumentOpen] = useState(false)
const [removeDialog, setRemoveDialog] = useState<{
open: boolean
connection: Connection | null
}>({ open: false, connection: null })

const projects = (queryClient.getQueryData<Project[]>(["projects"]) ||
[]) as Project[]
Expand Down Expand Up @@ -215,14 +220,26 @@ export function ConnectionsDetail() {
}, [connectionsError])

const deleteConnectionMutation = useMutation({
mutationFn: async (connectionId: string) => {
await $fetch(`@delete/connections/${connectionId}`)
mutationFn: async ({
connectionId,
deleteDocuments,
}: {
connectionId: string
deleteDocuments: boolean
}) => {
await $fetch(`@delete/connections/${connectionId}`, {
query: { deleteDocuments },
})
return { deleteDocuments }
},
onSuccess: () => {
onSuccess: (_data, variables) => {
analytics.connectionDeleted()
toast.success(
"Connection removal has started. Documents will be permanently deleted in the next few minutes.",
variables.deleteDocuments
? "Connection removal has started. Documents will be permanently deleted in the next few minutes."
: "Connection removed. Your memories have been kept.",
)
setRemoveDialog({ open: false, connection: null })
queryClient.invalidateQueries({ queryKey: ["connections"] })
},
onError: (error) => {
Expand Down Expand Up @@ -341,9 +358,7 @@ export function ConnectionsDetail() {
<ConnectionRow
key={connection.id}
connection={connection}
onDelete={() =>
deleteConnectionMutation.mutate(connection.id)
}
onDelete={() => setRemoveDialog({ open: true, connection })}
isDeleting={deleteConnectionMutation.isPending}
disabled={!hasProProduct}
projects={projects}
Expand Down Expand Up @@ -398,6 +413,26 @@ export function ConnectionsDetail() {
onClose={() => setIsAddDocumentOpen(false)}
defaultTab="connect"
/>

<RemoveConnectionDialog
open={removeDialog.open}
onOpenChange={(open) => {
if (!open) setRemoveDialog({ open: false, connection: null })
}}
provider={removeDialog.connection?.provider}
documentCount={
(removeDialog.connection?.metadata?.documentCount as number) ?? 0
}
onConfirm={(deleteDocuments) => {
if (removeDialog.connection) {
deleteConnectionMutation.mutate({
connectionId: removeDialog.connection.id,
deleteDocuments,
})
}
}}
isDeleting={deleteConnectionMutation.isPending}
/>
</>
)
}
Loading
Loading