Skip to content
Merged
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
6 changes: 6 additions & 0 deletions integrations/sharepoint/definitions/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as sdk from '@botpress/sdk'
import { addToSync } from './sync'

export const actions = {
addToSync,
} as const satisfies sdk.IntegrationDefinitionProps['actions']
14 changes: 14 additions & 0 deletions integrations/sharepoint/definitions/actions/sync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from '@botpress/sdk'

export const addToSync = {
title: 'Add Libraries to Sync',
description: 'Register additional SharePoint document libraries for real-time sync without re-deploying.',
input: {
schema: z.object({
documentLibraryNames: z.string().array().min(1).title('Document Library Names').describe('Libraries to add.'),
}),
},
output: {
schema: z.object({}),
},
}
24 changes: 24 additions & 0 deletions integrations/sharepoint/definitions/configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { z, IntegrationDefinitionProps } from '@botpress/sdk'

export const configuration = {
schema: z.object({
clientId: z.string().min(1).title('Client ID').describe('Azure AD application client ID'),
tenantId: z.string().min(1).title('Tenant ID').describe('Azure AD tenant ID'),
thumbprint: z.string().min(1).title('Certificate Thumbprint').describe('Certificate thumbprint'),
privateKey: z.string().min(1).title('Private Key').describe('PEM-formatted certificate private key'),
primaryDomain: z
.string()
.min(1)
.title('Primary Domain')
.describe('SharePoint primary domain (e.g. contoso, without .sharepoint.com)'),
siteName: z.string().min(1).title('Site Name').describe('SharePoint site name'),
documentLibraryNames: z
.string()
.array()
.optional()
.title('Document Library Names')
.describe(
'Document libraries to subscribe to for real-time sync. Not needed for knowledge-connector browsing only.'
),
}),
} satisfies IntegrationDefinitionProps['configuration']
3 changes: 3 additions & 0 deletions integrations/sharepoint/definitions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { configuration } from './configuration'
export { states } from './states'
export { actions } from './actions'
27 changes: 27 additions & 0 deletions integrations/sharepoint/definitions/states.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { z, IntegrationDefinitionProps } from '@botpress/sdk'

export const states = {
configuration: {
type: 'integration',
schema: z.object({
subscriptions: z
.record(
z.object({
webhookSubscriptionId: z.string().min(1),
changeToken: z.string().min(1),
itemPathCache: z
.record(
z.object({
absolutePath: z.string(),
name: z.string(),
})
)
.default({}),
expiresAt: z.string().optional().describe('ISO 8601 expiry date of the SharePoint webhook subscription.'),
})
)
.title('Webhook Subscriptions')
.describe('Active SharePoint webhook subscriptions keyed by document library name.'),
}),
},
} satisfies IntegrationDefinitionProps['states']
13 changes: 13 additions & 0 deletions integrations/sharepoint/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import rootConfig from '../../eslint.config.mjs'

export default [
...rootConfig,
{
languageOptions: {
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: import.meta.dirname,
},
},
},
]
62 changes: 62 additions & 0 deletions integrations/sharepoint/hub.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# SharePoint

Connect one or many SharePoint document libraries to Botpress. Supports both real-time webhook sync to knowledge bases and file browsing via the knowledge-connector plugin.

> **Webhook expiry:** SharePoint webhook subscriptions expire after **30 days**. The integration automatically renews them on incoming notifications before they expire.

## Knowledge-Connector

This integration is compatible with the Botpress knowledge-connector plugin. Once configured, you can browse SharePoint document libraries directly from the knowledge base UI and select files to index.

## Actions

### Add To Sync

Dynamically add new document libraries to your sync configuration without re-deploying.

**Input:**

- `documentLibraryNames` — Array of library names to add.

## Setup

### 1. Register an app in Microsoft Entra

1. Open **App registrations** in the Microsoft Entra admin center.
2. Click **New registration**, give it a name, click **Register**.
3. Note the **Application (client) ID** and **Directory (tenant) ID**.

### 2. Create a self-signed certificate

```bash
# Generate PKCS#8 private key and self-signed certificate in one step
openssl req -x509 -newkey rsa:2048 -keyout myPrivateKey.key \
-out myCertificate.crt -days 365 -nodes \
-subj "/CN=BotpressSharePoint"
```

The `myPrivateKey.key` file contains your private key (starts with `-----BEGIN PRIVATE KEY-----`). The `myCertificate.crt` file is what you upload to Azure AD.

### 3. Upload the certificate to your app registration

Go to **Certificates & secrets → Certificates → Upload certificate** and upload the `.crt` file.

After uploading, Azure shows the thumbprint (40 hex characters). You can also compute it locally:

```bash
openssl x509 -in myCertificate.crt -fingerprint -sha1 -noout \
| sed 's/SHA1 Fingerprint=//' | tr -d ':'
# → e.g. A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2
```

Use this value (no colons) as the **Certificate Thumbprint** in the integration configuration.

### 4. Grant API permissions

Under **API Permissions**, add the following **Application permissions** and grant admin consent:

- **SharePoint → Sites.FullControl.All** — required to create and delete webhook subscriptions (read/write alone is insufficient for push notifications)

Click **Grant admin consent** when done.

> **Note:** This integration authenticates directly against the SharePoint REST API (not Microsoft Graph), so only SharePoint permissions are required.
59 changes: 59 additions & 0 deletions integrations/sharepoint/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions integrations/sharepoint/integration.definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as sdk from '@botpress/sdk'
import filesReadonly from './bp_modules/files-readonly'
import { actions, configuration, states } from './definitions'

export default new sdk.IntegrationDefinition({
name: 'sharepoint',
version: '1.0.0',
title: 'SharePoint',
description: 'Sync SharePoint document libraries with Botpress knowledge bases.',
readme: 'hub.md',
icon: 'icon.svg',
configuration,
states,
actions,
attributes: {
category: 'Knowledge Base',
repo: 'botpress',
},
}).extend(filesReadonly, ({}) => ({
// Enables knowledge-connector plugin to browse and index SharePoint files
entities: {},
actions: {
listItemsInFolder: {
name: 'filesReadonlyListItemsInFolder',
attributes: { ...sdk.WELL_KNOWN_ATTRIBUTES.HIDDEN_IN_STUDIO },
},
transferFileToBotpress: {
name: 'filesReadonlyTransferFileToBotpress',
attributes: { ...sdk.WELL_KNOWN_ATTRIBUTES.HIDDEN_IN_STUDIO },
},
},
}))
24 changes: 24 additions & 0 deletions integrations/sharepoint/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@botpresshub/sharepoint",
"dependencies": {
"@azure/msal-node": "^3.6.2",
"@botpress/common": "workspace:*",
"@botpress/sdk": "workspace:*",
"axios": "^1.10.0"
},
"devDependencies": {
"@botpress/cli": "workspace:*",
"@botpress/sdk": "workspace:*",
"@sentry/cli": "^2.39.1"
},
"private": true,
"scripts": {
"build": "bp add -y && bp build",
"check:bplint": "bp lint",
"check:type": "tsc --noEmit",
"test": "vitest --run"
},
"bpDependencies": {
"files-readonly": "../../interfaces/files-readonly"
}
}
Loading
Loading