Guide
Connect SharePoint
Sync the file metadata of SharePoint document libraries through your Microsoft 365 connection, and choose which libraries Moira Connect reads.
On this page
What you get
SharePoint is part of the microsoft provider: the same Entra app, account and /v1/files routes as OneDrive, and the same file events, plus drive.library.removed. Three capabilities add it:
sites.readfinds the sites the account follows or can search.drive.library.readreads the metadata of the libraries you select. Likesites.read, it needsSites.Read.All.drive.library.writecreates, changes, deletes and shares items. It needsSites.ReadWrite.All, so request it only if you will write. It also needs the matchingdrive.file.*capability, and sharing needsdrive.permission.manage.
Only metadata is synced: names, sizes, locations and modified times. File contents are never downloaded or uploaded. Sharing is not read from SharePoint: a file lists only grants made through the API.
Prerequisites
- A Moira Connect project, selected in the console, and a server API key.
accounts:readis enough for reading; selecting libraries and writing needaccounts:write. See API keys. - A Microsoft 365 work or school account. Personal accounts have no SharePoint and report
unsupported. - Permission to register an app in Microsoft Entra, and an administrator who can approve the
Sitespermissions. - The SharePoint capabilities enabled for your project, and a signed-in user who follows the site or can find it by search. See Get the capabilities enabled.
Set up the Microsoft app
SharePoint uses the Microsoft app you register for Outlook, Calendar and OneDrive. See Provider credentials.
- In the console, open Provider Apps and choose Microsoft 365. Copy the redirect URI, which ends in
/oauth/callback/microsoft. Ignore the Outlook callback URI. - In the Microsoft Entra admin center, register an app with a multitenant account type. Add personal accounts only for personal OneDrive.
- Under Authentication, add a Web redirect URI.
- Under Certificates & secrets, create a client secret and copy its Value, not its ID. Entra shows it once.
- Under API permissions, choose Add a permission, Microsoft Graph, Delegated permissions. Add
Sites.Read.All, andSites.ReadWrite.Allonly if you will write. Also add the permissions that are always requested:Calendars.ReadWrite,Tasks.ReadWrite,Files.ReadWrite,openid,profileandoffline_access. - Select Grant admin consent for your tenant. This covers your own tenant only: an administrator of another organization has to approve the app for it.
- In Provider Apps, enter the Application (client) ID and the secret Value.
- Clear the saved scope list. A saved Scopes and Permissions list (the form starts with one) is used in place of the scopes of the services you tick (the sign-in scopes are still added), Select Clear all, then choose Save credentials.
If the app is already configured, its card shows no list. Choose Replace credentials, enter the Application (client) ID and a secret Value again (create a new secret if you lost the Value), select Clear all, then choose Save replacement. This applies to every Microsoft connection in the project. Connected accounts keep their consent until they reconnect.
Get the capabilities enabled
The three SharePoint capabilities are entitlement-gated. A Moira operator grants each one to your project, and you cannot grant it yourself. Moira Connect marks these capabilities with a Plan badge. Ask through Contact sales, naming your project. There is no self-service request.
Choose services in Connect account lists each capability as enabled or not yet enabled. GET /v1/accounts/{id}/capabilities returns a state per capability: not_granted means no entitlement, not_selected means it is not in your selections, and pending_consent means the account has not approved it yet.
A capability is granted for an account when it is enabled for your project (where it is gated), selected, and approved on the consent screen.
Connect the account
- Open Connect account and choose Microsoft.
- Enter an Account reference, your own label for the connection. Give each account its own reference: a reference that is already in use points at the account that has it instead of creating another one. Reusing the reference of a ready account completes with no new consent, so newly ticked services stay
pending_consent; reconsent instead. - Under Choose services, tick
sites.readanddrive.library.readin the Drive group, anddrive.library.writeonly if you will write. An Admin consent badge marks capabilities that usually need administrator approval, and an Included badge marks capabilities that are always requested and cannot be unticked. - The Requested OAuth scopes panel is a preview: a saved scope list replaces it, and the sign-in scopes are still added. Microsoft's consent screen describes permissions in plain language rather than by scope name.
- Select Connect Microsoft. The console opens the consent in a new tab and shows Connection started.
To change the services of a connected account, update the selections and ask for a new consent. Both calls need accounts:write and an Idempotency-Key header.
PUT /v1/capability-selections replaces every selection of its scope, the whole project unless you pass account_id. A missing row is handled differently: for a reconsent a capability without a row returns to its default, and a new connection that does not name its capabilities requests only the project's enabled selections, so a project-wide list must name every capability you want. This example is for one account:
curl -X PUT https://api.moiraconnect.com/v1/capability-selections \
-H "Authorization: Bearer $MOIRA_API_KEY" \
-H "Idempotency-Key: $REQUEST_ID" \
-H "Content-Type: application/json" \
--data '{"provider":"microsoft","account_id":"<ACCOUNT_ID>","selections":[
{"capability":"sites.read","enabled":true},
{"capability":"drive.library.read","enabled":true}]}'POST /v1/accounts/{id}/reconsent returns a url: open it and finish the consent. Its body must hold at least one of success_redirect_url, webhook_id or opener_origin, or the request fails with a 422 and the code completion_target_required. In the console, Reconnect / adjust services on the account page does the same.
Choose libraries
Discovery is attempted with each account sync. It runs at most every 6 hours after a success and 1 hour after a failed or partial run, so a new library can take that long to appear. The first run is at the first sync after sites.read is granted. In the console, tick a library in the SharePoint libraries panel of the account page.
GET /v1/drives?account_id=$ACCOUNT_ID&limit=50 returns the account's drives (abridged):
{
"data": {
"items": [
{ "id": "5b1c0c8e-0000-4000-8000-000000000003", "kind": "library",
"drive_ref": "b!example-graph-drive-id", "site_display_name": "Contoso Intranet",
"display_name": "Policies", "selected": false, "status": "active", "last_error": null }
],
"next_cursor": null
}
}limit is 1 to 100 on this route, and a value outside it is a 422. Pass next_cursor back as cursor for the next page.
Select a library with PATCH /v1/drives/{id} and {"selected": true}. It must be active, and drive.library.read must be granted. The library is read at the next account sync.
Read files
GET /v1/files?account_id=$ACCOUNT_ID&drive=$DRIVE_REF&limit=50 lists files, newest provider_modified_at first, folders included. drive_ref is the value of the drive filter, and default is the personal drive. The response has data.items and data.next_cursor, and limit is clamped to 1 to 100. GET /v1/files/{id} returns one file by its UUID id.
- Without
drive, the list holds the personal drive and the selected libraries. - A library file's
provider_file_idcombines the drive and the item: use it as returned to name a parent folder. - A library file is visible only while
drive.library.readis granted and its library is selected and active. Otherwise its id returns404 file_not_found.
Sync changes arrive as drive.file.upserted and drive.file.deleted events that carry drive_ref. A delete made through the API emits drive.file.deleted without drive_ref. A removed library emits one drive.library.removed event (with drive_id and files_trashed), and a share made through the API emits drive.permission.created.
Write files
- You need
drive.library.writeand the matchingdrive.file.*capability:drive.file.create,drive.file.update(rename or move) ordrive.file.delete, on by default for Microsoft. - Sharing with
POST /v1/files/{id}/permissionsalso needsdrive.permission.manage, which is off by default: select it, then reconsent. - Every write needs an
Idempotency-Keyheader and returns202with acommand_id.
Create an item with POST /v1/files. It uploads no content:
curl -X POST https://api.moiraconnect.com/v1/files \
-H "Authorization: Bearer $MOIRA_API_KEY" \
-H "Idempotency-Key: $REQUEST_ID" \
-H "Content-Type: application/json" \
--data '{"account_id":"<ACCOUNT_ID>","name":"Drafts","drive":"<DRIVE_REF>","parents":["<FOLDER_PROVIDER_FILE_ID>"]}'driveisdefaultor thedrive_refof a selected, active library. Every parent must belong to that library, otherwise the request is a422.- Rename or move with
PATCH /v1/files/{id}, delete withDELETE /v1/files/{id}. - Without
drive.library.writethe request is a403 capability_not_grantedand nothing is queued. If the matchingdrive.file.*capability is off, the command is accepted and then fails withcapability_not_granted.
A request body must be a JSON object sent with Content-Type: application/json, otherwise the request fails with invalid_json or unsupported_media_type. See Errors for the other status codes.
Deselect a library
Clear the checkbox in the console, or send PATCH /v1/drives/{id} with {"selected": false}. The library stops syncing and its files leave /v1/files. The personal drive refuses any change with 409 personal_drive_always_selected.
Limits
- Metadata only: no file content is downloaded or uploaded.
- Discovery reads at most 200 sites and 100 libraries per site, document libraries only.
- The account syncs about every 15 minutes. A very large library can need several syncs to list fully, and cleanup of its deleted files can lag.
- A removed file stays in the list with
trashed: true. A library that fails with a permanent error such as403getsstatus: inaccessibleand itslast_error, while others keep syncing. A library that no longer exists getsstatus: removedand its files are trashed.
Troubleshooting
- The consent screen asks for administrator approval. See step 6 of Set up the Microsoft app, or sign in as the administrator during the connection.
409 entitlement_required. The request named only SharePoint capabilities and the project holds no entitlement. The409for a connection created through the API appears when the connect link is opened, not onPOST /v1/hosted-connect-sessions(201). A reconsent returns a JSON409. Connect account in the console does not trigger it. Reconnect / adjust services can, if your selections turn off every capability except the SharePoint ones.- The consent screen does not mention SharePoint sites. The capabilities are not enabled, or a saved scope list replaces the ticked services' scopes. See Set up the Microsoft app.
GET /v1/driveslists only the personal drive.sites.readis not granted, the user follows no site and search finds none, or discovery has not run yet. Check the capability states:not_selectedmeans tick it and reconnect,pending_consentmeans reconsent, andunsupportedmeans a personal account.- Selecting a library fails.
403 capability_not_grantedmeansdrive.library.readis not granted.409 drive_not_selectablemeans the library isinaccessibleorremoved: readlast_error. A new selection lists no files until the next sync.
Next step
Select one small library and compare its files with SharePoint first.