← Back to articles Intune

Planner Capacity View (GA Oct 2026): Resource Allocation, Workload Visualization & Over-allocation Prevention

Planner Capacity View (GA Oct 2026): Resource Allocation, Workload Visualization & Over-allocation Prevention

Microsoft Planner's Capacity View lands as the most significant resource-management addition to the unified Planner platform since Project for the Web merged into it. For the first time, project managers can see exactly who is overloaded, by how much, and reassign work without ever leaving Planner — all backed by Dataverse's bookable resource engine. This guide walks you through every layer: architecture, licensing gates, Graph API integration, Dataverse queries, and the PowerShell automation needed to run this at enterprise scale.

⚠ GA Timeline Notice Roadmap ID 569733 targets General Availability in October 2026. Features described here reflect confirmed roadmap capabilities and pre-GA preview behaviour. Validate against your Message Center (MC-prefixed announcements filtered on Planner / Project) before deploying governance policies.

1. Full System Architecture

PRESENTATION API LAYER BACKEND ENGINE DATA STORE Planner Web / Teams planner.microsoft.com View Switcher Board · Grid · Schedule · Capacity Capacity View Renderer · Heatmap · Drag-Drop License Gate Plan 1 / Plan 3 / Plan 5 check Drag-Drop Reassignment PATCH → Dataverse write Over-allocation Alert Red cell · Threshold breach Microsoft Graph API graph.microsoft.com/v1.0 Planner Tasks Endpoint /planner/plans/{id}/tasks Dataverse Web API {org}.crm.dynamics.com/api/data OData $filter / $expand Time-phased query projection MSAL / Entra ID Token Delegated + App-only scopes Capacity Bucket Engine Time-phased allocation computation Effort Aggregation Sum task hours per user per bucket Working Hours Calendar bookableresource capacity baseline Threshold Detection Green ≤80% · Amber ≤100% · Red >100% Reassignment Write-back PATCH msdyn_resourceassignment Portfolio Rollup (Plan 5) Cross-project capacity aggregation Microsoft Dataverse Project for the Web storage layer msdyn_projecttask effort · duration · start · finish msdyn_resourceassignment hours per contour · assignee ref msdyn_projectteammember user · role · required hours bookableresource work hours template · capacity bookableresourcebooking confirmed bookings · start/end msdyn_resourcerequirement demand signal · unfulfilled needs Data read flow Write-back flow Threshold / gate Return read path Active data pulse (animated)
Full Planner Capacity View architecture: user actions in the Planner UI trigger Graph API and Dataverse Web API calls. The capacity bucket engine aggregates task effort against bookable resource calendars, detects over-allocation thresholds, and writes reassignment changes back to Dataverse in real time.

2. Licensing Reality Check

The biggest production mistake with Capacity View will be assuming it works for all Planner users. It doesn't. The feature is gated at the plan type level, not just the user license level — both conditions must be true simultaneously.

Planner Plan 1

Basic Planner + Project for the Web access. Schedule and Grid views available. Capacity View: not available. No effort fields on tasks.

Planner Plan 3 REQUIRED

Full Capacity View access. Per-user effort tracking, team-level heatmap, drag-drop reassignment, Dataverse resource management. Minimum for this feature.

Planner Plan 5

Everything in Plan 3 plus portfolio-level capacity rollup across multiple plans/projects. Cross-project over-allocation detection. Needed for enterprise PMO scenarios.

M365 Copilot (Add-on)

AI-driven capacity suggestions — "reassign this task to reduce over-allocation." Works on top of Plan 3+. Not required for core Capacity View functionality.

🔴 Silent Failure Mode If a user has Planner Plan 3 but opens a plan that was not created as a Premium (Project for the Web) plan, the Capacity view tab will not appear at all — no error, no message. The task effort fields simply don't exist in basic Planner plans. Always verify plan type: a Premium plan shows Schedule, Grid, Goals, and Board in the view switcher. A basic plan only shows Board, Charts, Schedule.

3. How the Capacity Calculation Works

Understanding the math behind the heatmap prevents misreading the data. The engine runs a time-phased aggregation on a per-user, per-bucket (day or week) basis, compared against the user's working-hour baseline from their bookableresource calendar entry in Dataverse.

STEP 1 Collect Inputs Task effort + start/end dates Assignee list per task STEP 2 Calendar Lookup bookableresource entity Working hrs template → daily cap STEP 3 Time-Phase Split Distribute effort across buckets Daily = effort ÷ duration days STEP 4 Aggregate & Compare Σ task hours per user per bucket vs. calendar capacity Ratio Check GREEN ≤ 80% of capacity Within safe working threshold No action required AMBER 81% – 100% of capacity Approaching limit — review soon Hover for task breakdown RED > 100% of capacity Over-allocated — intervention needed Drag-drop or date adjustment ≤80% 81-100% >100%
The 4-step capacity calculation pipeline: task effort is collected, mapped to working-hour calendars, distributed across daily/weekly buckets, aggregated per user, then evaluated against three threshold bands. Green = safe, Amber = at risk, Red = over-allocated and requires remediation.
💡 Pro tip: Effort distribution model Planner uses a flat contour by default — effort is divided equally across all working days in the task's start-to-due window. A task with 16 hours effort spanning 4 working days contributes 4 hours per day per assignee. This is important when tasks span weekends or holidays: if the resource calendar has no working hours on those days, the engine skips them and recalculates the contour across remaining working days.

4. Prerequisites & Admin Setup

  1. Verify Tenant Release Ring

    Navigate to Microsoft 365 Admin Center → Settings → Org settings → Organization profile → Release preferences. For the pre-GA window, you need Targeted Release (entire org or selected users). Post-GA (October 2026), Standard Release is sufficient. Check Message Center for MC announcements tagged Planner or Project.

  2. Enable Project for the Web at Tenant Level

    Go to Admin Center → Settings → Org settings → Project. Ensure "Turn on Project for the Web for your organization" is enabled. Without this, Premium Planner plans cannot be created and Capacity View will never appear.

    # Verify Project for the Web service plan is enabled on SKU
    Connect-MgGraph -Scopes 'Organization.Read.All'
    Get-MgSubscribedSku | Where-Object { $_.SkuPartNumber -like '*PROJECT*' } |
        Select-Object SkuPartNumber, CapabilityStatus,
            @{N='ConsumedUnits';E={$_.ConsumedUnits}},
            @{N='PrepaidUnits';E={$_.PrepaidUnits.Enabled}}
  3. Assign Planner Plan 3 Licenses

    The SKU part number is PROJECTPREMIUM for Planner Plan 3 / Project Plan 3. Use the bulk assignment script in Section 7 for production deployments. Individual assignment via Admin Center → Users → Active Users → Licenses also works for testing.

  4. Create or Upgrade to a Premium Plan

    In Planner web, click New plan and select Premium plan (or "Create from template" for project templates). Existing basic plans cannot be converted to Premium — tasks must be migrated. Teams-connected plans: click the Planner tab in Teams, then choose to upgrade or create a new Premium plan alongside the existing one.

  5. Populate Task Effort Fields

    In the Grid or Board view, open each task → Task details pane → set the Effort field (hours). Set explicit Start date and Due date. Tasks without effort values register as zero hours and make the Capacity View unreliable. Use the PowerShell audit script in Section 7 to find all zero-effort tasks at scale.

  6. Verify Resource Calendar in Dataverse

    Each plan member needs a corresponding bookableresource record in Dataverse with a Work Hours template assigned. Navigate to Power Platform Admin Center → Environments → [your env] → Dataverse → Tables → Bookable Resource to verify. Missing records default to 8 hours/day but will not reflect custom schedules, part-time workers, or time zones.

  7. Open the Capacity View

    In your Premium plan, click the view switcher in the top bar → select Capacity. The view renders immediately if data is populated. Toggle between Daily and Weekly buckets using the controls in the upper-right toolbar. Use the date range picker to focus on a sprint or quarter.

5. Graph API & Dataverse Queries

The Capacity View renders from Dataverse data, not from the standard Graph Planner API. For automation, reporting, or building custom dashboards, you need to query Dataverse directly. The Graph API covers basic task CRUD; capacity data lives exclusively in Dataverse.

5.1 Standard Graph API — Task Operations

# GET all tasks in a Premium plan
GET https://graph.microsoft.com/v1.0/planner/plans/{plan-id}/tasks

# GET specific task detail (includes effort via plannerTaskDetails)
GET https://graph.microsoft.com/v1.0/planner/tasks/{task-id}/details

# PATCH a task to update assignee (triggers reassignment write-back)
PATCH https://graph.microsoft.com/v1.0/planner/tasks/{task-id}
Content-Type: application/json
If-Match: {etag}

{
  "assignments": {
    "{new-user-aad-id}": {
      "@odata.type": "#microsoft.graph.plannerAssignment",
      "orderHint": " !"
    },
    "{old-user-aad-id}": null
  }
}
⚠ ETag Requirement on PATCH All PATCH operations against Planner tasks require the If-Match header with the current ETag from a prior GET. Stale ETags return HTTP 412 Precondition Failed. Always fetch the current ETag immediately before patching in automated pipelines.

5.2 Dataverse Web API — Capacity Data

# GET all project tasks with effort and date fields
GET https://{org}.crm.dynamics.com/api/data/v9.2/msdyn_projecttasks
    ?$select=msdyn_subject,msdyn_effort,msdyn_scheduledstart,
             msdyn_scheduledend,msdyn_project
    &$filter=msdyn_effort gt 0
    &$orderby=msdyn_scheduledstart asc

# GET resource assignments with allocated hours per contour
GET https://{org}.crm.dynamics.com/api/data/v9.2/msdyn_resourceassignments
    ?$select=msdyn_name,msdyn_hours,msdyn_taskid,msdyn_bookableresourceid
    &$expand=msdyn_bookableresourceid($select=name,msdyn_calendarid)

# GET bookable resources with calendar capacity
GET https://{org}.crm.dynamics.com/api/data/v9.2/bookableresources
    ?$select=name,msdyn_calendarid,bookableresourceid
    &$filter=statecode eq 0

# GET confirmed bookings (cross-reference with assignments)
GET https://{org}.crm.dynamics.com/api/data/v9.2/bookableresourcebookings
    ?$select=name,starttime,endtime,duration,bookingstatus
    &$filter=starttime ge 2026-10-01T00:00:00Z
    &$orderby=starttime asc

# PATCH a resource assignment to update allocated hours (reassignment write-back)
PATCH https://{org}.crm.dynamics.com/api/data/v9.2/msdyn_resourceassignments({assignment-id})
Content-Type: application/json

{
  "msdyn_bookableresourceid@odata.bind": "/bookableresources({new-resource-id})",
  "msdyn_hours": 16
}

5.3 Required API Permissions

API Target Scope / Permission Type Capacity Data?
Microsoft Graph Tasks.ReadWrite Delegated ✗ Basic tasks only
Microsoft Graph Tasks.ReadWrite.All Application ✗ Basic tasks only
Dataverse Web API user_impersonation on Dynamics CRM Delegated ✓ Full capacity data
Dataverse Web API App Registration + Dataverse Security Role Application ✓ Full capacity data
Power Platform Admin Power Platform Administrator role Entra Role ✓ Environment + resource config
🔴 Dataverse App Registration Gotcha Adding API permissions in Entra ID is not enough for Dataverse app-only access. You must also create an Application User inside the Dataverse environment itself (Power Platform Admin Center → Environments → [env] → Users → Application users) and assign it a security role (minimum: Project User or custom role with read on the relevant tables). Without this, all Dataverse API calls return HTTP 403 regardless of Entra token validity.

6. Over-allocation Detection State Machine

UNASSIGNED No effort / no assignee Ignored by engine AVAILABLE Assigned, effort set Ratio ≤ 80% AT RISK 81% – 100% capacity Amber heatmap cell OVER-ALLOCATED > 100% capacity Red cell · Alert generated RESOLVED Reassigned / effort reduced Returns to Available Effort added Load ↑ 80% Load > 100% Task removed / effort ↓ Drag-drop or date adjust Engine recalculates Reassign all tasks
Over-allocation state machine: a resource starts Unassigned, moves to Available once effort is added, transitions to At Risk as load approaches capacity, then Over-Allocated when it exceeds 100%. Drag-drop reassignment or effort reduction moves the resource to Resolved, after which the engine recalculates and returns the state to Available.

7. Enterprise PowerShell Automation Suite

The following scripts cover the six most critical operational needs for deploying Capacity View at scale. Each script is production-ready with parameter blocks, error handling, and explicit exit codes.

Script 1 — Bulk Planner Plan 3 License Assignment with Validation

#Requires -Modules Microsoft.Graph.Users, Microsoft.Graph.Identity.DirectoryManagement

#Requires -Version 7.2
[CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)][string]$TenantId, [Parameter(Mandatory)][string[]]$UserPrincipalNames, [Parameter()][string]$SkuPartNumber = 'PROJECTPREMIUM', [Parameter()][string]$OutputCsvPath = '.\LicenseAssignment_Results.csv' ) $ErrorActionPreference = 'Stop' $results = [System.Collections.Generic.List[PSObject]]::new() try { Write-Host "[+] Connecting to Microsoft Graph..." -ForegroundColor Cyan Connect-MgGraph -TenantId $TenantId ` -Scopes 'User.ReadWrite.All', 'Organization.Read.All', 'Directory.Read.All' ` -NoWelcome # Resolve SKU ID $sku = Get-MgSubscribedSku -All | Where-Object { $_.SkuPartNumber -eq $SkuPartNumber -and $_.CapabilityStatus -eq 'Enabled' } if (-not $sku) { throw "SKU '$SkuPartNumber' not found or not enabled in tenant '$TenantId'. " + "Verify the license has been purchased." } $availableUnits = $sku.PrepaidUnits.Enabled - $sku.ConsumedUnits Write-Host "[+] SKU: $($sku.SkuPartNumber) | Available seats: $availableUnits" if ($availableUnits -lt $UserPrincipalNames.Count) { throw "Insufficient seats: $availableUnits available, " + "$($UserPrincipalNames.Count) requested. Purchase additional licenses first." } foreach ($upn in $UserPrincipalNames) { $record = [PSCustomObject]@{ UPN = $upn UserId = '' Status = '' AlreadyHad = $false Error = '' Timestamp = (Get-Date -Format 'o') } try { $user = Get-MgUser -UserId $upn -Property Id, DisplayName, AssignedLicenses, UserPrincipalName -ErrorAction Stop $record.UserId = $user.Id $alreadyLicensed = $user.AssignedLicenses.SkuId -contains $sku.SkuId if ($alreadyLicensed) { $record.Status = 'AlreadyAssigned' $record.AlreadyHad = $true Write-Host " [=] $upn already has $SkuPartNumber — skipping" -ForegroundColor Yellow } else { if ($PSCmdlet.ShouldProcess($upn, "Assign $SkuPartNumber license")) { Set-MgUserLicense -UserId $user.Id ` -AddLicenses @{ SkuId = $sku.SkuId; DisabledPlans = @() } ` -RemoveLicenses @() | Out-Null $record.Status = 'Assigned' Write-Host " [+] $upn → $SkuPartNumber assigned" -ForegroundColor Green } } } catch { $record.Status = 'Failed' $record.Error = $_.Exception.Message Write-Warning " [!] $upn failed: $($_.Exception.Message)" } $results.Add($record) } $results | Export-Csv -Path $OutputCsvPath -NoTypeInformation -Encoding UTF8 Write-Host "[+] Results exported to: $OutputCsvPath" -ForegroundColor Cyan $summary = $results | Group-Object Status Write-Host "`n=== SUMMARY ===" $summary | ForEach-Object { Write-Host " $($_.Name): $($_.Count)" } exit 0 } catch { Write-Error "[FATAL] $($_.Exception.Message)" exit 1 } finally { Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null }

Script 2 — Task Effort Audit: Find All Plans with Missing Effort Values

#Requires -Modules Microsoft.Graph.Planner, Microsoft.Graph.Groups

#Requires -Version 7.2
[CmdletBinding()] param( [Parameter()][string]$TenantId, [Parameter()][string]$OutputCsvPath = '.\TaskEffortAudit.csv', [Parameter()][switch]$PremiumPlansOnly ) $ErrorActionPreference = 'Stop' try { Connect-MgGraph -TenantId $TenantId ` -Scopes 'Group.Read.All', 'Tasks.Read.All', 'User.Read.All' ` -NoWelcome Write-Host "[+] Enumerating Microsoft 365 Groups with Planner plans..." $groups = Get-MgGroup -All -Filter "resourceProvisioningOptions/Any(x:x eq 'Team')" ` -Property Id, DisplayName -PageSize 999 $auditRows = [System.Collections.Generic.List[PSObject]]::new() $totalPlans = 0 $totalTasks = 0 $zeroEffort = 0 foreach ($group in $groups) { try { $plans = Get-MgGroupPlannerPlan -GroupId $group.Id -ErrorAction SilentlyContinue if (-not $plans) { continue } foreach ($plan in $plans) { $totalPlans++ $tasks = Get-MgPlannerPlanTask -PlannerPlanId $plan.Id ` -All -ErrorAction SilentlyContinue if (-not $tasks) { continue } foreach ($task in $tasks) { $totalTasks++ # Graph Planner API surfaces effort as null for non-Premium plans $effortHours = $task.AdditionalProperties.'effort' $hasStart = [bool]$task.StartDateTime $hasDue = [bool]$task.DueDateTime $isZero = (-not $effortHours -or $effortHours -eq 0) if ($isZero -or -not $hasStart -or -not $hasDue) { $zeroEffort++ $auditRows.Add([PSCustomObject]@{ GroupName = $group.DisplayName GroupId = $group.Id PlanTitle = $plan.Title PlanId = $plan.Id TaskTitle = $task.Title TaskId = $task.Id EffortHours = $effortHours HasStartDate = $hasStart HasDueDate = $hasDue AssigneeCount = $task.Assignments.AdditionalProperties.Count PercentComp = $task.PercentComplete Severity = if (-not $effortHours) { 'HIGH - No Effort' } elseif (-not $hasStart -or -not $hasDue) { 'MEDIUM - Missing Dates' } else { 'LOW - Zero Effort' } AuditTime = (Get-Date -Format 'o') }) } } } } catch { Write-Verbose "Group $($group.DisplayName) skipped: $($_.Exception.Message)" } } $auditRows | Export-Csv -Path $OutputCsvPath -NoTypeInformation -Encoding UTF8 Write-Host "`n=== EFFORT AUDIT SUMMARY ===" -ForegroundColor Cyan Write-Host " Plans scanned : $totalPlans" Write-Host " Tasks scanned : $totalTasks" Write-Host " Issues found : $zeroEffort" Write-Host " Report path : $OutputCsvPath" exit 0 } catch { Write-Error "[FATAL] $($_.Exception.Message)" exit 1 } finally { Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null }

Script 3 — Dataverse Resource Capacity Baseline Extraction & Over-allocation CSV Export

#Requires -Version 7.2

# Dependencies: MSAL.PS module for Dataverse token acquisition
# Install-Module MSAL.PS -Scope CurrentUser
[CmdletBinding()] param( [Parameter(Mandatory)][string]$TenantId, [Parameter(Mandatory)][string]$DataverseOrgUrl, # e.g. https://contoso.crm.dynamics.com [Parameter(Mandatory)][string]$ClientId, # App registration client ID [Parameter(Mandatory)][string]$ClientSecret, [Parameter()][datetime]$PeriodStart = (Get-Date).Date, [Parameter()][datetime]$PeriodEnd = (Get-Date).Date.AddDays(30), [Parameter()][double]$AmberThreshold = 0.80, [Parameter()][double]$RedThreshold = 1.00, [Parameter()][string]$OutputCsvPath = '.\CapacityBaseline.csv' ) $ErrorActionPreference = 'Stop' function Get-DataverseToken { param([string]$TenantId, [string]$ClientId, [string]$ClientSecret, [string]$Resource) $body = @{ grant_type = 'client_credentials' client_id = $ClientId client_secret = $ClientSecret scope = "$Resource/.default" } $tokenEndpoint = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" $response = Invoke-RestMethod -Method Post -Uri $tokenEndpoint -Body $body return $response.access_token } function Invoke-DataverseRequest { param([string]$Uri, [string]$Token) $headers = @{ Authorization = "Bearer $Token" 'OData-MaxVersion' = '4.0' 'OData-Version' = '4.0' Accept = 'application/json' Prefer = 'odata.include-annotations=*' } $allRecords = [System.Collections.Generic.List[object]]::new() $nextLink = $Uri do { $resp = Invoke-RestMethod -Method Get -Uri $nextLink -Headers $headers $resp.value | ForEach-Object { $allRecords.Add($_) } $nextLink = $resp.'@odata.nextLink' } while ($nextLink) return $allRecords } try { Write-Host "[+] Acquiring Dataverse token..." $token = Get-DataverseToken -TenantId $TenantId -ClientId $ClientId ` -ClientSecret $ClientSecret -Resource $DataverseOrgUrl $startStr = $PeriodStart.ToString('yyyy-MM-ddT00:00:00Z') $endStr = $PeriodEnd.ToString('yyyy-MM-ddT23:59:59Z') # Fetch bookable resources (capacity baseline) Write-Host "[+] Fetching bookable resources..." $resourcesUri = "$DataverseOrgUrl/api/data/v9.2/bookableresources" + "?`$select=name,bookableresourceid,msdyn_calendarid&`$filter=statecode eq 0" $resources = Invoke-DataverseRequest -Uri $resourcesUri -Token $token Write-Host " Found $($resources.Count) active resources" # Fetch resource assignments within period Write-Host "[+] Fetching resource assignments for period $startStr → $endStr..." $assignUri = "$DataverseOrgUrl/api/data/v9.2/msdyn_resourceassignments" + "?`$select=msdyn_name,msdyn_hours,msdyn_bookableresourceid,msdyn_taskid" + "&`$expand=msdyn_bookableresourceid(`$select=name,bookableresourceid)" + "&`$filter=createdon ge $startStr and createdon le $endStr" $assignments = Invoke-DataverseRequest -Uri $assignUri -Token $token Write-Host " Found $($assignments.Count) assignments" # Build per-resource aggregated hours map $resourceHoursMap = @{} foreach ($a in $assignments) { $resId = $a.msdyn_bookableresourceid.bookableresourceid $resName= $a.msdyn_bookableresourceid.name if (-not $resId) { continue } if (-not $resourceHoursMap.ContainsKey($resId)) { $resourceHoursMap[$resId] = @{ Name = $resName TotalAssigned = 0 AssignCount = 0 } } $resourceHoursMap[$resId].TotalAssigned += [double]$a.msdyn_hours $resourceHoursMap[$resId].AssignCount++ } # Calculate capacity: 8h/day default; period = working days only $workingDays = 0 for ($d = $PeriodStart; $d -le $PeriodEnd; $d = $d.AddDays(1)) { if ($d.DayOfWeek -notin @([DayOfWeek]::Saturday, [DayOfWeek]::Sunday)) { $workingDays++ } } $capacityHoursPerResource = $workingDays * 8 # Build output rows $outputRows = [System.Collections.Generic.List[PSObject]]::new() foreach ($res in $resources) { $id = $res.bookableresourceid $data = $resourceH

Was this article helpful?

🎁 Free Community Automation Hub

Functional Automation & Blueprints

Production-ready scripts, GitHub repositories, and architectural blueprints created for this technical guide.

PowerShell, Microsoft Graph API, PHP/M365 SaaS Engine
AUTOMATION TOOLKIT

Planner Capacity Management Suite

Professional automation suite for managing Microsoft Planner capacity, licensing, and task workload auditing at enterprise scale.

Star on GitHub Download .ps1
💡 Enterprise Blueprint
HIGH IMPACT

PlannerCapacity Pro

Instantly surface over-allocated team members across all Microsoft Planner plans before burnout and missed deadlines strike.

🤝 Custom Build

🎓 Ready to go deeper?

Practice real MD-102 exam questions, get AI feedback on your weak areas, and fast-track your Intune certification.

Start Free Practice → Book a Session
Souhaiel Morhag
Souhaiel Morhag
Microsoft Endpoint & Modern Workplace Engineer

Souhaiel Morhag is a Microsoft Intune and endpoint management specialist with hands-on experience deploying and securing enterprise environments across Microsoft 365. He founded MSEndpoint.com to share practical, real-world guides for IT admins navigating Microsoft technologies — and built the MSEndpoint Academy at app.msendpoint.com/academy, a dedicated learning platform for professionals preparing for the MD-102 (Microsoft 365 Endpoint Administrator) certification. Through in-depth articles and AI-powered practice exams, Souhaiel helps IT teams move faster and certify with confidence.

Related Articles

Popular on MSEndpoint