> For a complete page index of the Captain API documentation, fetch https://docs.captain.dev/llms.txt?excludeSpec=true

# SharePoint Indexing API

> Index a SharePoint site's documents (a single file, a folder recursively, or a whole document library) with no OAuth pop-up. Captain reads via a Microsoft Entra ID app your IT admin authorizes once.

## Agent Quick Reference: SharePoint headless indexing

* Auth = Microsoft Graph **app-only client credentials** (an Entra ID App Registration). No per-user OAuth, no API keys, no user accounts. Captain mints app-only tokens from the tenant_id + client_id + client_secret you pass on each request.
* One-time setup, done by a Microsoft 365 admin in **Microsoft Entra ID** (entra.microsoft.com): create an **App Registration**, add a **client secret**, grant **application** permissions (not delegated) and click **Grant admin consent** in the portal. Required application permissions: `Sites.Read.All` (read every site) and `User.Read.All` (resolve users/sites); `Files.Read.All` is also needed if the same app registration indexes OneDrive.
* `Sites.Selected` (Application) can be used instead of `Sites.Read.All` to scope access to specific sites, but it then requires a separate per-site grant and is not covered here — prefer `Sites.Read.All` for the standard setup.
* Request fields (all endpoints): `tenant_id`, `client_id`, `client_secret`, `site_url`, `processing_type` ("advanced" | "basic" | "auto"); optional `drive_id` for a non-default document library.
* Endpoints:
  * `POST /v2/collections/{name}/index/sharepoint`: the site's whole document library
  * `POST /v2/collections/{name}/index/sharepoint/directory`: one folder (recursive), field `folder_id`
  * `POST /v2/collections/{name}/index/sharepoint/file`: one file, field `item_id`
* Files are already native formats (docx/xlsx/pdf), so there is no export step. OneNote notebooks and site pages (.aspx) are skipped.
* For OneDrive for Business, see the separate OneDrive Indexing API guide (`/index/onedrive`, permission `Files.Read.All`).
* Common error `AADSTS7000215` = invalid client secret. `AADSTS500011` / consent errors = admin consent not granted. A 403 resolving a site under `Sites.Selected` = the per-site grant is missing.

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.

The app reads only through Microsoft Graph with the permissions your admin
grants, and every access is read-only and audit-logged. To scope access to
specific sites rather than the whole tenant, an admin can use the
`Sites.Selected` application permission plus a per-site grant instead of
`Sites.Read.All`; that advanced option is outside this standard setup.

To index individual users' OneDrive for Business drives instead, see the
[OneDrive Indexing API](/guides/onedrive-indexing) 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:

| Value           | What it is                                                                                               |
| --------------- | -------------------------------------------------------------------------------------------------------- |
| `tenant_id`     | Your Microsoft 365 directory (tenant) ID: a GUID, or the domain like `yourco.onmicrosoft.com`.           |
| `client_id`     | The Application (client) ID of the App Registration created for Captain.                                 |
| `client_secret` | A 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

> **Info**
>
> 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](https://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.

### Step 2: Grant application permissions and admin consent

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 — the delegated
   versions of these permissions are not sufficient.
3. Add both of these application permissions:

   * `Sites.Read.All` — read access to SharePoint site content.
   * `User.Read.All` — lets the app resolve users and site/drive identities
     (required; without it, resolving a site or its drive fails).

   If the same app registration will also index OneDrive, add `Files.Read.All`
   as well (see the OneDrive guide).
4. Click **Grant admin consent for \<your org>** on the same page, then
   confirm. Each permission's **Status** should show a green "Granted" check.
   This is all done from the Azure portal — no command line or Graph call is
   needed.

## Indexing

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

### A site's whole document library

```bash
curl -X POST "https://api.filesearch.dev/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)

```bash
curl -X POST "https://api.filesearch.dev/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

```bash
curl -X POST "https://api.filesearch.dev/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/jobs/{job_id}` for progress, then query the
collection with `POST /v2/collections/{name}/query`.

## What gets indexed

* **Documents** (PDF, DOCX, XLSX, 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

| Symptom                                                    | Cause & fix                                                                                                                                                                                                                                                  |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `AADSTS7000215: Invalid client secret`                     | The 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                                   | The app is missing `Sites.Read.All` or `User.Read.All` (Application), or admin consent wasn't granted. Add both application permissions and click **Grant admin consent**. (If you scoped with `Sites.Selected` instead, the per-site grant may be missing.) |
| A folder job finishes with some files failed               | Those 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. The permissions above (`Sites.Read.All`, `User.Read.All`) are all
  read scopes.
* To narrow access to specific sites, an admin can swap `Sites.Read.All` for
  the `Sites.Selected` application permission and grant the app only the sites
  to be indexed; everything else in the tenant then stays 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.