Integrations
Integrations connect a VDF AI deployment to the systems your organisation already runs, so agents can search and act on that content. Every integration is scoped to the user who connects it: you only ever see the status of, sync, or disconnect your own connections, and the credentials you supply are used only to act as you against the third‑party system.
Several providers share one uniform surface under /api/integrations/{provider}: check the connection with status, remove it with disconnect, and — for providers that build a search index — start a background sync and poll sync/status. How a provider is connected depends on the provider:
- Credential providers are connected by posting credentials to
connect. These arebitbucket(Bitbucket Data Center),tfs(Azure DevOps Server / TFS),dynamics365(Dynamics 365 Finance & Operations), andemail(a generic IMAP/SMTP mailbox). - OAuth providers are connected in the browser instead of by posting credentials. These are
ms365files(OneDrive & SharePoint),ms365mail(Outlook Mail), andms365teams(Microsoft Teams). See Start an OAuth connection.
Jira, Confluence, and GitHub predate this uniform contract and keep their own routes; see the Jira, Confluence, and GitHub pages.
- GET /integrations/{provider}/status Retrieve integration status
- POST /integrations/{provider}/connect Connect an integration
- GET /integrations/{provider}/oauth/auth-url Start an OAuth connection
- POST /integrations/{provider}/sync Sync an integration
- GET /integrations/{provider}/sync/status Retrieve sync status
- POST /integrations/{provider}/disconnect Disconnect an integration
- GET /integrations/{provider}/oauth/callback Complete an OAuth connection
Paths are relative to /api
Retrieve integration status
GET /api/integrations/{provider}/status
Show all 7 routes
- GET
/api/integrations/bitbucket/status - GET
/api/integrations/tfs/status - GET
/api/integrations/dynamics365/status - GET
/api/integrations/email/status - GET
/api/integrations/ms365files/status - GET
/api/integrations/ms365mail/status - GET
/api/integrations/ms365teams/status
Returns whether the caller has connected the given provider, and its sync state.
Returns the caller's own connection for provider, or isConnected: false when they have not connected it. Never returns another user's connection, and never returns the stored credentials.
- Authentication
- Bearer token How it works
Path parameters
-
providerstring RequiredThe integration to inspect.
Possible values-
bitbucket -
tfs -
dynamics365 -
email -
ms365files -
ms365mail -
ms365teams
-
Returns
Returns the connection status object.
curl "$VDF_BASE_URL/api/integrations/bitbucket/status" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/integrations/bitbucket/status`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/integrations/bitbucket/status",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"provider": "bitbucket",
"isConnected": true,
"authType": "token",
"account": {
"id": "https://bitbucket.example.com",
"name": "https://bitbucket.example.com (7 projects)"
},
"connectedAt": "2026-09-01T09:30:00Z",
"lastSyncAt": "2026-09-01T09:34:12Z",
"syncStatus": "completed",
"lastSyncIndexed": 128,
"syncError": null,
"syncRunning": false
} Connect an integration
POST /api/integrations/{provider}/connect
Show all 4 routes
- POST
/api/integrations/bitbucket/connect - POST
/api/integrations/tfs/connect - POST
/api/integrations/dynamics365/connect - POST
/api/integrations/email/connect
Connects a credential-based integration for the caller and verifies it before storing.
Connects one of the credential-based providers. The handler verifies the credentials against the target system before storing anything, so a bad server URL, token, or password fails here rather than later. On success the credentials are saved against the caller's account and, for providers that build a search index, a background sync starts automatically.
The request body depends on provider:
bitbucket—server_url(required),token(required).tfs—server_url(required),collection(optional),pat(required).dynamics365—resource_url(required),tenant_id(required),client_id(required),client_secret(required),entities(optional).email—imap_host(required),username(required),password(required), and the optional transport fieldsimap_port,imap_ssl,imap_starttls,smtp_host,smtp_port,smtp_security,from_address,from_name. Connecting verifies IMAP; ifsmtp_hostis given it verifies SMTP too, so a mailbox that can read but not send is rejected at connect time.
The OAuth providers (ms365files, ms365mail, ms365teams) are not connected here; see Start an OAuth connection.
- Authentication
- Bearer token How it works
Path parameters
-
providerstring RequiredThe integration to connect.
Possible values-
bitbucket -
tfs -
dynamics365 -
email
-
Body parameters application/json
-
server_urlstringBase URL of the server. Required for
bitbucketandtfs; any trailing REST path is stripped. -
tokenstringHTTP access token. Required for
bitbucket; must be at least 10 characters. -
collectionstringProject collection, appended to the server URL. Optional,
tfsonly. -
patstringPersonal access token. Required for
tfs; must be at least 10 characters. -
resource_urlstringEnvironment URL of the Dynamics 365 F&O instance. Required for
dynamics365. -
tenant_idstringDirectory (tenant) ID of the app registration. Required for
dynamics365. -
client_idstringApplication (client) ID of the app registration. Required for
dynamics365. -
client_secretstringClient secret of the app registration. Required for
dynamics365. -
entitiesstringComma-separated list of entities to index. Optional,
dynamics365only; entities that do not exist in the environment are rejected. -
imap_hoststringIMAP server host name. Required for
email. -
imap_portintegerIMAP port, 1–65535. Optional,
emailonly. -
imap_sslbooleanWhether to use SSL for IMAP. Optional,
emailonly. -
imap_starttlsbooleanWhether to upgrade a plain IMAP connection with STARTTLS. Optional,
emailonly. -
smtp_hoststringSMTP server host name. Optional,
emailonly; when set, sending is verified at connect time. -
smtp_portintegerSMTP port, 1–65535. Optional,
emailonly. -
smtp_securitystringSMTP transport security. Optional,
emailonly.Possible values-
tls -
ssl -
none
-
-
usernamestringMailbox username. Required for
email. -
passwordstringMailbox password. Required for
email. -
from_addressstringAddress to send from. Optional,
emailonly; defaults to the username. -
from_namestringDisplay name to send as. Optional,
emailonly.
Returns
Returns the connected account summary.
Errors
- 400 A required field is missing, or the target system rejected the credentials or server URL.
- 502 The target system could not be reached.
- 504 The target system did not respond in time.
curl -X POST "$VDF_BASE_URL/api/integrations/bitbucket/connect" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"server_url": "https://bitbucket.example.com",
"token": "<ACCESS_TOKEN>"
}' const response = await fetch(`${process.env.VDF_BASE_URL}/api/integrations/bitbucket/connect`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
server_url: 'https://bitbucket.example.com',
token: '<ACCESS_TOKEN>',
}),
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/api/integrations/bitbucket/connect",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
json={
"server_url": "https://bitbucket.example.com",
"token": "<ACCESS_TOKEN>",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"message": "Successfully connected to Bitbucket Data Center",
"account": {
"id": "https://bitbucket.example.com",
"name": "https://bitbucket.example.com (7 projects)"
},
"syncStarted": true
} Start an OAuth connection
GET /api/integrations/{provider}/oauth/auth-url
Show all 3 routes
- GET
/api/integrations/ms365files/oauth/auth-url - GET
/api/integrations/ms365mail/oauth/auth-url - GET
/api/integrations/ms365teams/oauth/auth-url
Returns the URL a user opens to grant access to a Microsoft 365 integration.
Begins the OAuth flow for a Microsoft 365 integration. The response contains an auth_url; open it in the browser and the user grants consent to Microsoft. Microsoft then redirects back to the deployment, which exchanges the authorisation code and stores the resulting tokens against the user who started the flow. The redirect is bound to that user, so a completed flow can only connect the account of the person who initiated it. When the flow finishes, poll Retrieve integration status to confirm the connection.
Use ms365files for OneDrive & SharePoint, ms365mail for Outlook Mail, and ms365teams for Microsoft Teams. Consent is delegated: the connection can read exactly what the signed-in user can read.
- Authentication
- Bearer token How it works
Path parameters
-
providerstring RequiredThe OAuth integration to connect.
Possible values-
ms365files -
ms365mail -
ms365teams
-
Query parameters
-
returnUrlstringAbsolute URL to return the browser to after the flow completes. Defaults to the deployment's integrations page.
Returns
Returns the provider authorisation URL to open in the browser.
Errors
- 503 The provider is not configured on this server. Ask your administrator to add the client credentials.
curl "$VDF_BASE_URL/api/integrations/ms365files/oauth/auth-url?returnUrl=https%3A%2F%2Fexample.com%2Fconsultant%2Fapp%2Fintegrations" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/integrations/ms365files/oauth/auth-url?returnUrl=https%3A%2F%2Fexample.com%2Fconsultant%2Fapp%2Fintegrations`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/integrations/ms365files/oauth/auth-url",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
params={
"returnUrl": "https://example.com/consultant/app/integrations",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"auth_url": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=..."
} Sync an integration
POST /api/integrations/{provider}/sync
Show all 4 routes
- POST
/api/integrations/bitbucket/sync - POST
/api/integrations/tfs/sync - POST
/api/integrations/dynamics365/sync - POST
/api/integrations/ms365files/sync
Starts a background sync that indexes the caller's connected content for search.
Starts a background job that reads the caller's connected content and refreshes the search index agents use. The call returns immediately; poll Retrieve sync status for progress. Only one sync runs per user and provider at a time — if one is already running, the call returns started: false. Available for the indexing providers only: bitbucket, tfs, dynamics365, and ms365files.
- Authentication
- Bearer token How it works
Path parameters
-
providerstring RequiredThe integration to sync.
Possible values-
bitbucket -
tfs -
dynamics365 -
ms365files
-
Returns
Returns whether a background sync was started.
Errors
- 400 The integration is not connected for the caller.
curl -X POST "$VDF_BASE_URL/api/integrations/bitbucket/sync" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/integrations/bitbucket/sync`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/api/integrations/bitbucket/sync",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"started": true
} Retrieve sync status
GET /api/integrations/{provider}/sync/status
Show all 4 routes
- GET
/api/integrations/bitbucket/sync/status - GET
/api/integrations/tfs/sync/status - GET
/api/integrations/dynamics365/sync/status - GET
/api/integrations/ms365files/sync/status
Returns the progress of the caller's most recent sync for the given provider.
Returns whether a sync is running now and the outcome of the last one, for the caller's own connection. Available for the indexing providers: bitbucket, tfs, dynamics365, and ms365files.
- Authentication
- Bearer token How it works
Path parameters
-
providerstring RequiredThe integration to inspect.
Possible values-
bitbucket -
tfs -
dynamics365 -
ms365files
-
Returns
Returns the sync status object.
curl "$VDF_BASE_URL/api/integrations/bitbucket/sync/status" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/integrations/bitbucket/sync/status`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/integrations/bitbucket/sync/status",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"isConnected": true,
"syncRunning": false,
"syncStatus": "completed",
"lastSyncAt": "2026-09-01T09:34:12Z",
"lastSyncIndexed": 128,
"syncError": null
} Disconnect an integration
POST /api/integrations/{provider}/disconnect
Show all 14 routes
- POST
/api/integrations/bitbucket/disconnect - POST
/api/integrations/tfs/disconnect - POST
/api/integrations/dynamics365/disconnect - POST
/api/integrations/email/disconnect - POST
/api/integrations/ms365files/disconnect - POST
/api/integrations/ms365mail/disconnect - POST
/api/integrations/ms365teams/disconnect - DEL
/api/integrations/bitbucket/disconnect - DEL
/api/integrations/tfs/disconnect - DEL
/api/integrations/dynamics365/disconnect - DEL
/api/integrations/email/disconnect - DEL
/api/integrations/ms365files/disconnect - DEL
/api/integrations/ms365mail/disconnect - DEL
/api/integrations/ms365teams/disconnect
Removes the caller's connection and any content it added to the search index.
Removes the caller's own connection for provider. For indexing providers, the rows this connection contributed to the search index are removed as well, so disconnecting also stops that content being retrievable. The DELETE method is accepted as an alternative to POST on the same path.
- Authentication
- Bearer token How it works
Path parameters
-
providerstring RequiredThe integration to disconnect.
Possible values-
bitbucket -
tfs -
dynamics365 -
email -
ms365files -
ms365mail -
ms365teams
-
Returns
Returns confirmation and the number of indexed items removed.
Errors
- 404 The caller has no active connection for this provider.
curl -X POST "$VDF_BASE_URL/api/integrations/bitbucket/disconnect" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/integrations/bitbucket/disconnect`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/api/integrations/bitbucket/disconnect",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"message": "Disconnected from Bitbucket Data Center",
"removedItems": 128
} Complete an OAuth connection
GET /api/integrations/{provider}/oauth/callback
Show all 3 routes
- GET
/api/integrations/ms365files/oauth/callback - GET
/api/integrations/ms365mail/oauth/callback - GET
/api/integrations/ms365teams/oauth/callback
Completes a Microsoft 365 OAuth connection after the user returns from Microsoft.
The browser is sent here by Microsoft once the user has granted consent; clients never call it directly. The request is identified by the signed state created when the flow began with Start an OAuth connection. On success the deployment exchanges the authorisation code, stores the tokens against the user who started the flow, and redirects to the integrations page; on failure it redirects there with an error code. The same contract serves ms365files, ms365mail, and ms365teams.
- Authentication
- None
Path parameters
-
providerstring RequiredThe OAuth integration being connected.
Possible values-
ms365files -
ms365mail -
ms365teams
-
Query parameters
-
codestringAuthorisation code returned by Microsoft on success.
-
statestringOpaque value that ties the callback to the connection that began it.
-
errorstringError code returned by Microsoft when the user declines or consent fails.
-
error_descriptionstringHuman-readable detail accompanying
error.
Returns
Redirects the browser back to the integrations page.
The response is an HTTP redirect; there is no JSON body.
curl -i "$VDF_BASE_URL/api/integrations/ms365files/oauth/callback?code=%3CAUTHORIZATION_CODE%3E&state=%3CSTATE%3E" const response = await fetch(`${process.env.VDF_BASE_URL}/api/integrations/ms365files/oauth/callback?code=%3CAUTHORIZATION_CODE%3E&state=%3CSTATE%3E`);
if (!response.ok) throw new Error(`Request failed with status ${response.status}`); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/integrations/ms365files/oauth/callback",
params={
"code": "<AUTHORIZATION_CODE>",
"state": "<STATE>",
},
allow_redirects=False,
timeout=30,
)
response.raise_for_status()
print(response.headers["Location"])