How to Integrate with the SharePoint API: 2026 Architecture Guide
A technical guide to integrating with the SharePoint API in 2026 — covering Microsoft Graph hierarchies, OAuth with Entra ID, File Picker v8 pitfalls, rate limits, and pagination.
B2B SaaS applications moving upmarket eventually hit a wall: enterprise customers demand a native SharePoint integration. Building a reliable pipeline to read, write, and sync files from Microsoft's ecosystem is notoriously painful. You are dealing with strict permission hierarchies, aggressive rate limits, an OAuth lifecycle that actively fights you, and — if you need file selection UI — a File Picker protocol that feels like it was designed to break your spirit.
This guide covers the full architecture: Microsoft Graph's data model, OAuth through Microsoft Entra ID, the File Picker v8 protocol, rate limiting, pagination, and how to handle all of it from the perspective of a team that has shipped this integration for production B2B SaaS products.
The Business Case for a SharePoint Integration
If your B2B SaaS product is moving upmarket, SharePoint is not optional. Microsoft reports that SharePoint now serves over one billion users worldwide and processes more than two billion pieces of content every day. Two billion Power Automate flows use SharePoint data every single week.
Every enterprise deal you chase will eventually surface the question: "Does it work with SharePoint?" Whether your product handles document management, compliance workflows, knowledge bases, or RAG ingestion pipelines, your customers' files live in SharePoint document libraries. Their Teams channels store files in SharePoint. Their OneDrive is SharePoint.
The real pressure comes from the buyer profile. Large enterprises are SharePoint's power users, and 71% of SharePoint developers work in companies with over 1,000 employees. These are exactly the accounts your sales team is trying to close.
Many engineering teams initially try to offload this requirement to generic workflow tools or ask customers to upload files manually. Enterprise IT departments reject these workflows. They require a native, embedded experience that respects their existing Entra ID compliance boundaries and access controls. Saying "we'll add SharePoint later" means losing deals now.
Understanding the SharePoint API Architecture: Sites, Drives, and Items
The SharePoint API architecture relies on a strict hierarchy via the Microsoft Graph: you must first query Sites, then enumerate the Drives within those Sites, and finally access the Drive Items (files and folders) within those Drives.
Microsoft Graph is the unified endpoint for all Microsoft 365 services. You do not query a dedicated "SharePoint API" domain; instead, you make requests to graph.microsoft.com. The data model is not a flat file system. It is a deeply nested hierarchy that reflects SharePoint's organizational structure.
graph TD
A[Tenant / Organization] --> B[Site <br> e.g., Marketing Team]
B --> C[Drive <br> Document Library]
C --> D[DriveItem <br> Folder]
C --> E[DriveItem <br> File.pdf]
D --> F[DriveItem <br> Nested File.docx]To retrieve a specific document, your application must execute a sequence of API calls. You cannot simply ask for "all files belonging to this user."
- Resolve the Site:
GET https://graph.microsoft.com/v1.0/sites/{hostname}:/{server-relative-path} - List the Drives:
GET https://graph.microsoft.com/v1.0/sites/{site-id}/drives - List Items in a Drive:
GET https://graph.microsoft.com/v1.0/drives/{drive-id}/root/children
Limit your queries to fetching only the most essential data. Use $select and $filter parameters to trim responses — this isn't just about performance, it directly impacts how quickly you hit rate limits.
The Permissions Fiasco
As our engineering team noted when documenting the challenges we face while building integrations, the permission model is highly unintuitive. Following the hierarchy above, one might assume that adding a user as the owner of a SharePoint site grants them access to everything on that site. It does not.
Account configurations vary wildly between enterprise customers. A user might be a site admin but lack explicit permissions to a specific nested document library (Drive). Your integration must gracefully handle 403 Forbidden errors at every level of the hierarchy and surface actionable error messages to the end-user, rather than failing the entire sync job silently.
Navigating SharePoint OAuth and Permission Scopes
SharePoint authentication in 2026 is in the middle of a major transition. SharePoint Add-Ins stopped working for new tenants as of November 1st, 2024, and are fully retired as of April 2nd, 2026. IDCRL is fully retired as of May 1, 2026, and cannot be re-enabled.
This means the only supported path forward is OAuth 2.0 via Microsoft Entra ID (formerly Azure Active Directory). Just as we covered in our guide to Microsoft Dynamics 365 integration, every integration must go through an Azure app registration.
Delegated vs. Application Permissions
When registering your OAuth application in the Azure portal, you must choose between Delegated and Application permissions:
- Delegated Permissions: The application acts on behalf of the signed-in user. It can only access what the user can access. For customer-facing B2B SaaS integrations, this is almost always what you want. You will request scopes like
Sites.Read.All,Files.ReadWrite.All, andoffline_access. - Application Permissions: The application runs as a background service with its own identity. It requires tenant-wide admin consent and can access all files across the entire organization. Enterprise IT teams are extremely hesitant to grant these scopes to third-party SaaS vendors.
| Permission Type | Use Case | Admin Consent Required | Scope Example |
|---|---|---|---|
| Delegated | File picker, user-facing features | Sometimes | Sites.Read.All |
| Application | Background syncs, data pipelines | Always | Sites.ReadWrite.All |
The permission gotcha that bites everyone: Many enterprises will refuse to grant Sites.ReadWrite.All application permissions because it gives your app access to every site in their tenant. Microsoft offers Sites.Selected as a granular alternative, but it requires the tenant admin to explicitly grant your app access to each specific site. Plan for this back-and-forth in your customer onboarding flow.
The OAuth Token Lifecycle
Microsoft access tokens are short-lived, typically expiring in 60 to 90 minutes. Without proactive token refresh, your integration silently breaks mid-session.
This is the single most common failure mode reported by teams integrating with SharePoint. One n8n community user reported: "I am using the Microsoft SharePoint node to upload files every hour with a Schedule Trigger. It works well for a few hours, before failing with an error: The access token has expired. If I edit the Credentials and click the Reconnect button, the workflow works again, until the 'new' access token expires."
You need proactive token refresh — not reactive refresh after a 401. Truto handles this by refreshing OAuth tokens shortly before they expire, so the integrated account never hits an expiration error. If you're building this yourself, you need a background process that tracks token TTL and refreshes proactively.
Proactive refresh alone isn't sufficient at scale, either. It introduces severe concurrency challenges. If you have multiple background workers syncing different document libraries for the same integrated account simultaneously, they might both attempt to refresh the token at the exact same time. Microsoft's OAuth server will issue a new token to the first request and immediately invalidate the old refresh token. The second worker will receive an invalid_grant error, permanently breaking the integration connection for that customer.
Architectural Requirement: Your token management layer must implement strict distributed locks around token refresh operations to prevent race conditions. Read our full guide on handling OAuth token refresh failures in production for the exact locking patterns required.
Implementing the SharePoint File Picker v8
If your goal is to let users select files from their SharePoint account to attach to records in your application, do not build a custom UI that traverses the Sites > Drives > Items hierarchy. It is a massive waste of engineering resources and will never match the native user experience.
Instead, implement the Microsoft File Picker v8. It gives your users the native Microsoft file browsing experience inside your application — but it's also one of the most frustrating components to integrate correctly.
How the File Picker v8 Protocol Works
The File Picker v8 is not a JavaScript library you install via NPM. It is a hosted web page (FilePicker.aspx) that you open in a popup window or iframe, communicating with your host application via the browser's MessageChannel API.
The protocol works as follows:
- Open a popup window (or iframe) to host the picker UI.
- Construct a deeply nested JSON configuration object and POST a form to
{baseUrl}/_layouts/15/FilePicker.aspx. - Establish communication using
postMessageand aMessageChannel. - On "initialize", capture the
MessagePort, start it, and send an "activate" message. - Handle picker command messages — the critical ones are "authenticate" (where your app must return a valid token for the requested resource), "pick" (user selected files), and "close" (user cancelled).
Here's a simplified diagram of the communication flow:
sequenceDiagram
participant App as Your App
participant Popup as File Picker Popup
participant SP as SharePoint
App->>Popup: Open popup with POST to FilePicker.aspx
Popup->>App: postMessage("initialize") + MessagePort
App->>Popup: Send "activate" via port
Popup->>App: "authenticate" command
App->>Popup: Return access token
Popup->>SP: Load files with token
SP->>Popup: File listing
Note over Popup: User browses and selects files
Popup->>App: "pick" command with selected items
App->>App: Process selected filesAnd a simplified code example of the message handling on your side:
// Simplified MessageChannel listener
window.addEventListener('message', (event) => {
if (event.data.type === 'initialize' && event.ports.length > 0) {
const port = event.ports[0];
port.onmessage = (msg) => {
if (msg.data.command === 'pick') {
console.log('User selected files:', msg.data.items);
// Handle file selection
} else if (msg.data.command === 'close') {
// Handle user cancellation
}
};
}
});Common File Picker v8 Pitfalls
Developers frequently encounter 500 Internal Server Errors and silent failures when launching the picker. These almost always trace back to one of these configuration issues:
- Wrong base URL: The picker URL must match the exact SharePoint site URL. OneDrive personal accounts use a
/pickerendpoint, while OneDrive Business and SharePoint use/_layouts/15/FilePicker.aspx. Using the wrong endpoint guarantees a crash. - Token audience mismatch: Using a Graph API access token directly with the File Picker throws a
PrefetchFailure (3000003, invalid_client)error due to anAudienceUriValidationFailedException. The picker needs a token scoped to the SharePoint resource, nothttps://graph.microsoft.com. This distinction trips up nearly everyone on the first attempt. - Expired tokens: The picker does not handle authentication itself. It relies entirely on the token your application provides. Use
acquireTokenSilent()(or equivalent) before every picker launch. - Stale consent: If you remove a permission from your app registration, users who already consented still receive tokens with that old scope until their consent is revoked. New users won't get that permission. This creates maddening inconsistencies during testing across accounts.
The redirect URI trap: Even though the SharePoint File Picker itself doesn't use the redirect URI directly, you still need to configure it — because the underlying MSAL authentication flow requires it as part of the OAuth handshake. The redirect URI acts as a "return address" for Entra ID to send tokens back to your app after the user signs in.
Handling SharePoint API Rate Limits and Pagination
If you are building a data pipeline to ingest SharePoint files — syncing knowledge base articles into a vector database for an AI agent, for example — you will hit Microsoft Graph rate limits almost immediately. Migrating or syncing data in batches is essential.
SharePoint Online uses throttling to maintain optimal performance and reliability. It limits the number of API calls within a time window to prevent overuse of resources. When limits are exceeded, SharePoint throttles further requests from that client for a short period.
What Throttling Looks Like
When you exceed the allowed request volume, SharePoint returns HTTP 429 ("Too Many Requests") or 503 ("Server Too Busy"). Here's the painful part: Microsoft does not publish exact throttling limits. They are dynamically adjusted based on several factors.
What we do know:
- The actual request rate depends on the API choice and corresponding resource unit cost. You can estimate roughly 2 resource units per request on average.
- Microsoft Graph consumes fewer resources than CSOM and legacy SharePoint REST to achieve the same functionality. Adopting Graph improves performance and reduces throttling.
- Multiple applications running against the same tenant share the same resource bucket. In rare cases, one noisy neighbor app can trigger rate limiting for yours.
- Throttling limits differ based on the number of licensed users in the tenant. Smaller customers get lower limits — which means your integration is most fragile for the accounts that can least tolerate disruption.
If your background workers ignore throttling responses and continue hammering the API, Microsoft will temporarily ban your application ID, halting all syncs across all customers.
Defensive Patterns for Rate Limits
Always respect Retry-After headers. When throttled, the response includes a Retry-After value indicating exactly how many seconds to wait before retrying.
Use RateLimit headers proactively. When your application has consumed 80% of its resource unit quota, SharePoint starts sending RateLimit headers. If you detect only 10% of resource units remaining, slow down automatically to avoid getting throttled at all.
Decorate your traffic. Well-decorated traffic is prioritized over undecorated traffic. Always set a descriptive User-Agent header with your AppID and AppTitle — undecorated requests get throttled more aggressively.
A basic retry strategy with exponential backoff:
import time
import requests
def call_sharepoint_api(url, headers, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.json()
if response.status_code in (429, 503):
retry_after = int(response.headers.get('Retry-After', 2 ** attempt))
time.sleep(retry_after)
continue
response.raise_for_status()
raise Exception(f"Failed after {max_retries} retries")In production, add jitter to the backoff calculation to prevent the "thundering herd" problem — where all your workers wake up and retry at exactly the same time after a shared throttling event.
Pagination with $skipToken
SharePoint document libraries can contain hundreds of thousands of files. The API limits responses to a maximum page size (typically 200 items via the $top parameter).
To retrieve the next page, you do not pass a page number. Instead, you must look for the @odata.nextLink property in the JSON response. This property contains a fully qualified URL with a $skipToken query parameter. Your application must follow this exact URL for the next request. Attempting to parse or manually construct the $skipToken will result in invalid query errors. You cannot jump to arbitrary pages — your code needs to follow the full chain sequentially.
How Truto Simplifies SharePoint Integrations
Building and maintaining a SharePoint integration in-house is a significant commitment. Our own engineering team spent a full week — a senior engineer for five days, the CTO for two days, and half a day just getting a sandbox provisioned — building just the initial integration. And that was before handling the edge cases across different tenant configurations.
To see the full breadth of what building file storage integrations actually entails across providers, the challenge multiplies quickly.
Truto abstracts this entire layer away, providing a unified architecture that requires zero integration-specific code in your backend.
One Function for the File Picker
Instead of manually orchestrating popup windows and MessageChannel ports, Truto's Link SDK provides a single showFilePicker function that wraps the native Microsoft File Picker v8 for SharePoint — and works identically for OneDrive, Google Drive, Box, and Dropbox:
import { showFilePicker } from '@truto/truto-link-sdk';
showFilePicker('sharepoint', integratedAccountToken, {
selection: { mode: 'multiple' },
typesAndSources: { mode: 'all' },
search: { enabled: true }
})
.then((pickedItems) => {
console.log('Selected files:', pickedItems);
})
.catch((error) => {
console.error('User cancelled or error:', error);
});Your config is deep-merged with sensible defaults (search enabled, upload enabled, folder creation enabled, multi-select on), so you override only what you need.
Under the hood, Truto automatically:
- Fetches the target customer's integrated account to retrieve the correct
rootSiteUrl. - Injects a valid, unexpired OAuth token (refreshing it proactively if necessary).
- Manages the 1080x680 popup window and the MessageChannel
initializehandshake. - Normalizes the selected file payload into a consistent schema across all supported providers.
This same architecture powers integrations across all the messy file picker APIs we've had to tame — one SDK function, five native provider pickers, zero provider-specific code in your application.
Persistent Selections and JSONata Transformations
Truto solves one of the most frustrating aspects of the native SharePoint picker: state persistence. When a user opens the picker, Truto automatically pre-populates the sourceItems configuration field using data stored in the integrated account's context. Previously selected files remain checked when the user reopens the picker, providing a continuous user experience. This is a native Microsoft File Picker feature that most custom implementations miss entirely.
After file selection, Truto runs a consistent pipeline: deduplication by ID, optional JSONata transformation, and persistence to the integrated account context. The JSONata step lets you filter, reshape, or enrich files before storage:
// Add a timestamp and filter out non-PDFs before saving
config.trutoExpression = '$.[name ~> /\\.pdf$/i].($merge([{"selected_at": $now()}, $]))';This expression runs on the full array of selected items, allowing you to reshape or filter the payload instantly. The final data is persisted to the integrated account context, ready for your backend to consume via a standard webhook or API call.
OAuth Lifecycle Management
Truto manages the entire OAuth lifecycle for SharePoint — including proactive token refresh before expiration with distributed locking to prevent the race conditions described above. This eliminates the most common failure mode across the board. When a refresh does fail (the customer revoked access, for instance), Truto emits webhooks so your system can prompt re-authentication, and automatically notifies when the account recovers.
The Unified API Layer
Beyond the File Picker, Truto's Unified API and Proxy API let you interact with SharePoint data through normalized endpoints. The Unified API maps SharePoint resources into common data models shared across providers (Google Drive, Box, Dropbox, OneDrive), while the Proxy API gives you raw, unmapped access to SharePoint-specific features when you need it.
What This Means for Your Roadmap
SharePoint integration is not a weekend project. The authentication landscape is shifting (legacy auth methods are now retired), the rate limiting is opaque and dynamic, the File Picker v8 has real multi-tenant pitfalls, and the permissions model will surprise you.
Here's a practical decision framework:
| Scenario | Recommended Approach |
|---|---|
| You need file picker UI for 1-2 providers | Build it yourself with the File Picker v8 |
| You need file picker UI across 3+ cloud storage providers | Use a unified SDK (like Truto's showFilePicker) |
| You need background data sync from SharePoint | Evaluate build vs. buy based on sync volume and rate limit tolerance |
| You're building for enterprise customers with strict security reviews | Ensure your approach supports Sites.Selected permissions and handles token refresh proactively |
The engineering cost isn't just the initial build. It's the ongoing maintenance: Microsoft updates the File Picker, changes throttling behavior, retires auth models, and tweaks permission scopes. Every one of those changes requires your team to respond.
If SharePoint is one of many integrations your product needs — and it usually is — the math on building versus buying shifts fast.
FAQ
- How do I authenticate with the SharePoint API in 2026?
- You must use OAuth 2.0 via Microsoft Entra ID (formerly Azure AD). Legacy authentication methods including SharePoint Add-Ins and Azure ACS are fully retired as of April 2, 2026, and IDCRL is retired as of May 1, 2026. Register an app in Entra ID with either delegated or application permissions.
- How do I get all files from a SharePoint site using the API?
- You cannot query files directly. You must first resolve the Site ID, then list the Drives (document libraries) within that Site, and finally iterate through the Drive Items. Use $select and $filter parameters to minimize data transfer and reduce rate limit consumption.
- Why does the SharePoint File Picker v8 return a 500 Internal Server Error?
- This typically occurs due to an incorrect base URL (SharePoint uses /_layouts/15/FilePicker.aspx while OneDrive personal uses /picker), a token scoped to Microsoft Graph instead of the SharePoint resource, or an expired access token in the configuration payload.
- What are the SharePoint API rate limits?
- Microsoft does not publish exact throttling limits — they are dynamically adjusted based on tenant size, API type, and current load. When throttled, SharePoint returns HTTP 429 or 503 with a Retry-After header. Microsoft Graph generally consumes fewer resource units than the legacy REST API.
- Do I need Sites.ReadWrite.All permissions for a SharePoint integration?
- Not necessarily. Sites.ReadWrite.All grants access to every site in the tenant, which many enterprises will refuse. Use Sites.Selected for granular, per-site permissions — though it requires the tenant admin to explicitly grant access to each site your app needs.