Skip to content

SharePoint Access API

A data-firewalled surface in front of Microsoft Graph that exposes SharePoint Online sites, document libraries, lists, list items, sensitivity labels, and Microsoft Search to AI agents and automation tooling. Every call is gated by a PortEden access token with three-layer enforcement: operation flag, allow/block rules, and field masking.

Base URL

https://cliv1b.porteden.com/api/access/sharepoint

Rate Limits

60 req/min · 300 req/hr per IP

What it covers

  • SharePoint Online (Microsoft 365 tenants)
  • OneDrive-for-Business document libraries (treated as ordinary SharePoint drives — same endpoints, same DTO shapes)
  • Both delegated (per-user OAuth) and Sites.Selected (app-only, enterprise) connection modes — endpoints are identical from the caller's perspective

Key properties

  • Token-gated. Every call requires a PortEden access token (pe_…) that has explicitly opted into SharePoint access via sharePointAccessEnabled = true.
  • Opaque, prefixed IDs. All resource IDs ship with a sharepoint:… prefix. Treat them as opaque strings — never split, parse, or rebuild them client-side. They round-trip verbatim.
  • DTO reuse from Drive. File-level responses use the same TypeScript shapes as the Drive surface. Only the id prefix and provider field differ.
  • Three-layer enforcement. Operation bitflag → allow-all/block-all default → explicit allow/block rules.
  • Field masking. Responses are post-filtered to drop any field the token did not whitelist via visibleSharePointFields.

Quick Reference

EndpointMethodOperationDescription
/filesGETlist_files / search_filesSearch and list files
/files/{fileId}GETget_file_metadataGet file metadata
/files/{fileId}/downloadGETdownload_fileGet short-lived view / download URLs
/files/{fileId}/permissionsGETget_file_metadataGet sharing permissions
/files/uploadPOSTupload_fileUpload a new file (≤ 100 MB)
/foldersPOSTcreate_folderCreate a folder
/files/{fileId}/renamePATCHrename_fileRename a file or folder
/files/{fileId}/movePATCHmove_fileMove a file/folder
/files/{fileId}DELETEdelete_fileRecycle-bin a file/folder
/files/{fileId}/sharePOSTshare_fileShare with user/group/domain
/sitesGETread_sitesList sites
/sites/{siteId}GETread_sitesGet a single site
/sites/{siteId}/listsGETread_listsList lists in a site
/sites/{siteId}/lists/{listId}GETread_listsGet a list (with columns)
/sites/{siteId}/lists/{listId}/itemsGETread_list_itemList items in a list
/sites/{siteId}/lists/{listId}/items/{itemId}GETread_list_itemGet a list item
/sites/{siteId}/lists/{listId}/itemsPOSTwrite_list_itemCreate a list item
/sites/{siteId}/lists/{listId}/items/{itemId}PATCHwrite_list_itemUpdate a list item
/sites/{siteId}/lists/{listId}/items/{itemId}DELETEdelete_list_itemDelete a list item
/sensitivity-labelsGETread_sensitivity_labelsList the tenant's label catalogue
/files/{fileId}/sensitivity-labelGETread_sensitivity_labelsGet the label assigned to an item
/searchGETsearch_contentKQL search across files, list items, lists, sites

Authentication

All endpoints require a PortEden Access Token via Bearer authentication:

Authorization: Bearer pe_k1_abc123def456...

Required token state for any endpoint to return data:

  1. sharePointAccessEnabled === true
  2. The token's operator must have at least one connected SharePoint provider on a resource the token is linked to
  3. allowedSharePointOperations must include the operation flag the endpoint demands
  4. The targeted resource must not be blocked by a SharePoint rule attached to the token

Internal vs external base URL

The same surface is reachable from https://mgtv1b.porteden.com for internal management tooling. External integrations (CLI, MCP) should always use cliv1b.porteden.com.

Resource ID format

All SharePoint IDs are prefixed and composite. They are opaque to the caller — pass them through verbatim wherever the API asks for an ID.

ResourceEncoded formExample
File / foldersharepoint:drive:{driveId}:item:{itemId}sharepoint:drive:b!abc…:item:01ABCDEF…
Sitesharepoint:site:{hostname},{spsiteGuid},{spwebGuid}sharepoint:site:contoso.sharepoint.com,1111…,2222…
Listsharepoint:site:{siteId}:list:{listGuid}sharepoint:site:…:list:33333333-…
List itemsharepoint:site:{siteId}:list:{listGuid}:item:{itemId}sharepoint:site:…:list:…:item:42
Sensitivity labelbare GUID (no prefix)b8a3a3e4-…

Don't parse the ID

Rule patterns persisted on the backend are stored in their raw (unprefixed) form, but the API always emits and accepts the prefixed form. Don't mix them. The provider on returned DTOs is always the literal string "SHAREPOINT". Anything starting with something other than sharepoint: on a SharePoint endpoint is a bug — fail closed.

Permissions Model

Each request is gated by three layers in order: the operation bitflag → the token's allow-all / block-all default → explicit allow/block rules. Responses are then post-filtered through the field mask.

Layer 1: SharePoint access enabled

The master gate. When sharePointAccessEnabled is false, every SharePoint endpoint returns:

HTTP/1.1 422 Unprocessable Entity
{
"error": "SHAREPOINT_NOT_ENABLED",
"message": "SharePoint access is not enabled for this access token. Enable it at https://my.porteden.com."
}

Layer 2: Allowed operations

allowedSharePointOperations is a bitflag set serialised as a string array. Each endpoint requires one specific flag — calls without the required flag return 422 OPERATION_NOT_ALLOWED.

Files

API stringDescription
list_filesBrowse files inside a folder/library
search_filesFree-text + filter search across the connected SharePoint surface
get_file_metadataRead a single file's metadata or its sharing permissions
download_fileGet short-lived view / download URLs
upload_fileUpload a new file (binary body, ≤ 100 MB)
create_folderCreate a folder under a library or another folder
rename_fileRename a file or folder
move_fileMove a file/folder to a new parent
delete_fileRecycle-bin a file/folder (not permanent)
share_fileAdd a sharing permission
update_permissionsModify or remove an existing sharing permission

Sites & Lists

API stringDescription
read_sitesList and fetch SharePoint sites
read_listsList and fetch SharePoint lists (and their column schema)
read_list_itemRead a list item's fields
write_list_itemCreate or update list items
delete_list_itemDelete a list item

Labels & Search

API stringDescription
read_sensitivity_labelsEnumerate the tenant's sensitivity-label catalogue and the label assigned to a specific item
search_contentRun a Microsoft Search KQL query across driveItem, listItem, list, site

Composite Flags

API stringEquivalent flags
read_onlylist_files + search_files + get_file_metadata + download_file + read_sites + read_lists + read_list_item + read_sensitivity_labels + search_content
write_fileupload_file + create_folder
edit_filerename_file + move_file
write_listwrite_list_item + delete_list_item
allevery flag above

Note

Default if a token is created without specifying operations: read_only.

Layer 3: Allow-all vs block-all

sharePointAllowAllDefault behaviourRules behave as
false (default)All resources blockedallow list — explicit allow rules unlock specific resources
trueAll resources allowedblock list — explicit block rules deny specific resources

Block rules always win. If the token is in block-all mode and performs an upload / create_folder / create_list_item, the backend auto-creates a matching allow rule for the new resource so the token can read it back.

SharePoint rule types

Rules attach to the token (managed via /api/access-tokens/{tokenId}/sharepoint-rules). Each rule has a ruleType, a raw pattern, and an action (allow | block). Matching is performed against the raw identifier — prefixed forms in this documentation are decoded server-side automatically.

ruleTypePattern isMatches
file_ida single file/folder IDexact item match
foldera folder IDevery descendant of the folder
drive_ida drive IDevery item inside that drive (document library)
site_ida composite site IDeverything under the site (drives, lists, items)
list_ida list's GUIDevery list item in the list
list_item_ida list item IDexact item match
mime_typea MIME string with optional *files matching the MIME type
sensitivity_label_ida label GUIDitems carrying the label
content_type_ida SharePoint content-type IDlist items of that content type

404 vs BLOCKED

A 404 returned to the caller may be a real "missing resource" or it may be a policy-driven block — when the response code is BLOCKED, treat it as a policy decision, not a missing-resource.

Visible fields (post-response field mask)

visibleSharePointFields is the post-response field-mask. Any field the token does not whitelist will be omitted from the JSON payload (or returned as null), regardless of whether the underlying user has permission to see it.

API stringApplies toControls
namefiles, list items, sites, listsdisplay name
mime_typefilesmimeType
sizefilesbyte size
created_timeallcreatedDateTime / createdTime
modified_timealllastModifiedDateTime / modifiedTime
ownersfilesowners[]
shared_withfilessharedWith[]
descriptionfiles, sites, listsdescription
web_view_linkallwebUrl / webViewLink
download_linkfilesdownloadLink and the downloadUrl returned by /files/{fileId}/download
parent_folderfilesparentFolderId, parentFolderName
sensitivity_labelfiles, list itemsembedded label info on driveItem responses + the /sensitivity-label endpoint
site_namelist items, listssite display name on list-scoped responses
list_namelist itemsparent list's display name
content_typelist itemscontentType
field_valueslist itemsthe fields dictionary — turn off to hide row contents
allshorthand for everything above

field_values is high-blast-radius

Turn it off to give a token visibility into list metadata (which lists exist, when they changed, who owns them) without exposing the actual row contents. Default for new tokens is all.

SharePoint Error Codes

All error bodies are JSON { "error": "<code>", "message": "<human-readable>" } unless noted.

HTTPerror codeWhen it firesRecommended caller behaviour
401Missing / malformed Authorization header, or revoked / expired tokenre-auth
422SHAREPOINT_NOT_ENABLEDsharePointAccessEnabled = falseguide user to enable SharePoint on the token
422NO_SHAREPOINT_PROVIDEROperator has no SharePoint connection on any of the token's resourcesguide user to connect SharePoint
422OPERATION_NOT_ALLOWEDOperation flag missing from allowedSharePointOperationsname the missing flag in the message
400INVALID_IDEmpty or oversize siteId / listId / fileId / itemIdlikely caller bug — log and abort
400INVALID_QUERYSearch q is empty/whitespaceprompt for a query
403READ_ONLYConnection has only read scopesguide user to reconnect with write scopes
403NO_PROVIDERNo SharePoint connection or all connections excluded by token rulessame as NO_SHAREPOINT_PROVIDER
404BLOCKEDA rule (block action, or block-all default with no allow match) matchedsurface as policy denial, not 'not found'
404Real Graph 404 (item missing)surface as missing-resource
429Rate limit exceeded (PortEden or Microsoft Graph upstream)back off using Retry-After
5xxGraph upstream error or unexpected exceptionretry with exponential backoff

Render accessInfo verbatim

The accessInfo string returned in successful responses (and on some 4xx bodies) is already user-formatted with https://my.porteden.com deep links. Render it as plain text — do not template over it.

Rate Limits

Two IP-bucketed limits cover the SharePoint surface, applied on top of any per-token quota:

ScopeLimit
Per IP, rolling 60 s60 requests
Per IP, rolling 1 h300 requests

Standard rate-limit headers are emitted on every response:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 41
X-RateLimit-Reset: 1714330080
Retry-After: 7 # only on 429

Microsoft Graph throttling is also possible upstream — when Graph returns 429, PortEden propagates the same status with the Retry-After header forwarded. Treat 429 as "back off and retry," never as a permanent failure.

Data Types Reference

All TypeScript types below are emitted as camelCase JSON. Optional fields may be absent or null.

Reused from Drive (/api/access/drive)

File-level responses share these shapes with the Drive surface — only the id prefix and provider string differ.

interface DriveFileDto {
id: string; // "sharepoint:drive:…:item:…"
name?: string;
mimeType?: string;
size?: number; // bytes
createdTime?: string; // ISO 8601
modifiedTime?: string; // ISO 8601
owners?: DriveUserDto[];
sharedWith?: DriveUserDto[];
description?: string;
webViewLink?: string;
downloadLink?: string;
parentFolderId?: string;
parentFolderName?: string;
labels?: Record<string, string>;
isFolder: boolean;
provider: string; // always "SHAREPOINT" on this surface
}
interface DriveUserDto {
email: string;
displayName?: string;
role?: string; // "owner" | "writer" | "reader" | "commenter"
}
interface DrivePermissionDto {
id: string;
type: string; // "user" | "group" | "domain" | "anyone"
role: string; // "owner" | "writer" | "reader" | "commenter"
emailAddress?: string;
domain?: string;
displayName?: string;
}
interface DriveFileListResponse {
files: DriveFileDto[];
nextPageToken?: string;
hasMore: boolean;
accessInfo?: string;
authWarnings?: string[];
}
interface DriveFileResponse {
file?: DriveFileDto;
accessInfo?: string;
}
interface DriveFileLinkResponse {
success: boolean;
webViewLink?: string;
downloadUrl?: string;
exportLinks?: Record<string, string>; // always null/absent for SharePoint
fileName?: string;
mimeType?: string;
size?: number;
isGoogleWorkspaceFile: boolean; // always false for SharePoint
errorMessage?: string;
errorCode?: string;
}
interface DrivePermissionsResponse {
permissions: DrivePermissionDto[];
accessInfo?: string;
}
interface DriveOperationResult {
success: boolean;
fileId?: string; // populated for create/upload responses
errorMessage?: string;
errorCode?: string; // "READ_ONLY" | "NO_PROVIDER" | "BLOCKED" | "OPERATION_NOT_ALLOWED" |
}

Reused request bodies

interface CreateDriveFolderRequest {
name: string;
parentFolderId?: string;
description?: string;
}
interface RenameDriveFileRequest {
newName: string;
}
interface MoveDriveFileRequest {
destinationFolderId: string;
}
interface ShareDriveFileRequest {
type: "user" | "group" | "domain" | "anyone";
role: "reader" | "writer" | "commenter";
emailAddress?: string;
domain?: string;
sendNotification?: boolean; // default true
message?: string;
}

SharePoint-specific

interface SharePointSiteDto {
id: string; // "sharepoint:site:hostname,siteGuid,webGuid"
displayName?: string;
name?: string;
description?: string;
webUrl?: string;
createdDateTime?: string; // ISO 8601
lastModifiedDateTime?: string;
isPersonalSite: boolean;
}
interface SharePointSiteListResponse {
sites: SharePointSiteDto[];
nextPageToken?: string;
hasMore: boolean;
accessInfo?: string;
}
interface SharePointListDto {
id: string; // "sharepoint:site:…:list:…"
displayName?: string;
name?: string;
description?: string;
webUrl?: string;
template?: string; // "genericList" | "documentLibrary" | "tasks" | "events" |
hidden: boolean;
createdDateTime?: string;
lastModifiedDateTime?: string;
columns?: SharePointColumnDto[]; // populated on GET /lists/{listId}, omitted on listing
}
interface SharePointColumnDto {
name?: string; // API name use in $filter / selectFields
displayName?: string; // human label
columnType?: string; // "text" | "number" | "choice" | "dateTime" | "personOrGroup" | "lookup" |
hidden: boolean;
readOnly: boolean;
required: boolean;
indexed: boolean;
}
interface SharePointListListResponse {
lists: SharePointListDto[];
nextPageToken?: string;
hasMore: boolean;
accessInfo?: string;
}
interface SharePointListItemDto {
id: string; // "sharepoint:site:…:list:…:item:…"
name?: string;
webUrl?: string;
contentType?: string;
createdDateTime?: string;
lastModifiedDateTime?: string;
fields?: Record<string, unknown>; // null when the token has field_values masked off
createdBy?: string;
lastModifiedBy?: string;
}
interface SharePointListItemListResponse {
items: SharePointListItemDto[];
nextPageToken?: string;
hasMore: boolean;
accessInfo?: string;
}
interface CreateListItemRequest {
fields: Record<string, unknown>; // keyed by column API name
}
interface UpdateListItemRequest {
fields: Record<string, unknown>; // only included keys are updated
}
interface SensitivityLabelDto {
id: string; // bare GUID, no prefix
displayName?: string;
description?: string;
color?: string; // "Green" | "Yellow" | "Orange" | "Red" |
sensitivity: number; // priority/severity, higher = more sensitive
toolTip?: string;
isActive: boolean;
isAppliable: boolean;
}
interface SensitivityLabelListResponse {
labels: SensitivityLabelDto[];
accessInfo?: string;
}
interface ItemSensitivityLabelResponse {
success: boolean;
labelId?: string;
labelDisplayName?: string;
assignmentMethod?: "standard" | "privileged" | "auto";
assignedDateTime?: string;
errorMessage?: string;
}
interface SharePointSearchHitDto {
entityType: "driveItem" | "listItem" | "list" | "site";
id: string; // already prefixed
title?: string;
summary?: string;
webUrl?: string;
lastModifiedDateTime?: string;
}
interface SharePointSearchResponse {
hits: SharePointSearchHitDto[];
from: number;
total: number; // unfiltered Graph hit count
moreResultsAvailable: boolean;
accessInfo?: string;
}

MCP Tool Authoring Notes

For teams mapping these endpoints onto Claude / ChatGPT tools, a few hard rules:

  1. Always treat IDs as opaque. Round-trip the prefixed string the API returns. Do not try to construct one from raw Graph IDs — the : separators look composable but the format is reserved.
  2. Don't ask the model for selectFields blindly. When the model doesn't know the schema, fetch the list once (GET /lists/{listId}) and offer the column API names as enum values in the tool schema.
  3. Render accessInfo verbatim in the tool's natural-language response. It contains the canonical "go to https://my.porteden.com" guidance the user expects.
  4. Surface BLOCKED explicitly. Don't collapse it into a 404 in the tool's UX — users need to know when an item exists but is policy-blocked, vs. when it genuinely doesn't exist.
  5. Cache the sensitivity-label catalogue per session. It rarely changes and the endpoint is cheap, but every call still consumes a tenant-wide quota.
  6. Re-fetch download URLs every time. They expire in roughly an hour and are unauthenticated — caching one in chat memory is a confidentiality footgun.
  7. Page list-item queries. Default limit=50 is usually fine; for bulk export tools, raise to 200 (the cap) and follow pageToken.
  8. KQL is the search language, not Lucene and not raw SQL. Quote-wrap queries with spaces ("quarterly review") and use field selectors (Title:"Q1", FileType:docx) for precision.