SharePoint Indexing API

Captain indexes SharePoint document libraries through Microsoft’s Graph API. There is no per-user OAuth flow and no user passwords involved: your Microsoft 365 admin registers an app once, and Captain uses that app’s credentials to read a single file, a folder (recursively), or a whole document library.

Access can be scoped to exactly one site. With the recommended Sites.Selected permission, your admin grants read access to the specific site you want indexed and nothing else; Captain never sees the rest of the tenant.

To index individual users’ OneDrive for Business drives instead, see the OneDrive Indexing API guide. The endpoints and permissions are separate by design.

What your IT admin provides

Three values, all from a one-time App Registration in Microsoft Entra ID:

ValueWhat it is
tenant_idYour Microsoft 365 directory (tenant) ID: a GUID, or the domain like yourco.onmicrosoft.com.
client_idThe Application (client) ID of the App Registration created for Captain.
client_secretA secret generated for that app. Captain uses it per job to mint short-lived tokens; it is never stored.

Plus the URL of the SharePoint site to index, e.g. https://yourco.sharepoint.com/sites/Sales.

One-time setup

Every step below is done by someone with admin rights in your Microsoft 365 tenant. It takes about 10 minutes and is done once. After that, indexing is a plain API call.

Step 1: Create an App Registration and client secret

  1. Sign in at entra.microsoft.com (or the Azure portal) and go to Identity → Applications → App registrations.
  2. Click New registration. Name the app (for example captain-indexing), leave every default as is, and click Register.
  3. On the app’s Overview page, copy two values: the Application (client) ID and the Directory (tenant) ID.
  4. Go to Certificates & secrets and click New client secret. Pick an expiry and click Add.
  5. Copy the secret’s Value right away; it is shown only once. This is the client_secret you’ll send to Captain. Keep it secret.
  1. On the app’s API permissions page, click Add a permission and choose Microsoft Graph.
  2. Choose Application permissions, not Delegated. Application permissions are what let Captain run headlessly, with no signed-in user.
  3. Add one of:
    • Sites.Selected (recommended). The app can read only the specific sites you grant in Step 3. Nothing else in the tenant is visible.
    • Sites.Read.All. Read access to every SharePoint site, with no Step 3 needed. Simpler but broader; security-conscious tenants usually prefer Sites.Selected.
  4. Click Grant admin consent for <your org> on the same page.

Step 3 (Sites.Selected only): Grant the app access to the site

With Sites.Selected, one more one-time call maps the app to each site it may read. Using Microsoft Graph (with any admin credential that has Sites.FullControl.All, e.g. Graph Explorer):

1POST https://graph.microsoft.com/v1.0/sites/{site-id}/permissions
2Content-Type: application/json
3
4{
5 "roles": ["read"],
6 "grantedToIdentities": [
7 { "application": { "id": "<client_id>", "displayName": "captain-indexing" } }
8 ]
9}

Or with PnP PowerShell:

1Grant-PnPAzureADAppSitePermission -AppId "<client_id>" -DisplayName "captain-indexing" `
2 -Site "https://yourco.sharepoint.com/sites/Sales" -Permissions Read

To find {site-id} for the Graph call: GET https://graph.microsoft.com/v1.0/sites/yourco.sharepoint.com:/sites/Sales.

Indexing

All three endpoints take the same auth fields plus the site URL, and differ only in scope.

A site’s whole document library

$curl -X POST "https://api.runcaptain.com/v2/collections/{collection_name}/index/sharepoint" \
> -H "Authorization: Bearer $CAPTAIN_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "tenant_id": "00000000-1111-2222-3333-444444444444",
> "client_id": "<application (client) id>",
> "client_secret": "<client secret value>",
> "site_url": "https://yourco.sharepoint.com/sites/Sales",
> "processing_type": "advanced"
> }'

This indexes the site’s default document library (“Documents”). If the site has additional libraries, pass the optional drive_id of the library to index instead.

A folder (recursively)

$curl -X POST "https://api.runcaptain.com/v2/collections/{collection_name}/index/sharepoint/directory" \
> -H "Authorization: Bearer $CAPTAIN_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "tenant_id": "00000000-1111-2222-3333-444444444444",
> "client_id": "<application (client) id>",
> "client_secret": "<client secret value>",
> "site_url": "https://yourco.sharepoint.com/sites/Sales",
> "folder_id": "01ABCDEF...",
> "processing_type": "advanced"
> }'

folder_id is the folder’s Graph driveItem ID. Subfolders are indexed too.

A single file

$curl -X POST "https://api.runcaptain.com/v2/collections/{collection_name}/index/sharepoint/file" \
> -H "Authorization: Bearer $CAPTAIN_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "tenant_id": "00000000-1111-2222-3333-444444444444",
> "client_id": "<application (client) id>",
> "client_secret": "<client secret value>",
> "site_url": "https://yourco.sharepoint.com/sites/Sales",
> "item_id": "01GHIJKL...",
> "processing_type": "advanced"
> }'

item_id is the file’s Graph driveItem ID.

Every response is { "job_id": "...", "status": "pending" }. Poll GET /v2/collections/{name}/jobs/{job_id} for progress, then query the collection with POST /v2/collections/{name}/query.

What gets indexed

  • Documents (PDF, DOCX, XLSX, PPTX, CSV, TXT, images, etc.): SharePoint stores files in their native formats, so everything downloads and processes directly. No export step.
  • Skipped: OneNote notebooks, site pages (.aspx), and any file the app can’t access (logged and skipped; the job continues).

Troubleshooting

SymptomCause & fix
AADSTS7000215: Invalid client secretThe secret is wrong or expired. Generate a new one under Certificates & secrets (copy the Value, not the Secret ID).
Consent errors (AADSTS500011, “insufficient privileges”)Admin consent wasn’t granted. On the app’s API permissions page, click Grant admin consent.
403 resolving the site (with Sites.Selected)The per-site grant (Step 3) is missing for that site, or was granted to a different app ID.
A folder job finishes with some files failedThose items are restricted beyond the app’s grant. Captain skips them and indexes the rest.

Security notes

  • Grants are read-only. Captain never writes to or deletes from SharePoint.
  • With Sites.Selected, Captain can only see the sites your admin explicitly granted. Other sites, users’ OneDrives, mail, and everything else in the tenant stay invisible.
  • The client secret is a credential: treat it like a password. Revoke it any time under Certificates & secrets, or kill all access by deleting the App Registration.
  • Captain uses the secret only to mint short-lived tokens per job; nothing durable is stored beyond the request.
  • Every read appears in your Microsoft 365 audit logs, attributed to the app.