Table of Contents

Altus_API_GetAssignmentByDay - Custom API Documentation

Note

This is an internal Altus API and is planned to change in the future - If you take a dependency on this API check release notes before deploying updates.

Warning

This API can return large volumes of data and consume high amounts of server resources. Use this API carefully with narrow filters and respect Microsoft Service Protection limits and retry guidance. Failure to do so may cause throttling and failures in other parts of the system.

Overview

Altus_API_GetAssignmentByDay is a Dynamics 365 / Power Apps custom API that returns assignment hours broken down by day for a specified project. It is registered as the sensei_assignmentbyday custom action and can be called from Power Automate, client scripts, or other plugins.

The API supports two project types:

  • Native Altus projects: schedule data is synthesized from Dynamics task and assignment records.
  • External (imported) projects: schedule data is read from a published Microsoft Project (.mpp) file attached to the project.

The actual hours feature is only relevant for MPP-backed external projects. For native Altus projects, the API intentionally does not populate protected actuals.


Request

  • Type: POST
  • URI: https://<host>.crm.dynamics.com/api/data/v9.2/sensei_assignmentbyday
  • Headers:
    • Content-Type: application/json

Example request

{
  "projectid": "2beacc65-c52b-f011-8c4d-002248112dcc",
  "start": "2025-12-31T13:30:00.000Z",
  "finish": "2026-06-29T14:30:00.000Z",
  "aggregationType": "resource",
  "resourceids": [],
  "taskids": [],
  "includeRelatedNamedResources": true,
  "totalsonly": false,
  "includeprotectedactuals": true
}

Parameters

Name Type Required Description
projectid Guid Yes Project record to query. The API validates the project exists.
start DateTime No Inclusive lower bound for the date range. If omitted, no lower bound is applied.
finish DateTime No Inclusive upper bound for the date range. If omitted, no upper bound is applied. If both are supplied and start > finish, the API throws an InvalidPluginExecutionException.
aggregationType string No resource, task, or none. Default is none. Case-insensitive.
resourceids string[] No Bookable resource GUIDs to include. If omitted, all resources are included.
taskids string[] No Project task GUIDs to include. If omitted, all tasks are included.
includeRelatedNamedResources bool No Whether to include assignments linked through named (generic) resources. Default is true.
totalsonly bool No If true, the API omits the per-day A array and returns only subtotal values. Default is false.
includeprotectedactuals bool No If true, the API includes a protected-actuals subtotal in the P field for MPP-backed projects. Default is false.

Data source logic

The API determines the schedule source as follows.

  1. If the project has an External System Reference:
    • It looks for an attached Microsoft Project (.mpp) file in the project documents.
    • If an MPP file is found, it reads assignment data and maps it back to Altus task and resource IDs.
    • Planner MPPs are handled through a separate mapping path, because Planner tasks do not directly match the Altus IDs used by native project records.
    • If no MPP file is found, the API returns an empty dataset.
  2. If the project has no external system reference:
    • It synthesizes assignment-by-day data from Altus task and resource-assignment records by using SyntheticCalculatorService.

After the raw assignment-by-day records are loaded, the API applies the filters and grouping requested by the caller:

  • date range (start / finish)
  • resourceids / taskids
  • includeRelatedNamedResources
  • aggregation mode (aggregationType)

When actuals can be returned

The P field is not part of the general planned-hours payload. It only appears when the API is operating on an external MPP project and the caller explicitly sets includeprotectedactuals to true.

The actuals used by this API are not read from Altus timesheet records or finance actuals. They are read from the underlying MPP, and only for protected actuals - no actuals imported from Altus timesheets are returned.

Conditions where protected actuals are returned

  • The project is external / MPP-backed.
  • The API finds the attached MPP file.
  • includeprotectedactuals is set to true.
  • There is actual work in the MPP assignment data for the relevant task/resource combination.

Conditions where protected actuals are not returned

  • The project is a native Altus project: P is always absent.
  • includeprotectedactuals is omitted or set to false: P is not included.
  • There are no qualifying actual-work hours: P is omitted.
  • totalsonly is true: the per-day A array is omitted, but P can still be present when enabled.

How the actuals are calculated

In the MPP reader, the API uses the task's Date10 value as the protected-actuals epoch when it is present.

  • If a task has a Date10 value, the code sums only MPP actual-work slots where the timephased slot start is strictly before that epoch.
  • If a task does not have a Date10 value, the code falls back to the assignment's full actual-work total for that assignment - assuming that all actuals are protected.
  • The result is stored as ProtectedActualHours on the assignment record and then summed during aggregation.

This behaviour is implemented only for MPP based projects and is intentionally separate from the synthetic time-phased Altus project path, which does not attempt to populate actuals.

The API does not read actuals from approved timesheets or from the native Altus schedule. For native projects, the P value is deliberately null and omitted from the response.


Response format

The API writes a single JSON-serialized output parameter named data.

{
  "@odata.context": "https://<host>.crm.dynamics.com/api/data/v9.2/$metadata#Microsoft.Dynamics.CRM.sensei_assignmentbydayResponse",
  "data": "[<array of stringified response objects>]"
}

The data value is a JSON string containing an array of AssignmentByDayDataResponse objects. When there are no matching rows, the returned value is [].

Response fields

Each item in the array can contain the following fields:

Field Meaning
G Group key. This is the resource or task identifier used for the selected aggregation mode. It is empty when aggregationType is none.
T Task ID. Only emitted when present.
R Resource ID. Only emitted when present.
S Subtotal planned hours for the row or group.
P Protected actuals subtotal. Only emitted when includeprotectedactuals is enabled and the value is not null.
A Per-day assignment data. This is omitted when totalsonly is true, or when no data is present.

Example response

[
  {
    "G": "90cb0f2f-0c1d-4f4e-9d01-1d9d95d9f0d8",
    "T": "60e171be-1094-4d92-ae9a-f7a47f842e5e",
    "R": "e431f082-77cf-4302-b9df-1854f3f6ed6e",
    "S": 12.5,
    "P": 4.5,
    "A": [
      { "D": "2025-01-01", "H": 8.0 },
      { "D": "2025-01-02", "H": 4.5 }
    ]
  }
]

If totalsonly is true, the response is effectively the same shape without the A array:

[
  {
    "G": "90cb0f2f-0c1d-4f4e-9d01-1d9d95d9f0d8",
    "T": "60e171be-1094-4d92-ae9a-f7a47f842e5e",
    "R": "e431f082-77cf-4302-b9df-1854f3f6ed6e",
    "S": 12.5,
    "P": 4.5
  }
]

Aggregation modes

The API supports three aggregation modes.

  • aggregationType = none (default): one row per resource-task pair.
  • aggregationType = resource: rows are grouped by Bookable Resource ID.
  • aggregationType = task: rows are grouped by project task ID.

Each row still includes the subtotal S, and P is added by summing protected actual hours for the group when enabled.


Implementation notes

The current implementation validates the project, resolves whether the project is MPP-backed, and then either:

  • reads assignment data; or
  • synthesizes schedule data for native Altus tasks.

For MPP data, the daily work values are derived from GetTimephasedWork, while protected actuals are derived from GetTimephasedActualWork and the task Date10 epoch. For native Altus schedules, the synthetic path is used and no protected actuals are generated.

This means the most important practical rule is:

Actuals are available only for MPP-backed projects before the Date10 epoch date and only when the caller asks for them via includeprotectedactuals=true.