> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aioncx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Workforce staffing to draft

> The same native workforce operations through REST and MCP.

The workforce contract exposes read-only discovery, configuration evidence, staffing calculations and source-fenced draft requests. See the [OpenAPI 3.1 specification](https://docs.aioncx.com/api-reference/workforce/openapi.json). Release requires both transports together. A handler unit test does not prove external accessibility.

## Discover before configuring

`GET /api/v1/workforce/discovery` and the MCP tool `atlas_discover_workforce` call the same native read. Supply `from`, `to` and `as_of` ISO instants; the observation window is at most 31 elapsed days. `view=summary` returns exact Queue, Agent-slot and membership counts plus source states and unresolved decisions. `view=queues` or `view=people` returns at most 100 detail records using `offset` and `limit`; optional `search` filters by display name after Atlas count-checks the complete canonical population. An incomplete read returns a failure rather than a partial inventory labeled complete.

The source facts distinguish configured routing membership, opt-in, provider team, supervisor and skill evidence, observed handling, derived presence, provider service-goal assertions and approved Atlas policy. Supervisor assignments do not grant Atlas roles; skill IDs remain provider routing evidence and never grant Atlas Work Qualification. A missing skill snapshot is unknown, not an empty skill set. An exactly identified Queue may show its observed offered-work total, but that aggregate does not prove interval completeness or forecast readiness. Provider business-hours and closure references do not prove the full dated calendar definition. A newer provider hours notice marks an older adopted Atlas calendar as stale; preview and confirm the native calendar update through the existing importer. Missing activity mappings or Person links remain unavailable/unresolved. This operation creates no WFM configuration. Provider identity, semantic demand and Person detail require their separate token grants and current native authority; a workforce grant alone does not expose those sections.

```bash theme={"system"}
curl --fail-with-body "$ATLAS_URL/api/v1/workforce/discovery?from=2026-10-01T00%3A00%3A00Z&to=2026-10-25T00%3A00%3A00Z&as_of=2026-10-25T00%3A00%3A00Z&view=summary" \
  -H "Authorization: Bearer $ATLAS_TOKEN" -H "X-Workspace-Id: $WORKSPACE_ID"
```

Discover the MCP tool with `tools/list`, then invoke it on `/api/mcp` with the same token's MCP transport grant:

```json theme={"system"}
{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"atlas_discover_workforce","arguments":{"workspace_id":"<workspace UUID>","from":"2026-10-01T00:00:00Z","to":"2026-10-25T00:00:00Z","as_of":"2026-10-25T00:00:00Z","view":"summary"}}}
```

Missing `api:connect` or current schedule authority is a 403 REST error. An archived activity source is an explicit unavailable section rather than zero presence. A changing or capped inventory fails the read; retry after source stabilization. REST errors use `{error:{code,message}}`, and MCP returns the same code in a structured tool failure.

## Review and configure a workforce

Admin → Workspace setup → Workforce setup begins with this deterministic discovery. The existing Atlas native agent must read the same inventory before preparing a setup plan. It may inspect only a named gap, then submits the existing structured preview with supported changes or exact blockers. The discovery read does not create a group or publish a schedule. Existing saved setup plans remain readable under current source permissions.

Applications and external agents can perform the same setup through `POST /api/v1/workforce/setup/preview` / `atlas_preview_workforce_setup`, forecast-group publication, scheduling-group creation and dated membership creation. Each mutation rechecks current native authority and attributes its effect to the token Actor. Forecast-group publication additionally requires `mcp:schedules:publish` alongside `mcp:schedules:write`, including when invoked through saved-assessment apply; the native transaction rechecks the stored grant. No new credential, setup ledger or agent runtime is required.

Preview accepts at most 20 recommendations and 100 total proposed members, under shared compute admission. A forecast configuration always requires `horizonDays`, even when `demand` is null. Targets must match the exact work scope, reporting timezone and effective period. A scheduling recommendation may name another recommendation's `forecastGroupKey`; its returned command binds that exact forecast publication. Apply the forecast first. A queue is not required to belong to exactly one forecast group, but the same work units are never forecast twice. A proposed forecast whose scope equals an active Forecast Group's is refused as `forecast_group.duplicate_scope: <id>`, naming the group to reuse. One that is a slice of an active group's work units, or of another proposal's, is refused as `forecast_group.scope_overlap: <id or key>`: a slice with no planning distinction belongs in semantic reporting, and one with a distinction needs a partition or demand binding rather than a second additive forecast. Scopes on different filter dimensions are not claimed to overlap. A Forecast Group the forecast-group cutover created from a scheduling group's manual demand without a queue keeps the all-queue scope (`objectType` `queue`, `objectId` null), which contains every queue scope: publish a narrower revision of it, or reuse it, before setup proposes a forecast for one of its queues. Proposed demand bindings are also checked against each other as they would be saved, so two proposals that bind the same demand additively, mix interpretations or exceed it with partition minimums are both refused with the conflict and the other proposal's key.

Discovery groups queues whose provider service goal, hours, routing and active members' routing skills all match into an `equivalent_queues` question. It suggests one Forecast Group that keeps each Queue as a dimension, or reuse of a group one of them already has (`proposedValue.reuseForecastGroups`). It never merges queues, and any unknown or differing evidence, such as a language skill, keeps them apart.

Retain the returned commands, including their keys and source fingerprints. Group creation links the group and native object in one transaction. Membership creation provisions any needed native Agent slot in the same transaction as its dated membership; an assignment/FTE/overlap failure leaves neither partial effect. Existing overlapping membership is refused, not silently edited. Assignment enforce mode cannot be bypassed with a Person-only request.

When a reused Agent occupancy is employment-linked, membership must explicitly name that employment assignment, including in a legacy workspace. A Person-only request returns `workforce.member_assignment_required` (422) instead of losing the assignment context and leaving a roster that cannot commit shifts. Inspect the effective assignment and submit a new reviewed request.

`createReady` describes configuration validation. `operationalReadiness: not_verified` remains until source, qualified roster, demand and draft checks succeed. Membership eligibility is rechecked at commit. Successful configuration does not claim a forecast, executable schedule, customer activation or later service improvement.

For an assessment saved by the native agent, `POST /api/v1/workforce/setup/apply` / `atlas_apply_workforce_setup` selects only `{sessionId, resultRef, key, memberRequestKey?}`. The backend loads the exact saved command; callers cannot replace its configuration. A new correction prevents uncommitted effects from the old result. Already committed requests remain recoverable using the original selector and current authority. Each item is an independent authorized effect, so partial completion is explicit.

The requested historical window is retained as `{start, end, timezone}` and is limited to 366 elapsed days. It does not imply complete observations or change a source's grain. The setup page issues the assessment through the existing Agent Kernel with reads only, retains corrections in that conversation, and lets the operator reopen their saved work. `GET /api/v1/workforce/setup/assessments` / `atlas_list_workforce_assessments` lists at most ten of the issuing Person's own sessions executing in the selected workspace; it returns no conversation history or titles from other workspaces. External agents can independently discover sources, invoke the same native preview and apply its exact commands through the paired REST/MCP operations.

Use `GET /api/v1/workforce/configuration-results/{requestKey}?kind=group|member|forecast_group` or `atlas_inspect_workforce_configuration` after uncertainty. A saved receipt confirms the native effect. A failed read leaves the outcome unknown; a null response is no confirmed receipt and does not prove an in-flight request failed. Reuse the original key rather than create another. An unrelated same-name group is never treated as a successful retry.

### External creation and recovery example

In an authorized test workspace, retain one UUID in `REQUEST_KEY`. The connection needs explicit `api:connect`, workspace scheduling write scope and the issuer's current `manage_schedules` capability. Membership additionally requires `manage_workforce_roster`.

```bash theme={"system"}
curl "$ATLAS_URL/api/v1/workforce/scheduling-groups" \
  -H "Authorization: Bearer $ATLAS_TOKEN" -H "X-Workspace-Id: $ATLAS_WORKSPACE_ID" \
  -H "Idempotency-Key: $REQUEST_KEY" -H 'Content-Type: application/json' \
  --data '{"name":"Reviewed support roster","timezone":"America/Chicago","operating_open":"08:00","operating_close":"17:00"}'

curl "$ATLAS_URL/api/v1/workforce/configuration-results/$REQUEST_KEY?kind=group" \
  -H "Authorization: Bearer $ATLAS_TOKEN" -H "X-Workspace-Id: $ATLAS_WORKSPACE_ID"
```

Using the same connection and UUID, an MCP client discovers and invokes:

```json theme={"system"}
{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"atlas_create_schedule_group","arguments":{"workspace_id":"<workspace UUID>","request_key":"<the same REQUEST_KEY UUID>","name":"Reviewed support roster","timezone":"America/Chicago","operating_open":"08:00","operating_close":"17:00"}}}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"atlas_inspect_workforce_configuration","arguments":{"workspace_id":"<workspace UUID>","request_key":"<the same REQUEST_KEY UUID>","kind":"group"}}}
```

Send these to `/api/mcp` with the bearer credential, `Accept: application/json, text/event-stream` and the negotiated MCP protocol version. A changed body under the same key returns `workforce.operation_conflict`. A superseded review returns `workforce.assessment_superseded`. Missing transport or current native grants return 403; unavailable commit/read acknowledgement returns an unknown outcome (503). No browser cookies are used. Older MCP group/member argument forms remain accepted, but callers must supply or retain the returned request key for safe recovery.

## Credentials

In Settings → MCP connections, create an integration with explicit REST access. REST requires `api:connect` on the existing `atlas_mcp_` connection. Existing keys gain no implicit REST permission. Scheduling reads require workforce workspace grants and current `manage_schedules` authority; effects also require the workforce write grant. Provider reads retain separate provider grants and current provider setup authority.

Explicit integrations default to 90 days. A narrowly scoped single-workspace planning connection can explicitly expire only when revoked. Ordinary MCP defaults remain seven days. Rotation shows the new secret once and atomically revokes its predecessor. Both transports recheck current issuer authority and attribute effects to the token Actor. Browser cookies, caller identity fields, and `atlas_ingest_` scheduling credentials are refused. OAuth is not implemented by this contract.

Renewable integrations can explicitly include `mcp:schedules:publish` for reviewed forecast-group publication and `mcp:semantic:read` with its workspace selector for setup evidence. These grants are not implicit, and current native authority is checked on creation, rotation and invocation. Until-revoked planning keys exclude both grants; use a renewable connection when setup needs them.

Complete or reconcile pending issued draft requests before rotating a connection. The successor has its own token Actor and cannot inspect or commit requests issued by the revoked predecessor; existing source receipts remain available to authorized native users.

## Configured planning

Availability, preferences, daily and weekly patterns, rotations, activity obligations and demand bindings are native configuration on their existing owners. The signed-in product, `/api/v1/workforce` and MCP call the same typed operations; transports differ only in how identity is established.

| Capability | REST (`/api/v1/workforce`) | MCP |
| - | - | - |
| My published schedule and activities | `GET /me/schedule` | `atlas_get_my_schedule` |
| My profile and history | `GET /me/scheduling-profile` | `atlas_get_my_scheduling_profile` |
| Preview and submit my change | `POST /me/scheduling-profile/preview`, `POST /me/scheduling-profile/changes` | `atlas_preview_my_scheduling_change`, `atlas_submit_my_scheduling_change` |
| My change status and withdrawal | `GET /me/scheduling-profile/changes`, `POST /me/scheduling-profile/changes/{changeId}/withdraw` | `atlas_list_my_scheduling_changes`, `atlas_withdraw_my_scheduling_change` |
| Manager profiles and revisions | `GET /scheduling-profiles`, `GET /scheduling-profiles/{subjectKind}/{subjectId}`, `POST …/preview`, `POST …/revisions` | `atlas_list_scheduling_profiles`, `atlas_get_scheduling_profile`, `atlas_preview_scheduling_profile`, `atlas_save_scheduling_profile` |
| Employee change review | `GET /scheduling-profile-changes`, `POST /scheduling-profile-changes/{changeId}/resolve` | `atlas_list_scheduling_profile_changes`, `atlas_resolve_scheduling_profile_change` |
| Daily, weekly and rotation patterns | `GET /scheduling-patterns`, `GET /scheduling-patterns/{patternId}`, `POST /scheduling-patterns`, `PUT /scheduling-patterns/{patternId}`, `POST /scheduling-patterns/preview`, `POST /scheduling-patterns/{patternId}/preview`, `POST /scheduling-patterns/{patternId}/archive` | `atlas_list_scheduling_patterns`, `atlas_get_scheduling_pattern`, `atlas_save_scheduling_pattern`, `atlas_preview_scheduling_pattern`, `atlas_archive_scheduling_pattern` |
| Effective pattern assignments | `GET /rotation-assignments`, `POST /rotation-assignments`, `POST /rotation-assignments/{assignmentId}/end` | `atlas_list_rotation_assignments`, `atlas_assign_rotation`, `atlas_end_rotation_assignment` |
| Demand bindings | `GET /scheduling-groups/{groupId}/demand-bindings`, `POST …/demand-bindings`, `POST …/demand-bindings/preview`, `POST …/demand-bindings/{bindingId}/end` | `atlas_list_workforce_demand_bindings`, `atlas_save_workforce_demand_binding`, `atlas_preview_workforce_demand_binding`, `atlas_end_workforce_demand_binding` |
| Shared requirements and draft | The existing requirement and draft operations; a draft `target` may name `participant_group_ids` | The existing staffing and draft tools |

Every write takes a retained UUID: the REST `Idempotency-Key`, or `request_key` through MCP and the signed-in product. An unchanged retry returns the recorded result and a changed body under the same key conflicts. Archive, assignment and assignment end retain an exact-body native receipt. Inspect `/configuration-operations/{requestKey}` (`atlas_inspect_configuration_operation`) before retrying an uncertain result; `committed` returns the original result, while `not_found` permits only a retry with the retained key and body. Withdrawal and resolution replay from their recorded state. Profile changes and pattern saves retain their existing revision/change keys.

### Self-service and manager authority

In Settings → MCP connections, a custom connection can include **My schedule**: `mcp:self:read` with the `mcp:self:workspace:<workspace>` selector, and optionally `mcp:self:write`. Each request rechecks the issuer's current `view_own_schedule`, and writes their current `manage_own_scheduling_preferences`. The Person always comes from the connection or session; caller identity fields are refused. Self operations change only availability and scheduling preferences, never contract, qualification or manager terms, and they never list another Person's profile. They do not use `manage_schedules`.

Manager operations require the workforce workspace grant (and the write grant for effects) intersected with current authority: `manage_scheduling_profiles` for the exact Person or Assignment, `manage_rotations` for patterns and assignments, and `manage_schedules` for the Scheduling Group of a binding. Reads never provision an Actor or mutate configuration.

A manager's Person profile read also returns `assignments`: minimal workspace-visible, noncancelled Employment Assignment IDs, including assignments with no profile or only a future-dated profile. These choices come from the employment owners rather than effective profile rows. Unavailable or incomplete assignment evidence fails the read; it is never reported as an empty list. Assignment-scoped term changes omit the Person-only `employment_type` field entirely.

An employee change becomes an effective revision unless the availability it submits conflicts with published commitments, falls below a contract minimum or has impact that could not be assessed; those stay `pending_review`. A preference-only change always applies directly, whatever the unchanged availability shows. Pending changes never affect planning.

A change takes effect today unless it names `validFrom`. A second change on the same day replaces the first, which stays in the revision history with an empty range (`validTo` the day before `validFrom`) and never governs a date. Preferences can also take effect ahead of a revision scheduled for a later date: that revision keeps its own terms and takes the new preferences unless it schedules its own. Other terms start on or after the latest revision's start date; an earlier date is refused with the `scheduling-profile.invalid_supersession_date` issue (422). Each save still names the revision it was based on (`scheduling-profile.stale_revision`, 409), and an unchanged retry returns the recorded revision.

Availability-only preview and save operations also accept an inclusive `valid_to` alongside `valid_from`. This temporary range changes only availability. The native profile writer retains existing date boundaries and other terms, including future scheduled employment or preference changes, and restores each original date's availability after the end. Same-day replacements remain immutable history. The preview returns `validTo`; employee changes retain this range through pending review and approval. Published work stays unchanged.

A pending change is reviewed through the existing approval owner, as for time off and shift swaps. It is a `scheduling_profile_change` object record that the workspace's published decision policies evaluate when it is submitted. Workspace provisioning seeds an editable "Scheduling change review" starter that routes every such change to `wfm_manager_role`. Admins can branch on the `published_commitment_conflict`, `below_contract_minimum` and `impact_incomplete` facts. The routing creates an approval request and its Inbox item. The submission returns `routing`:

* `routed`: an approval request and Inbox item exist.
* `not_configured`: no published policy routes the change.
* `routing_pending`: evaluation was incomplete. An unchanged retry routes it again.

Every change read lists its `reviews` with their approval request IDs and status. A routed change resolves only through its review. The approvals rail, or the paired resolve operation, approves or rejects it in the same transaction as the review. That transaction requires the routed reviewer role and current `manage_scheduling_profiles` authority over the change's subject. It rechecks the revision the change was based on (`scheduling-profile.stale_revision`, 409), closes the change's other pending reviews and closes the Inbox item. The employee withdraws a routed change by cancelling its review. The native resolve and withdraw commands refuse a routed change with `scheduling-profile.review_in_progress` (409). Only a change that no policy routed is resolved directly by a manager with current authority. Employees cannot review their own changes, and no authority grant resolves a change without a person.

An approval or rejection of a reviewed change, whether through its review or the direct resolve, reaches the employee as an Inbox item ("Your scheduling change was approved" or "declined") with Inbox's usual notification delivery. A withdrawal sends no notice. The employee reads the outcome through `GET /me/scheduling-profile/changes` or `atlas_list_my_scheduling_changes`: `status`, `resolvedAt`, `resolutionNote` and each review's status. A notice that could not be handed over is retried by the approval delivery sweep, never by repeating the decision.

The private note on a change is visible to its author and to reviewing managers only. Roster, approval, Inbox, assistant and list projections omit it, and so does the decision notice.

### Availability and preferences

Availability is explicit: `unknown`, `unrestricted` or `restricted`. A restricted declaration has weekly rules (`closed`, or several non-overlapping `windows` per day), dated `exceptions` and the IANA `zone` its wall times were declared in. Profile validity and exception dates are civil dates; window times are wall times in the declared zone; resolved intervals are UTC instants and survive DST. Person-wide restrictions apply to every Assignment; an Assignment profile can only narrow them, and a contradiction is returned with both sources. The other profile terms resolve the same way, field by field: a Person-wide weekly limit caps all of the Person's work and an Assignment limit caps that Assignment's work, shift-length bounds from both scopes intersect, and an Assignment that leaves a field empty inherits the Person-wide restriction instead of discarding it. Bounds that leave no shift length are a `profile.shift_bounds_contradict` contradiction. Only effective revisions take part; a pending employee proposal never does. A dated exception changes only its date and scope; it never removes leave, contracts, qualifications or other commitments. Observed login time is never a declaration.

A missing profile, an `unknown` declaration or an omitted weekday is unassessed. Autonomous generation and rotation exclude unassessed time and report it; they never assume all-day availability. When assessed time on a date admits none of a Person's permitted shift starts and lengths, generation reports `availability.no_permitted_shift` for that date instead of leaving a silent gap. Manually edited, copied, rotated and published schedules meet the same hard constraints as generation: a shift in time an explicit declaration excludes is blocked (`availability.outside_declared_availability`), as is a shift length outside the resolved profile bounds, a required activity that is missing, shares its only placement with another required activity, or breaks its order, separation or contiguous-work limit, and a weekly pattern maximum the schedule breaks in a week it works (counting published commitments). Unassessed time keeps its historical manual meaning; only autonomous planning excludes it. Legacy one-window profiles stay readable; once a structured declaration exists, a legacy availability edit is refused rather than silently ignored.

The own-profile read includes `weeklyPatternChoices`: named weekly pattern IDs and revisions with `eligible` and native validation `findings`, plus a `complete` flag. This self-scoped projection needs no `manage_rotations` grant and exposes no other people's assignments or usage. An eligible configuration remains subject to native person/date checks when scheduling.

The published own-schedule read includes `time_accounting` for each shift. It separates `elapsed_minutes`, `paid_minutes`, `contract_minutes` and `productive_minutes`, with `contract_basis` and the immutable `pattern_revision` at issue time. Default work intervals use the existing native accounting owner; missing flags, contradictory activities or unavailable pattern evidence return `status: unavailable`, never guessed totals.

Preferences (`startWindows`, `finishWindows`, `shiftLengths`, `workingDays`, ranked `daysOff`, ranked `weeklyPatterns`) are soft. Absent preferences cost nothing. Each satisfied or unmet preference is normalized by its stated strength or rank and reported with its denominator; a strong preference never overrides a hard rule. Explanations name the preference kind, not personal reasons.

A start window carries a `strength`, or a `rank` when the windows are ranked start choices ("1st choice 08:00, 2nd choice 09:00"). Each workday's start then scores one item weighted like a `moderate` preference: the n-th of that day's choices in rank order satisfies 1/n, and a start outside all of them satisfies 0. Ranked and strength-weighted windows cannot apply to the same day, and choices that share a day need different ranks. A finish window (`days`, `earliest`, `latest`, `strength`) is read at the shift's local finish in the preference zone, so it holds across a DST change; its `days` select the workday the shift starts on, and a finish at midnight meets a window that ends at `24:00`. Unmet finish windows are reported as `preference.finish_window_unmet`.

### Patterns, obligations and accounting

A `daily` pattern bounds starts (`earliest`, `latest`, `incrementMinutes`; a fixed start sets them equal), durations (a `range` with an increment or explicit `options`), its zone basis, applicability, eligible work, an optional declared `split`, and required `activities`. A `weekly` pattern lists eligible daily patterns per day with workday, hour (with an explicit basis), rest, consecutive-day and consistent-start bounds; weeks start on Monday in the planning zone. A `rotation` orders weekly patterns from the assignment's anchor week. Four ten-hour days and five eight-hour days can be assigned to different people in one roster. A personal assignment narrows the alternatives without removing mandatory rules, and adjacent published commitments count toward rest, weekly hours and consecutive days.

An activity obligation has a type, shift-length trigger, duration, a shift-relative or wall-clock window, precedence and ordering, minimum separation, contiguous-work limit, recurrence, eligible qualifications and movement (`movable`, `deferrable`, `cancelable` or `protected`). Generation places required activities while it evaluates each candidate, so a permitted meal move can improve productive coverage. A required activity with no permitted position is a `activity.required_infeasible` contradiction or a search limit, never a dropped activity. Breaks, meals and other required activities come only from an authored pattern, including one applied from a reviewed setup recommendation. Atlas never chooses employer obligations itself: a historical fixed-window rotation, or a generated shift with no authored pattern, carries no breaks or meals, and the result reports `activity.policy_not_configured` for those days. Protected and generated blocks remain locked through rotation, copy and edit validation. A span edit, move or published change that would cut or drop a protected block is refused whole with `schedule.protected_activity_outside_shift` (422 in the schedules workspace); a move leaves the block at its fixed time. A derived time-off projection still re-clips with its shift.

Elapsed, paid, contract-counted and productive minutes are separate. An 8.5-hour span with an unpaid 30-minute meal and two paid 15-minute breaks is 8 paid and 7.5 productive hours, and 6.75 expected productive hours after an assessed 10% residual loss applied once. Contract hours follow the pattern's `contractBasis`. Issued proposals cite the exact pattern revision. Archiving keeps history and returns current usages.

### Demand bindings and shared plans

A binding makes a Scheduling Group eligible to supply a Forecast Group's demand; it never copies the demand. Overlapping additive bindings of one Forecast Group are refused unless they declare a `partition` with share bounds, a `shared_pool`, or distinct `sequential_stage` keys. The group's `default_forecast_group_id` remains the zero-or-one adapter while no explicit binding exists. One requirement row per Forecast Group scope is owned by one supplying group. Each sequential stage is its own scope with its own requirement and suppliers, and carries `stage_rollup_counts_each_stage`: a total across stages counts an interaction once for every stage it passes through. A binding's `qualification_ids` restrict who supplies its demand to people qualified at that time; a Person whose groups supply only qualification-gated demand they cannot work on a date is excluded with `work_qualification.not_qualified_for_demand`.

Partition shares add up only on the dates they share: totals are checked on each segment between effective dates, so a dated replacement never counts alongside the binding it replaces. Minimum shares above the whole demand are refused (`demand_binding.partition_exceeds_demand`); maximum shares that stop short of it are a warning (`demand_binding.partition_incomplete`), because a split is bound one supplier at a time. In planning, a fixed share (minimum equals maximum) is that group's own obligation: only its people supply it, and it can be planned alone. Flexible bounds keep the whole obligation, so every supplier must plan together: each supplier's minimum is its own and the rest is shared. Generation refuses a flexible partition rather than approximating it when a supplier is missing from the plan (`generation.partition_requires_participants`), when a supplier could pass its maximum by taking all of the shared rest (`generation.partition_bounds_unsupported`), or when the bounds change within the period (`generation.partition_change_unsupported`).

Issue a shared draft with `target: {participant_group_ids, period_start, period_end}` (2–10 groups). The 14-date and 200-roster limits apply to the complete scope. One physical timetable allocates each Person's time once across groups and scopes; channel concurrency changes throughput, not human time. Commit writes one draft Schedule per participating group atomically, rechecks every participating source revision, and refuses a shift that overlaps the same Person's work in any other schedule. Single-group plans treat that work, including other drafts, as occupied time, so two concurrent drafts cannot double-book a Person.

### Objectives and results

Hard rules are enforced first. The declared objective then minimizes coverage shortfall by scope and interval, then preference penalty, then maximizes placed minutes and balances workload. Ties prefer the earlier start, the shorter length, fewer weekly minutes, then the Person id. Cost is unavailable unless authorized compatible cost inputs exist. A proposal returns its objective, score components, `contradictions` (proved input conflicts), `searchLimits`, exclusions with reasons, `policyGaps` and coverage shortfall. A search limit says where the bounded search stopped without deciding, never that an alternative is infeasible: `planning.activity_search_limit` when activity placement ran out of evaluations, `planning.shape_lengths_sampled` when a profile allows more hourly shift lengths than the search evaluates (the message names the sampled step), and `planning.improvement_search_limit` when local improvement reached its move or pass bound while moves were still improving the plan. Supply is evaluated between consecutive event boundaries (shift and activity edges, commitments, absences, service windows, memberships and qualifications), so time inside a forecast interval when nobody works is a shortfall and is never averaged away. Coverage is reported at the forecast's own grain: each interval carries its original requirement, its average `supplied`, its lowest `minimumSupplied`, the integrated `shortfall` and `inProtectedActivities`, the average number of the proposal's suppliers held in protected meetings or accommodations, which explains the shortfall protection leaves. A constraint-valid draft with unmet demand stays useful and shows its shortfall; hard violations cannot be committed. A weekly pattern maximum is a hard conflict in any week the proposal adds work to, even when earlier commitments already reached it; a week it leaves untouched is evidence. Generation and rotation proposals return the same finding lists, and the schedules review dialog and the assistant's draft tool show them before anything is applied: hard conflicts, which keep the proposal from being applied, contradictions, search limits, exclusions, weekly pattern evidence and policy gaps.

```bash theme={"system"}
curl --fail-with-body "$ATLAS_URL/api/v1/workforce/me/scheduling-profile/changes" \
  -H "Authorization: Bearer $ATLAS_TOKEN" -H "X-Workspace-Id: $WORKSPACE_ID" \
  -H "Idempotency-Key: $CHANGE_KEY" -H 'Content-Type: application/json' \
  --data '{"terms":{"scheduling_preferences":{"version":1,"daysOff":[{"day":"sat","rank":1}]}}}'
```

```json theme={"system"}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"atlas_submit_my_scheduling_change","arguments":{"workspace_id":"WORKSPACE_UUID","request_key":"CHANGE_KEY_UUID","terms":{"scheduling_preferences":{"version":1,"daysOff":[{"day":"sat","rank":1}]}}}}}
```

## Source and calculation contract

1. Read `/api/v1/workforce/discovery` / `atlas_discover_workforce`, then `/readiness` if configuration-only evidence is needed. Exact counts distinguish empty configuration from failed reads. `queue_settings` now pages all active queues and preserves provider service-goal units and dates. Missing, conflicting, sensitive or unavailable values are not defaults. These settings are configuration evidence, not observed performance or proof of current provider delivery. Provider discovery and sync receipts retain their separate grants.
2. Inspect `/connections`, inventory, sync status and calendar preparation. These reuse existing provider operations. Retained inventory alone does not prove current delivery; missing receipts remain unknown.
3. Inspect `/scheduling-groups` and `/scheduling-groups/{groupId}`. Confirm the existing dated roster, calendar and demand configuration. This API does not guess assignments or silently configure customer workspaces.
4. Read or compute requirements, then issue, inspect and commit a native draft request.

Staffing compute uses authored manual demand or completed forecast evidence. It does not run potentially long inference inside the WFM request. Missing evidence returns `prediction_not_ready` with a next action; obtain completed evidence through the existing forecast workflow before retrying.

`residual_unplanned_shrinkage` is an explicit fraction from zero up to, but excluding, one. It is the loss remaining after scheduled nonproductive activities. Zero explicitly assumes no additional unplanned loss; it is not inferred from total shrinkage.

New calculations retain exact net demand beside existing gross requirements. Productive coverage compares productive supply adjusted by residual loss with exact net demand over UTC event boundaries. Historical gross rows remain readable, but absent net evidence or an unassessed residual is unavailable for productive coverage decisions. Native callers that omit residual retain exact net without silently choosing zero; a recomputation with an explicitly assessed residual is required for coverage-backed leave approval. Missing-only evidence is reported as `state: "unknown"`, `assessment: "not_assessed"`, with null demand facts. Automatic approval remains blocked. An authorized human manager may use the existing native manual override with a reason; the receipt retains that coverage was not assessed. Invalid or stale evidence and unavailable qualification remain blocked. Rounded gross counts cannot reconstruct net demand. JS and PostgreSQL normalize derived gaps within `1e-9` people of zero to zero, keeping source evidence unrounded. Configured planning (`configured-plan-1`, `shared-plan-1`) scores qualified productive time, after scheduled activities and the assessed residual, against exact net demand and places required activities inside candidate evaluation; see [Configured planning](#configured-planning). It remains a bounded deterministic search: it reports proved input contradictions separately from search limits and never claims global infeasibility. Requests issued under an earlier calculation remain inspectable and their committed results replay. An uncommitted one is refused as `generation.source_changed` and must be refreshed, so every commit runs the configured-plan checks.

## REST example

Read the native Intraday snapshot with `GET /api/v1/workforce/scheduling-groups/{groupId}/intraday?date=YYYY-MM-DD` or MCP `atlas_get_workforce_intraday` with `workspace_id`, `group_id` and optional `date`. The date defaults to the scheduling group's local today. Both use the same `IntradayService` and snake-case response as the native board, including published schedule references, canonical activity, adherence, interval observation metadata and forecast/actual comparisons. The existing workforce workspace grant and current transport authority apply; the issuer must additionally have `view_workforce_actual_activity`. Raw provider status and substatus require `view_workforce_activity_raw`.

`intervals[].observation` separates elapsed and remaining interval time, source observation and stabilization cutoffs, and provisional/final person-time status. `forecast_actuals` retains the forecast's original interval grain and timezone. Its full-interval forecast and actual so far are separate values; the live read never writes or finalizes prediction actuals. Unknown source completeness remains explicit. Use `history=deferred` in REST or `include_history=false` in MCP for the native deferred-history mode. Existing refresh requests see newly ingested facts without initiating a provider collection.

Use actual authorized identifiers and retain UUID operation keys before sending. Environment variables below keep credentials out of command literals.

```bash theme={"system"}
curl --fail-with-body "$ATLAS_URL/api/v1/workforce/readiness" \
  -H "Authorization: Bearer $ATLAS_TOKEN" \
  -H "X-Workspace-Id: $WORKSPACE_ID"
```

Compute staffing with `POST /api/v1/workforce/scheduling-groups/{groupId}/requirements/compute`, the same bearer/workspace headers, `Content-Type: application/json`, and `Idempotency-Key: <compute UUID>`:

```json theme={"system"}
{"from":"2026-11-02","to":"2026-11-03","requested_at":"2026-10-22T23:00:00Z","residual_unplanned_shrinkage":0}
```

Use the retained current request timestamp. This persists latest staffing evidence; it is not an exactly-once staffing receipt or a schedule commitment. After uncertainty, inspect saved evidence before recomputing. Read-only `POST /requirements/evaluate` takes the date range and residual fraction but neither starts inference nor persists requirements.

Issue with `POST /api/v1/workforce/draft-requests`, a new retained `Idempotency-Key`, and:

```json theme={"system"}
{"target":{"group_id":"22222222-2222-4222-8222-222222222222","period_start":"2026-11-02","period_end":"2026-11-03"}}
```

Issue saves native proposal/source evidence and creates no shifts. Issue, inspect and commit responses reduce each scheduling profile in that evidence to its identity, revision, effective dates, weekly and shift hour limits and availability; free-text notes and change reasons stay in Atlas. Inspect `GET /draft-requests/{requestKey}` before `POST /draft-requests/{requestKey}/commit` with the same `Idempotency-Key` and no body. Commit rechecks current authority and source revisions, then creates a draft; publication remains separate. Identical retries through either transport return the native receipt. After a stale-source conflict, obtain and review a new proposal.

The setup page continues from verified scheduling configuration into these same operations. A scheduling recommendation can retain `review.planning` with `{from,to,timezone,residualUnplannedShrinkage,basis,evidenceRefs}`. It requires 1–14 inclusive dates, the scheduling group's timezone, a residual fraction in `[0,1)`, an explanation and retained source references. The native agent validates its cited references. Both external setup preview and assessment reads use the same additive contract. Proposed inputs are analysis, not a new source authority or permission to commit.

The draft form uses those proposed inputs without asking the operator to reconstruct them. Missing planning evidence blocks the initial calculation and links back to Atlas; an operator may explicitly enter their own inputs. Calculate staffing, review the proposed shifts, then commit. The page retains operation keys across reloads in the current browser tab and rereads the current draft against the receipt and reviewed shifts. A lost response remains recoverable using the original key. An empty proposal is not a completed first draft. Unconfigured demand returns `generation.demand_unavailable`; this operation does not substitute minimum-floor staffing. Signed-in operators and outside clients share the workspace compute limit, while retaining distinct human and token Actors.

## MCP example

MCP requires `mcp:connect` and native domain grants independently of REST. Send JSON to `/api/mcp`:

```bash theme={"system"}
curl --fail-with-body "$ATLAS_URL/api/mcp" \
  -H "Authorization: Bearer $ATLAS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-06-18' \
  --data '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```

```json theme={"system"}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"atlas_inspect_staffing_requirements","arguments":{"workspace_id":"11111111-1111-4111-8111-111111111111","group_id":"22222222-2222-4222-8222-222222222222","from":"2026-11-02","to":"2026-11-03"}}}
```

Use actual granted IDs. Draft tools are `atlas_issue_draft_schedule`, `atlas_inspect_draft_schedule`, and `atlas_commit_draft_schedule`. Their `request_key` equals the REST `Idempotency-Key`. Tools must be visible under real current grants; registration alone is insufficient.

## Limits, failures and verification

Requests are bounded to 64 KiB, 14 inclusive dates and 200 roster entries for generation. Heavy calls share one active operation per workspace and token, a 30-second cooperative budget, 10-second database requests and a 45-second deadline across database pages. Admission leases expire after 120 seconds if their completion acknowledgement is lost. They are operational telemetry, not effect receipts; read-only calculations do not write schedule or requirement records.

REST errors use `{ "error": { "code": "generation.source_changed", "message": "generation source changed" } }`. Newly exposed MCP operations return the same actionable structured code. Cases include expired/invalid bearer (401), missing REST or current native grant (403), source/revision conflict (409), invalid input (422), compute concurrency (429), and unavailable evidence (503). A timeout at commit is an unknown outcome: inspect and reuse the same native draft key.

Run `scripts/workforce/smoke-external.mjs` against an authorized deployment to exercise real HTTP REST and MCP without cookies or service-role credentials. Full source-to-draft and live customer activation require separately retained acceptance evidence. Confirmed command success does not establish later service improvement.

## Review, preflight and verification

`GET /api/v1/workforce/setup/assessment` and `atlas_read_workforce_assessment`
read an owned assessment, its retained revisions and currently authorized conversation.
`sessionId` is required; `resultRef` selects an earlier revision. `after` is an optional
history cursor. The original `{start,end,timezone}` remains unchanged during corrections.
The current source permission checks also apply to older derived analysis. External
provider, prediction and semantic evidence additionally requires the connection’s
corresponding domain grant; workforce read never inherits those grants from its owner.
`assessmentAvailability` explicitly distinguishes available, pending, wrong-workspace
and unavailable evidence, with an actionable reason for unavailable saved analysis.
History reads admit at most 200 distinct source checks with a 20-second cooperative
budget; a capped read returns `workforce.budget_exceeded`, never an empty success. No caller
user ID, browser session or substituted workspace identity is accepted.

Review details include cited queue/Person tables, assumptions, reused inputs, decisions,
and separate staffing/evidence issues. They are analysis, not an eligibility grant.
Native preview computes `createReady`; `operationalReadiness: "not_verified"` remains
separate. An unavailable historical count is not an observed zero.

`POST /api/v1/workforce/setup/check` and `atlas_check_workforce_setup` accept the same
saved selector plus `keys` and `mode`. `preflight` rechecks current authority, source
fingerprints and selected native inputs; it requires a current completed assessment
and the effect's write/publication grants. `verify` is read-only and does not provision
an Actor. It compares the original request with the current saved group/publication
or membership. Native commit repeats source, revision, assignment-span, overlap and
FTE validation; preflight does not reserve capacity or promise a later commit.

```bash theme={"system"}
curl --fail-with-body --get "$ATLAS_URL/api/v1/workforce/setup/assessment" \
  -H "Authorization: Bearer $ATLAS_TOKEN" \
  -H "X-Workspace-Id: $WORKSPACE_ID" \
  --data-urlencode "sessionId=$SESSION_ID"

# setup-check.json contains the exact retained sessionId/resultRef and selected keys:
# {"sessionId":"...","resultRef":"...","keys":["forecast","roster"],"mode":"preflight"}
curl --fail-with-body "$ATLAS_URL/api/v1/workforce/setup/check" \
  -H "Authorization: Bearer $ATLAS_TOKEN" \
  -H "X-Workspace-Id: $WORKSPACE_ID" \
  -H "Content-Type: application/json" --data-binary @setup-check.json
```

MCP discovery and invocation use the same external credential after initialization:

```json theme={"system"}
{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"atlas_read_workforce_assessment","arguments":{"workspace_id":"WORKSPACE_UUID","sessionId":"SESSION_UUID"}}}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"atlas_check_workforce_setup","arguments":{"workspace_id":"WORKSPACE_UUID","sessionId":"SESSION_UUID","resultRef":"RETAINED_RESULT_REF","keys":["roster"],"mode":"verify"}}}
```

Apply dependencies in order: forecast publication, linked scheduling group, then dated
memberships. Each item is one native transaction. Stop dependent effects until the
parent is `verified`; separate successful effects remain committed after a later failure.
If a response is lost, verify the original selector before retrying the same command.
`not_confirmed` means no effect was found for that issuer/key; `unknown` means the
read could not establish an outcome; `changed` means the saved current record differs
from the review. Neither is an applied-and-verified success. Human and token Actors
retain their existing distinct command ownership; REST/MCP replay uses the same token.

A superseded preflight returns 409 `workforce.assessment_superseded`; invalid or duplicate
selection keys return 422; revoked domain authority returns 403; admission contention
returns 429 `workforce.compute_busy`. A per-item `blocked` source check explains why
no apply should start. Structured MCP failures retain the same actionable code.
A verified configuration is not a demand-backed draft, provider activation or evidence
of improved service. Those subsequent operations retain their own native checks.

## Setup progress and run deletion

Use `view=summary` with the existing assessment read operation to poll progress.
It returns four plain-language sections and the run's state. While a run is active,
it does not hydrate the conversation or repeat source queries. Once complete, it
loads the selected plan with the same current source permission checks. `checked`
means a request returned; the plan establishes which evidence is usable. Full
source-authorized history remains available with `view=history` (the default).

The assessment list includes `needsAttention` metadata. A run can complete with
blockers or an inspection-limit failure; these display as “Needs attention”.
Completion and this summary flag do not establish scheduling readiness. Open the
result for its currently authorized findings.

```bash theme={"system"}
curl --fail-with-body --get "$ATLAS_URL/api/v1/workforce/setup/assessment" \
  -H "Authorization: Bearer $ATLAS_TOKEN" \
  -H "X-Workspace-Id: $WORKSPACE_ID" \
  --data-urlencode "sessionId=$SESSION_ID" --data-urlencode 'view=summary'

curl --fail-with-body -X DELETE \
  "$ATLAS_URL/api/v1/workforce/setup/assessment?sessionId=$SESSION_ID" \
  -H "Authorization: Bearer $ATLAS_TOKEN" \
  -H "X-Workspace-Id: $WORKSPACE_ID"
```

The same read and delete are discoverable through MCP:

```json theme={"system"}
{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"atlas_read_workforce_assessment","arguments":{"workspace_id":"WORKSPACE_UUID","sessionId":"SESSION_UUID","view":"summary"}}}
{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"atlas_delete_workforce_assessment","arguments":{"workspace_id":"WORKSPACE_UUID","sessionId":"SESSION_UUID"}}}
```

Deletion requires workforce write and the owner's current `manage_schedules`
authority. It closes only that Person's setup run and records the acting Actor.
It preserves saved groups, schedules and native command receipts. A running
lease or temporary storage outage returns `{deleted:false,cleanupPending:true}`;
the run is hidden immediately and existing native cleanup retries its removal.
Absent and foreign runs both return `{deleted:true,cleanupPending:false}`.
Missing write grants return 403; extra identity fields are invalid input.

The setup agent uses the same native kernel with a focused read/plan tool set.
It stops source discovery after twelve steps and finishes by sixteen, retaining
a native plan or an explicit implementation blocker. Invalid read arguments do
not erase subsequent corrections. Fixed tool diagnostics distinguish malformed
input (`invalid_tool_input`), an operation outside the agent's configuration
(`operation_not_configured`) and unusable evidence (`citation_unavailable`).
They contain no source contents and do not authorize access to cited results.
These bounds do not turn an unavailable
source, blocked plan or empty proposal into a completed first draft.

## Editing and explaining dated work

Schedules, scheduling groups, Person Scheduling and employee entry points remain
in the existing Atlas navigation. Reusable daily, weekly and rotation patterns
live under WFM configuration → Work patterns. Dated drafts, proposals, revisions
and publication stay under Schedules. The shared agent uses the same native
operations and current permissions as these forms.

The draft editor stages work locally. Check the complete candidate with
`POST /schedules/{scheduleId}/validate-candidate`, then issue it through
`POST /schedules/{scheduleId}/edits` with a stable `Idempotency-Key`. The body
contains the exact `base_updated_at` and complete shift/activity candidate; new
activities omit IDs. Native validation resolves identities, immutable pattern
provenance, availability, target-date obligations, qualifications and rules.
It returns source-attributed findings, `canSave`, scoped coverage,
`physical_coverage_evidence` and separate time accounting. Full candidates are
bounded to 8 MiB and 5000 shifts; compute admission and deadlines still apply.

Issue does not save the draft. Inspect with
`GET /schedules/{scheduleId}/edits/{requestKey}`; commit that exact request with
an empty-body `POST /schedules/{scheduleId}/edits/{requestKey}/commit`. Publication
is a separate operation through existing approval and notification owners.
After a lost response, inspect before retrying the same key and body. A stale
source conflict preserves local edits; refresh sources and explicitly reconcile
concurrent changes before issuing another candidate. To refresh changed calendar or approved-leave records, explicitly preview the candidate with `refresh_records: true`, review `record_changes`, then save through the same issue/inspect/commit owner. Only source-resolved changed dated records may be rebuilt; manual edits and unchanged protected activities remain. The superseded immediate
draft shift/activity endpoints return `schedule.edit_candidate_required`;
published changes continue through the existing revision owner.

`POST /schedules/{scheduleId}/explain-interval` accepts the full candidate plus
`start_ts`, `end_ts` and optional `suggest_fixes`. Demand remains at its original
grain. Shared Person capacity is allocated once; productive, protected and
contract-counted time stay distinct. Missing demand or accounting is unavailable.
Returned fixes have passed the same full-candidate validator and must be staged
and saved through the edit command. Search limits and no found improvement do
not establish infeasibility. Published and archived explanations read the saved
schedule at `base_updated_at`, ignore caller shift edits and never suggest edits.
MCP equivalents are
`atlas_validate_schedule_candidate`, `atlas_issue_schedule_edit`,
`atlas_inspect_schedule_edit`, `atlas_commit_schedule_edit` and
`atlas_explain_schedule_interval`.

## Target-date copying and group activities

`GET`/`POST /scheduling-groups/{groupId}/calendar` extend the existing group
Calendar with versioned meeting and coaching series, effective ranges,
recurrence exceptions and protected target people or assignments. Save with
`expected_revision`, a reason and a stable request key. An optional
`request_key` on the read inspects its original receipt. Matching tools are
`atlas_get_group_activity_calendar` and `atlas_save_group_activity_calendar`.

`POST /schedules/{scheduleId}/copy` carries editable work and immutable pattern
provenance into a new draft. Daily shape and obligation validation use the same
pinned revision as the copied activities, even after the reusable pattern changes.
It resolves approved leave, meetings and coaching
from records for the target dates, including recurrence exceptions, rather than
retiming source-date obligations. Native validation and source fences apply
before atomic save. Retain `Idempotency-Key` and the exact options; inspect with
`GET /schedules/{scheduleId}/copy?operation_key=...` or
`atlas_inspect_schedule_copy` before retrying an uncertain copy.
`atlas_copy_schedule_to_week` and batch copy use the same native owner.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.