Paid REST API for schedule submit, staff, defaults, and related reads. Use an API key from API management. Billing follows your subscription (not per-call metering). Use the sidebar to jump to endpoints and fields.
What this documentation covers
This page documents the product REST API for external integrations.
Schedule-result completion webhooks are outbound HTTPS notifications configured in API management (not REST CRUD). See Completion webhooks on this page for payload and signature details.
In-app browser routes (for example payment callbacks or analytics paths) are not a substitute for this product API. For external systems, use /v1/* on this page and completion webhooks.
Subscription billing & access
This HTTP API is part of the paid product: access requires an active commercial agreement and keys are issued to your organization after onboarding.
Billing follows your subscription and contract. For paid plans, commercial charges are typically per registered staff seat per month (see the pricing page).
Playground / free-tier limits do not apply to paid API keys. Effective limits such as schedule span, staff count, and whether 15-minute granularity is enabled are determined by your paid entitlements. See Limits & defaults on this page.
Authentication
Requests must include a valid API key issued under your paid subscription. Create, rotate, and revoke keys from the account area after signing in.
Send the key in the x-api-key header. Keys are tied to your organization for tenant resolution and plan-based access limits. Operations outside the key scopes return 403 (API_KEY_SCOPE_DENIED). Legacy keys without a scopes attribute still allow all operations.
Paste the block below into a chat assistant when you want help calling this product API without configuring MCP.
It summarizes base URL, authentication, main operations, and write-safety notes. It never includes a real API key—keep secrets in your environment only.
For live API calls from an agent, configure MCP (agents) instead of relying on this paste alone.
Context to paste
Auto Scheduler — Public API context for LLM assistants
Use this when helping a developer call the Auto Scheduler paid product REST API (/v1/*). Prefer configuring Tools MCP when available; this paste is for chat without MCP.
Base URL
- https://api.autoschedulers.com
- Paths are under /v1/*. Do not confuse this with the marketing website origin.
Authentication
- Every request needs header: x-api-key: <API_KEY>
- Never paste a real API key into chat logs or this prompt; ask the user to supply it via environment or a secret store.
- Keys are created in the product under Account → API management.
- JSON writes: Content-Type: application/json
Read endpoints
- GET /v1/schedule-results?limit=10
- GET /v1/schedule-results/{conditionId}
- GET /v1/schedule-conditions
- GET /v1/staff
- GET /v1/staff/{staffId}
- GET /v1/schedule-defaults
- GET /v1/schedule-results/{conditionId}/emergency-shift-context
Write endpoints (confirm with the user before calling)
- POST /v1/schedule — starts a job; may incur charges; max 14 calendar days
- PATCH /v1/schedule-defaults — tenant-wide
- POST /v1/staff
- PATCH /v1/staff/{staffId}
- PUT /v1/staff/{staffId}/weekly-shift-wish — full replace
- POST /v1/schedule-results/{conditionId}/emergency-shift — paid
- POST /v1/schedule-results/{conditionId}/confirm-assignments
- DELETE /v1/schedule-conditions/{conditionId}
- PUT /v1/schedule-actual/{conditionId} — full replace
Minimal curl (read)
curl -sS -H "x-api-key: YOUR_API_KEY" "https://api.autoschedulers.com/v1/staff"
curl -sS -H "x-api-key: YOUR_API_KEY" "https://api.autoschedulers.com/v1/schedule-results?limit=10"
Contract notes
- Schedule submit body needs a supported apiVersion and valid JSON fields — see api-docs Field reference or Docs MCP Resources; do not invent fields.
- planningHorizon on defaults is only week or two_weeks (month_attendance is rejected).
- Outbound completion webhooks are HMAC-signed HTTPS callbacks configured in API management (not REST CRUD).
Safety
- Write endpoints change tenant data. Confirm destructive or irreversible actions with the user before calling them.
- Prefer read/list first when exploring an unfamiliar tenant.
Endpoint
Submit a schedule condition as JSON. This is the paid integration surface; the exact base URL is provided per environment after your API access is provisioned.
Base URL
Host for the public REST API (/v1/*), not the browser site URL. Set MCP env AUTO_SCHEDULER_PUBLIC_API_BASE_URL and curl BASE_URL to this value.
AUTO_SCHEDULER_PUBLIC_API_BASE_URL — Base URL configured for this documentation environment. Use the same value in MCP and curl.
Append paths after the base URL (e.g. {baseUrl}/v1/schedule). A trailing slash is optional.
How to call the API
Replace BASE_URL with the API base URL (path prefix) provided when your access was provisioned.
Send your API key in the **x-api-key** header. Your organization (tenant) is identified from the key—you do not pass a separate tenant id in the query string.
Paid features (emergency shift, labor details, some schedule defaults) require an active paid subscription. You may receive **503** (e.g. code **STRIPE_NOT_CONFIGURED**) when billing is not configured for your tenant. Company profile, audit logs, and user (account) management are handled in the web app and are not part of the public API.
curl example (bash / macOS / Linux / WSL)
Windows PowerShell: a trailing \ is **not** a line continuation. Use one line, or end each line with a backtick (`) to continue.
You can call the same product REST API from agents such as Cursor, Claude, Gemini, and GPT through MCP. The operations match the /v1 surface on this page.
The bundled MCP servers use local stdio. Add command / args / env to each client config file. Create API keys in API management (never commit or paste keys into public chats).
For documentation only, a Resources MCP needs no login or API key (mcp/api-docs). This page (/{locale}/api-docs) is also viewable without signing in; creating keys still requires Account → API management.
Fastest path (recommended)
Create an API key in Account → API management (never paste the real key into chat).
In the repo: cd mcp/public-api && npm install && npm run build (also mcp/api-docs if you want documentation Resources).
Copy the env snippet below and the Cursor mcp.json under Client-specific setup; replace paths and YOUR_* placeholders.
Restart the client and confirm Tools (list_staff, …) appear. Prefer read tools before any write.
Which option to use
Tools MCP (public-api)
Live /v1 calls with your API key. Use this when the agent should actually list staff, submit schedules, etc.
Docs MCP (api-docs Resources)
Contract markdown only—no API key. Use when the agent needs field rules or request-body shape.
Copy for LLM (this page)
Orientation text for chat assistants without MCP. Not enough alone for correct request bodies—pair with MCP or the sections below.
Required environment variables (Tools MCP)
AUTO_SCHEDULER_PUBLIC_API_BASE_URL
Base URL of the public REST API (/v1/*)—not the website domain. Use the API host you were given (trailing slash optional)
AUTO_SCHEDULER_API_KEY
Key from API management (do not commit or post publicly)
Variable
Description
AUTO_SCHEDULER_PUBLIC_API_BASE_URL
Base URL of the public REST API (/v1/*)—not the website domain. Use the API host you were given (trailing slash optional)
AUTO_SCHEDULER_API_KEY
Key from API management (do not commit or post publicly)
This product ships local stdio MCP only, so ChatGPT cannot connect to it directly
To use GPT-class agents: (1) run the same MCP in Cursor / Claude / Gemini CLI, or (2) call this page's REST API with x-api-key from Custom Actions / your API client
You can register a ChatGPT connector only if you host a remote HTTPS MCP yourself (not a standard product offering).
OpenAI Agents / Responses API also expect remote MCP (HTTP). The bundled servers are for stdio clients.
Tools overview (public-api)
Read and write tools are available. Writes may start billed jobs or change organization-wide settings—confirm before calling.
Read
list_schedule_results / get_schedule_result
list_schedule_conditions
list_staff / get_staff
get_schedule_defaults
get_emergency_shift_context
Write (confirm first)
submit_schedule (runs a schedule job; may incur charges)
Same base URL and API key as POST /v1/schedule. Each card shows a minimal JSON response you can use as a contract sketch.
List routes accept query limit (1–100, default 50) and cursor (opaque token from the previous nextCursor). When there is no next page, nextCursor is omitted.
GET{baseUrl}/v1/schedule-results
Lists schedule-result parent summaries (newest index order).
If status is completed and not yet confirmed, rank-1 may be auto-confirmed on this GET (side effect). Prefer POST …/confirm-assignments for explicit confirmation.
Same API key and JSON bodies as other /v1 routes. Each card shows a minimal request body you can copy. Account users, company profile, and audit logs stay in the web app—not this API. POST /v1/staff returns 409 STAFF_LIMIT when the roster would exceed the tenant cap.
Labor and Soft prefer/avoid fields require a paid subscription. Replace example UUIDs, dates, and cell keys with your tenant data.
PATCH{baseUrl}/v1/schedule-defaults
Saves tenant-wide schedule defaults. Paid-tier fields follow your subscription.
requirementsByCellKey must include every cell in period × slots (missing/extra keys → 400). Cell key = YYYY-MM-DD__HH:mm. planningHorizon: week | two_weeks (month_attendance rejected). Optional: requiredRolesByCellKey, laborLawJurisdiction, usLaborStateCode, linkedMonthPlanStartDate / linkedMonthPlanEndDate, laborModelVersion.
Clones a completed source schedule with absences applied. Returns 201 with new conditionId. Paid subscription required.
Prefer absentDayPairs. Legacy: absentStaffIds + absentDatesYmd (Cartesian). You may also send absentSlots. Optional substituteDayPairs / substituteSlots open coverage (source must be completed).
Updates one staff member. Send any subset of the fields in the example.
Personal max minutes: integer or null to clear. laborConstraintMask only when laborLawCompliance is true. schedulingRoleIds / Soft prefer-avoid: UUID arrays; unknown catalog IDs filtered; prefer and avoid must be disjoint. Labor and Soft require paid.
Replaces the weekly wish grid for one staff member (full replace).
Dates/time range must match the tenant weekly-wish navigation window from schedule defaults. wishByCellKey must cover every cell in that window. Values: NONE | LOW | HIGH.
When a schedule result reaches a terminal status, we POST a signed JSON notification to your HTTPS URL. The body does not include the full result; fetch details with GET /v1/schedule-results/{conditionId}.
Playground results are not delivered. If the webhook is disabled or no URL is saved, nothing is sent.
Setup
After sign-in, open API management and save an HTTPS notification URL.
Turn on Enable notifications.
Copy the signing secret shown once (it cannot be revealed again; regenerate if lost).
Verify the signature on your receiver, then GET links.result with your API key if needed.
Events
Event type and when it is sent.
schedule.result.terminal
Schedule result status becomes completed, no_solution, failed, error, or canceled
type
When
schedule.result.terminal
Schedule result status becomes completed, no_solution, failed, error, or canceled
Request body
status is terminal only. customerId is omitted from the body.
Compute a digest from the raw body (before JSON parse) and the Timestamp, then compare to Signature. Reject requests when |now − timestamp| exceeds 300 seconds (5 minutes) unless you intentionally allow wider clock skew.
HMAC-SHA256(signingSecret, `${timestamp}.${rawBody}`) → hex; header value is v1=<hex>
Retries
On 5xx or timeout we retry automatically. Persistent failures go to a dead-letter queue. Browser push notifications use a separate path.
API key tied to your paid subscription (identifies your tenant and plan limits).
Content-Type
application/json
Header
Description
x-api-key
API key tied to your paid subscription (identifies your tenant and plan limits).
Content-Type
application/json
Response headers
Some response headers (such as request ids) are trace metadata, not your API key. Treat JSON **bodies** as confidential when they contain company or user data.
Paid tier limits (entitlements)
Typical validation limits for your contract (15-minute granularity, schedule span, registered staff count, and related caps).
Billing model
Stripe subscription; product charges typically scale with registered staff seats per the pricing page (not pay-per API call).
maxScheduleDays
Up to 14 calendar days per submission for paid two-week detailed planning (hard cap; not extended by contract).
maxStaffPerSchedule
Up to 500 staff rows per POST /v1/schedule (PUBLIC_SCHEDULE_MAX_STAFF; inline staff IDs in the request body).
maxRosterStaff
Up to 30 active staff via POST /v1/staff under the current app default (STAFF_ROSTER_MAX_PAID); returns 409 STAFF_LIMIT when exceeded. On free plans with more than 5 active roster members, POST /v1/schedule returns 403 FREE_STAFF_LIMIT (roster kept). If schedule settings are missing or not user-saved (configuredByUser false), POST /v1/schedule returns 409 SCHEDULE_SETTINGS_REQUIRED.
allowedGranularities
[60, 15] — 15-minute slots are a paid capability; hourly (60) is also supported.
maxPayloadBytes
524288 (512 KiB) UTF-8 unless a higher cap is granted.
strictUnknownRootKeys
true — unknown top-level or staff keys are rejected
Setting
Paid API (typical)
Billing model
Stripe subscription; product charges typically scale with registered staff seats per the pricing page (not pay-per API call).
maxScheduleDays
Up to 14 calendar days per submission for paid two-week detailed planning (hard cap; not extended by contract).
maxStaffPerSchedule
Up to 500 staff rows per POST /v1/schedule (PUBLIC_SCHEDULE_MAX_STAFF; inline staff IDs in the request body).
maxRosterStaff
Up to 30 active staff via POST /v1/staff under the current app default (STAFF_ROSTER_MAX_PAID); returns 409 STAFF_LIMIT when exceeded. On free plans with more than 5 active roster members, POST /v1/schedule returns 403 FREE_STAFF_LIMIT (roster kept). If schedule settings are missing or not user-saved (configuredByUser false), POST /v1/schedule returns 409 SCHEDULE_SETTINGS_REQUIRED.
allowedGranularities
[60, 15] — 15-minute slots are a paid capability; hourly (60) is also supported.
maxPayloadBytes
524288 (512 KiB) UTF-8 unless a higher cap is granted.
strictUnknownRootKeys
true — unknown top-level or staff keys are rejected
Per-cell required headcount remains an integer from 0 through 15. Exceeding rate limits may return an error response.
Allowed root keys (strict mode)
When strict mode is on, only the following top-level keys are accepted for the request apiVersion. Legacy (omitted or 2026-04-01) rejects requiredRolesByCell. Version 2026-09-09 accepts it. Any other key returns UNKNOWN_FIELD.
The payload follows the paid public schedule request model: calendar dates, time zone, slot grid (60- or 15-minute on paid API), per-cell requirements, and staff availability. Labor law jurisdiction is not in the JSON. paidOptimization is optional for apiVersion 2026-09-13+ (otherwise tenant defaults). Soft prefer/avoid staff fields require 2026-09-14. See Labor law & paid flags. DynamoDB keys and internal entity types must not appear in the body.
cellKey format — Keys in requirementsByCell and availableCells must match `${date}__${slotId}` where date is YYYY-MM-DD in the schedule range and slotId comes from the enumerated slots for your time range and granularity (same as `cellKey` in playground helpers).
Quick reference table
timeZone
Type
string
Required
Yes
IANA time zone for interpreting dates and slots.
scheduleStartDate / scheduleEndDate
Type
string (YYYY-MM-DD)
Required
Yes
Inclusive calendar range; start ≤ end.
timeRangeStart / timeRangeEnd
Type
string (HH:mm)
Required
Yes
Wall-clock range that defines the slot column; must yield at least one slot.
slotGranularityMinutes
Type
60 | 15
Required
Yes
Paid API: 60 (hourly) or 15 (15-minute slots). Must be in your allowedGranularities.
requirementsByCell
Type
object
Required
Yes
Keys = cellKey in the computed grid; values = integer 0–15.
requiredRolesByCell
Type
object
Required
No — 2026-09-09 only
cellKey → roleId→need. Sum of needs per cell ≤ requirementsByCell. Legacy versions → UNKNOWN_FIELD.
staff
Type
array
Required
Yes
Non-empty; max size per limits; each item: staffId, displayName, optional availableCells / laborConstraintMask / schedulingRoleIds (2026-09-09+) / Soft prefer-avoid (2026-09-14).
Field
Type
Required
Notes
timeZone
string
Yes
IANA time zone for interpreting dates and slots.
scheduleStartDate / scheduleEndDate
string (YYYY-MM-DD)
Yes
Inclusive calendar range; start ≤ end.
timeRangeStart / timeRangeEnd
string (HH:mm)
Yes
Wall-clock range that defines the slot column; must yield at least one slot.
slotGranularityMinutes
60 | 15
Yes
Paid API: 60 (hourly) or 15 (15-minute slots). Must be in your allowedGranularities.
requirementsByCell
object
Yes
Keys = cellKey in the computed grid; values = integer 0–15.
requiredRolesByCell
object
No — 2026-09-09 only
cellKey → roleId→need. Sum of needs per cell ≤ requirementsByCell. Legacy versions → UNKNOWN_FIELD.
staff
array
Yes
Non-empty; max size per limits; each item: staffId, displayName, optional availableCells / laborConstraintMask / schedulingRoleIds (2026-09-09+) / Soft prefer-avoid (2026-09-14).
Field reference (allowed values)
Field rules below match the public API validator. Prefer JSON numbers (not numeric strings). After validation the server may persist parent-row defaults (paid optimization copy, labor jurisdiction, model version) that are not request keys — see Labor law & paid flags.
apiVersion
string | omitted · Required: No — optional
Allowed values
Omitted: accepted as legacy key set (same as 2026-04-01). Role fields are rejected as UNKNOWN_FIELD.
If present, must be "2026-04-01", "2026-09-09", "2026-09-13", or "2026-09-14". Latest published version is 2026-09-14.
Any other string is rejected (TYPE_ERROR).
Behavior
Compared after trim-equivalent handling in sanitizeApiVersion.
Version selects the allowed top-level and staff object key sets (roles on 2026-09-09+; paidOptimization on 2026-09-13+; Soft prefer/avoid on 2026-09-14).
Typical errors
TYPE_ERRORUnsupported apiVersion value.
timeZone
string · Required: Yes
Allowed values
Wire format is a single string. The API does NOT publish a closed enum of every allowed string in the schema: the server validates dynamically.
Validation matches `isValidIanaTimeZone` in the codebase: Luxon `DateTime.now().setZone(zone).isValid` must be true.
Use canonical IANA Time Zone Database identifiers, e.g. `Asia/Tokyo`, `America/New_York`, `Europe/Berlin`, `UTC`. The valid set is whatever IANA zones Luxon accepts for the deployed runtime, not a manually curated API enum.
Do not rely on timezone abbreviations alone (`EST`, `JST`, `GMT`, etc.) — they are not stable IANA zone IDs and may be rejected.
For reference lists of zone names, see the IANA tz distribution (https://www.iana.org/time-zones) or your platform’s timezone API.
Behavior
Used with schedule dates and slot times in downstream normalization.
If you need a fixed list in your client, derive it from the same rules (IANA IDs) rather than expecting a server-side enum in the JSON schema.
Typical errors
INVALID_TIME_ZONEMissing, empty, or not a valid IANA zone.
scheduleStartDate, scheduleEndDate
string, string · Required: Yes — both
Allowed values
Must match /^\d{4}-\d{2}-\d{2}$/ (trimmed).
Must be real calendar dates; the inclusive range must produce at least one day via enumerateDates.
The number of days in range must not exceed maxScheduleDays (paid API hard cap: 14 calendar days for two-week planning).
Behavior
scheduleStartDate must be on or before scheduleEndDate.
Typical errors
INVALID_DATE_RANGEBad format, invalid dates, or empty range.
SCHEDULE_SPAN_TOO_LONGMore than maxScheduleDays days.
timeRangeStart, timeRangeEnd
string, string · Required: Yes — both
Allowed values
Non-empty strings (trimmed) interpreted by the same slot enumeration as the UI (`enumerateSlotsByGranularity`).
The pair must generate at least one slot; otherwise validation fails.
Behavior
Together with slotGranularityMinutes, defines slotId values used in cellKey construction.
Typical errors
INVALID_TIME_RANGEMissing values or zero slots generated for the range.
slotGranularityMinutes
number (JSON number) · Required: Yes
Allowed values
Must be exactly 60 or 15 (not a string).
Must also appear in allowedGranularities for the request. On the paid API both 60 and 15 are typically enabled; if 15 is disabled for your tenant, INVALID_GRANULARITY is returned.
Behavior
Defines slot count together with the time range.
Typical errors
INVALID_GRANULARITYNot 60/15, or not allowed for the plan.
requirementsByCell
Record<string, number> · Required: Yes
Allowed values
Must be a plain JSON object (not an array).
Each key must be a cellKey in the expected grid for (dates × slotIds). Unknown keys → UNKNOWN_CELL_KEY.
Each value must be a JSON number, integer, between 0 and 15 inclusive.
Omitted cells are treated as 0 requirement where not specified (only cells with need ≥ 1 participate in SHORTFALL checks).
Behavior
Keys are compared after trim via sanitizeCellKey.
Typical errors
INVALID_REQUIREMENTSNot an object, or invalid numeric value for a key.
UNKNOWN_CELL_KEYKey not in the computed date × slot grid.
requiredRolesByCell
object | omitted · Required: No — optional; apiVersion 2026-09-09 only
Allowed values
Only accepted when apiVersion is "2026-09-09". On omitted / 2026-04-01 → UNKNOWN_FIELD.
Must be a plain JSON object. Each key is a cellKey in the requirements grid.
Each value is an object mapping roleId (UUID) → integer need ≥ 1.
For each cell, sum(need) must be ≤ requirementsByCell[cell] (or 0 if omitted).
Submit path validates UUID shape and sum only (no catalog lookup).
Behavior
Values are normalized and validated (UUID role IDs; sum of needs ≤ headcount).
Persisted as requiredRolesBySlot on daily requirement rows.
Typical errors
UNKNOWN_FIELDSent on legacy apiVersion.
INVALID_REQUIREMENTSBad shape, non-UUID roleId, or sum exceeds headcount.
UNKNOWN_CELL_KEYcellKey not in the computed grid.
staff
array · Required: Yes — non-empty array
Allowed values
Length between 1 and maxStaffPerSchedule (default 500 for POST /v1/schedule; separate from the roster cap for POST /v1/staff).
Each element must be a plain object with allowed keys only for the apiVersion: staffId, displayName, availableCells, laborConstraintMask; plus schedulingRoleIds on 2026-09-09+; plus preferredSchedulingRoleIds / avoidedSchedulingRoleIds on 2026-09-14 (strict mode).
staffId and displayName must survive sanitization (see Sanitization section).
staffId must be unique across the array (DUPLICATE_STAFF_ID).
Behavior
Order is preserved for availability counting.
Typical errors
INVALID_STAFFEmpty array, too many rows, bad object, or failed sanitization.
UNKNOWN_FIELDExtra key on a staff object in strict mode.
DUPLICATE_STAFF_IDSame staffId twice.
staff[].availableCells
string[] | omitted · Required: No — optional
Allowed values
If omitted: the staff member is treated as available on all cells in the grid (implementation uses a null “all cells” set).
If present: must be a JSON array of strings; each non-empty string must be a cellKey in the expected grid.
Empty strings in the array are invalid (INVALID_AVAILABLE_CELL).
SHORTFALL check
For every cell where required count ≥ 1, the number of staff who count as available for that cell must be ≥ required count, or SHORTFALL_CELLS.
Typical errors
INVALID_AVAILABLE_CELLUnknown cell key or empty entry.
SHORTFALL_CELLSNot enough staff cover required headcount for a cell.
staff[].schedulingRoleIds / Soft prefer-avoid
string[] | omitted · Required: No — optional; roles on 2026-09-09+; Soft on 2026-09-14
Copied onto staff condition snapshot rows when non-empty.
Typical errors
UNKNOWN_FIELDSent on an apiVersion that does not allow the key.
INVALID_STAFFNot an array, non-UUID element, or Soft prefer/avoid overlap.
Labor law & paid optimization (not in the JSON body)
n/a — persisted server-side · In request: No — never send these keys
What the API stores on the parent row
The public API accepts only the allowed top-level keys and staff object keys for your apiVersion. Do not send laborLawJurisdiction, usLaborStateCode, or laborModelVersion. paidOptimization is optional for apiVersion 2026-09-13+. Soft prefer/avoid staff fields require 2026-09-14.
On submit, the server sets the parent SCHEDULE_CONDITION from shared defaults: paidOptimization comes from the body (apiVersion 2026-09-13+) or tenant SCHEDULE_DEFAULTS when omitted; otherwise DEFAULT_PAID_OPTIMIZATION (parent labor compliance forced on). laborLawJurisdiction / usLaborStateCode / laborModelVersion are inherited from tenant SCHEDULE_DEFAULTS (fallback JP / GENERIC / model for jurisdiction).
Statutory-style labor constraints in the optimizer require both parent and per-staff labor compliance flags to be on. Per-staff flags are not in the JSON body: the server inherits them from the tenant staff roster (unlisted staffIds default to on). Create staff via POST /v1/staff also defaults to on; turn off per person with paid PATCH /v1/staff/{staffId}.
The labor model is an approximation for scheduling, not legal advice.
Notes
Per-staff labor toggles and Soft prefer/avoid are managed with PATCH /v1/staff/{staffId} (paid). See Write endpoints.
If you send disallowed keys
UNKNOWN_FIELDStrict mode rejects unknown root or staff properties.
Sanitization rules
Applied before or during validation. These rules explain why a value might be rejected as INVALID_STAFF.
staffId
Trim; length 1–128.
Characters must match /^[a-zA-Z0-9._-]+$/ (no slashes or backslashes).
displayName
Strip control characters and zero-width characters; collapse whitespace; trim.
Angle brackets < and > are not allowed.
Max length 128 after sanitization; must not become empty.
Cell keys (requirements / availableCells)
Keys are trimmed; must match the exact cellKey strings in the computed grid.
Success response
202 Accepted
The request body passed validation and the condition was written; optimization may continue asynchronously. The handler may return a JSON body with conditionId and related fields.
Error codes (full list)
Validation errors return structured error information with a stable `code`. Parsing happens before validation (INVALID_JSON, PAYLOAD_TOO_LARGE). Runtime persistence failures may surface as DYNAMODB_ERROR. Write handlers for schedule defaults may return SCHEDULE_DEFAULTS_SAVE_FAILED (HTTP 400) with details when Zod or business rules fail.