Skip to content

Connecting SharePoint ​

Moʻolelo indexes drive items — files and folders in SharePoint document libraries — for the SharePoint sites you list. It uses Microsoft Graph delta queries for backfill and Microsoft Graph subscriptions for steady-state notifications.

Prerequisites ​

  • A Microsoft 365 tenant with SharePoint Online.
  • An Azure AD admin who can register an app and grant application permissions (admin consent required).
  • Network reachability: Moʻolelo's webhook endpoint must be reachable from Microsoft Graph's notification IPs. The endpoint must respond to a one-time validation request within 10 seconds.

Step 1 — register an Azure AD application ​

In the Azure Portal under "Microsoft Entra ID → App registrations":

  1. Create a new registration.
  2. Under Certificates & secrets, generate a client secret. Save the secret value immediately — Azure shows it only once.
  3. Under API permissions, add Microsoft Graph application permissions:
    • Sites.Read.All
    • Files.Read.All
  4. Have a tenant admin click Grant admin consent. Without admin consent, the app cannot read SharePoint data.

Make a note of:

  • Tenant ID (the Azure AD tenant GUID).
  • Application (client) ID.
  • Client secret value.

Step 2 — register the connector ​

Insert a row into connector_configs:

sql
INSERT INTO connector_configs
  (tenant_id, source, instance, mode, resources, raw_config)
VALUES (
  'default',
  'sharepoint',
  'contoso',                                              -- your "instance" label
  'backfill',
  '["contoso.sharepoint.com:/sites/intranet",
    "contoso.sharepoint.com:/sites/engineering"]'::jsonb, -- site identifiers
  '{
    "azure_tenant_id": "11111111-2222-3333-4444-555555555555",
    "client_id": "your-app-client-id",
    "client_secret": "your-app-client-secret",
    "scope": "https://graph.microsoft.com/.default"
  }'::jsonb
);

The resources are SharePoint site identifiers in Microsoft Graph's <host>:<server-relative-path> format. Find them by navigating to a SharePoint site in your browser and copying the URL path; for the root site, use <host>:/ (with a trailing slash).

Moʻolelo authenticates with Azure AD using the client-credentials flow and caches access tokens. Refresh is automatic.

Step 3 — create webhook subscriptions ​

Microsoft Graph webhooks are called subscriptions and are bound to specific resources. You create one per drive you want notifications from — typically the default document library of each site.

A subscription request to Graph looks like:

sh
curl -X POST \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  https://graph.microsoft.com/v1.0/subscriptions \
  -d '{
    "changeType": "updated",
    "notificationUrl": "https://<your-moolelo-host>/webhooks/sharepoint/contoso",
    "resource": "sites/<site-id>/drives/<drive-id>/root",
    "expirationDateTime": "2026-05-09T19:00:00Z",
    "clientState": "<random-secret>"
  }'

Microsoft Graph enforces a maximum subscription lifetime of around 60 minutes for drive items. Subscriptions must be renewed before expiry; an operator-side scheduler that calls PATCH /subscriptions/<id> periodically is the standard pattern.

The clientState value goes into Moʻolelo's deployment as WEBHOOK_INGRESS_SHAREPOINT_SECRET. Every notification carries the clientState field; the webhook receiver constant-time-compares it to the configured secret.

The validation handshake ​

The first time you create a subscription, Microsoft Graph POSTs the notificationUrl with a ?validationToken=<random> query parameter and an empty body. Moʻolelo's webhook receiver echoes the token in text/plain within the 10-second window, which Graph requires before activating the subscription. You don't have to do anything — the handshake is automatic — but if your network path adds enough latency to push the round-trip past 10 seconds, subscriptions won't activate.

Step 4 — flip to webhook mode ​

After backfill completes:

sql
UPDATE connector_configs
SET mode = 'webhook'
WHERE tenant_id = 'default' AND source = 'sharepoint';

What gets indexed ​

Per drive item, Moʻolelo indexes:

  • File name and path — surfaced as both lexical text and structural metadata.
  • File contents — Microsoft Office formats (Word, Excel, PowerPoint), PDFs, plain text, and Markdown are extracted by Docling and chunked along the document's heading hierarchy.
  • Author and last modifier — for residency routing.
  • Source URL — direct link back to the file in SharePoint.

What is not indexed today:

  • SharePoint list items (only drive items / files are covered).
  • Permission inheritance from sub-folders. Drive-item permissions are flat in our model — sub-folder permissions are evaluated at query time via the per-source policy hook.
  • OneDrive personal libraries. The connector only watches SharePoint sites listed in resources.

Permissions visible in search results ​

SharePoint's permission model is rich (sharing links, group memberships, broken inheritance, conditional access). Moʻolelo's strategy:

  1. At ingestion, extract the drive item's effective permission set from Graph's /drives/{id}/items/{id}/permissions endpoint.
  2. Project the permissions into authority records keyed by SharePoint principal IDs.
  3. At query time, match the calling user's Microsoft account against the authority records.

When a permission can't be resolved offline (typically: anonymous sharing links, or conditional-access-gated content), Moʻolelo falls back to a live Graph permission check. This is slower (an extra round-trip per document) but preserves correctness.

Troubleshooting ​

  • Subscription creation 400. The notificationUrl has to be HTTPS and externally reachable. Microsoft Graph validates this synchronously. Self-signed certificates are not accepted.
  • Access denied errors during backfill. Confirm admin consent was granted on the Azure AD app permissions (Step 1). Without consent, the app sees no SharePoint sites at all.
  • Webhook handshake timing out. Reduce the network hop count between Microsoft Graph's outbound IPs and your webhook receiver. CDN intermediaries that buffer the request body break the handshake.
  • Drive items appear in search but their content is empty. Docling couldn't parse the file format. Check the parser logs for parse failed; Moʻolelo falls back to indexing just the file name in this case.

Moʻolelo — self-hosted federated enterprise knowledge platform.