Auto Scheduler
PricingPlaygroundFlowBlogFAQAPI docs
Sign In

Auto Scheduler

Cloud service for shift scheduling and optimization—fair, constraint-aware work plans for your workforce.

Terms of Service|Privacy Policy|Legal Disclosures

Solver

On this page

OverviewScopeSubscription & billingAuthenticationCopy for LLMEndpointHow to call (quick start)MCP (agents)Read endpoints (GET)Write endpoints (PATCH / POST / PUT / DELETE)Completion webhooksHeadersLimits & defaultsAllowed root keysRequest body overviewQuick reference tableField reference (allowed values)apiVersiontimeZonescheduleStartDate / scheduleEndDatetimeRangeStart / timeRangeEndslotGranularityMinutesrequirementsByCellrequiredRolesByCellstaffavailableCellsstaff[].schedulingRoleIds / Soft prefer-avoidLabor law & paid flags (server defaults)SanitizationSuccess responseError codes (full list)JSON exampleFurther reading

Auto Scheduler

Paid API · Subscription

Developer API reference

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.

Manage keys: API management

Copy for LLM

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.

AUTO_SCHEDULER_PUBLIC_API_BASE_URL=https://api.autoschedulers.com
https://api.autoschedulers.com
POST https://api.autoschedulers.com/v1/schedule

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.

curl -sS -D - \
  -H "x-api-key: YOUR_API_KEY" \
  "https://api.autoschedulers.com/v1/schedule-results?limit=10"

curl -sS -D - \
  -H "x-api-key: YOUR_API_KEY" \
  "https://api.autoschedulers.com/v1/staff"

MCP (agents)

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)

  1. Create an API key in Account → API management (never paste the real key into chat).
  2. In the repo: cd mcp/public-api && npm install && npm run build (also mcp/api-docs if you want documentation Resources).
  3. Copy the env snippet below and the Cursor mcp.json under Client-specific setup; replace paths and YOUR_* placeholders.
  4. 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)

VariableDescription
AUTO_SCHEDULER_PUBLIC_API_BASE_URLBase URL of the public REST API (/v1/*)—not the website domain. Use the API host you were given (trailing slash optional)
AUTO_SCHEDULER_API_KEYKey from API management (do not commit or post publicly)

Env snippet (copy)

AUTO_SCHEDULER_PUBLIC_API_BASE_URL=https://api.autoschedulers.com
AUTO_SCHEDULER_API_KEY=YOUR_API_KEY

Shared prep

  1. Create an API key in API management (Tools MCP only; not needed for the docs MCP)
  2. In the repo run cd mcp/public-api → npm install && npm run build (and mcp/api-docs if you want Resources)
  3. Follow the client-specific steps below and adjust paths and env for your machine
  4. Restart the client (or reconnect MCP) and confirm tools / resources appear

Client-specific setup

The JSON shape is almost the same everywhere. What differs is the config file location—and that ChatGPT (GPT) does not launch local stdio servers.

Cursor

User mcp.json (e.g. Windows %USERPROFILE%\.cursor\mcp.json). You can also edit via Cursor Settings → MCP.

  1. Add the snippet below under mcpServers (keep any existing servers)
  2. Replace absolute paths and YOUR_BASE_URL / YOUR_API_KEY
  3. Restart Cursor and confirm Tools such as list_staff and Resources from api-docs

On Windows escape backslashes as \\ in JSON. On macOS / Linux use forward-slash paths.

{
  "mcpServers": {
    "auto-scheduler-public-api": {
      "command": "node",
      "args": [
        "C:\\path\\to\\auto-scheduler\\mcp\\public-api\\dist\\index.js"
      ],
      "env": {
        "AUTO_SCHEDULER_PUBLIC_API_BASE_URL": "https://api.autoschedulers.com",
        "AUTO_SCHEDULER_API_KEY": "YOUR_API_KEY"
      }
    },
    "auto-scheduler-api-docs": {
      "command": "node",
      "args": [
        "C:\\path\\to\\auto-scheduler\\mcp\\api-docs\\dist\\index.js"
      ]
    }
  }
}

public-api exposes Tools (API key required). api-docs exposes Resources (no key).

Claude (Desktop)

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json / Windows: %APPDATA%\Claude\claude_desktop_config.json (Settings → Developer → Edit Config)

  1. Add the snippet below under mcpServers in claude_desktop_config.json
  2. Adjust absolute paths and env for your machine
  3. Fully quit and restart Claude Desktop, then check Connectors / tools

Same mcpServers format as Cursor. Follow Anthropic's "Connect to local MCP servers" flow.

{
  "mcpServers": {
    "auto-scheduler-public-api": {
      "command": "node",
      "args": [
        "C:\\path\\to\\auto-scheduler\\mcp\\public-api\\dist\\index.js"
      ],
      "env": {
        "AUTO_SCHEDULER_PUBLIC_API_BASE_URL": "https://api.autoschedulers.com",
        "AUTO_SCHEDULER_API_KEY": "YOUR_API_KEY"
      }
    },
    "auto-scheduler-api-docs": {
      "command": "node",
      "args": [
        "C:\\path\\to\\auto-scheduler\\mcp\\api-docs\\dist\\index.js"
      ]
    }
  }
}

With Claude Code you can also register via project .mcp.json or claude mcp add.

Gemini (CLI)

User: ~/.gemini/settings.json / Project: .gemini/settings.json (project wins when both exist)

  1. Add the snippet under mcpServers (or use gemini mcp add)
  2. Prefer hyphenated server names (Gemini CLI restriction)
  3. Start the CLI and verify with /mcp (connection + tools)

command / args / env match Cursor and Claude. Optional fields: timeout, trust: false.

{
  "mcpServers": {
    "auto-scheduler-public-api": {
      "command": "node",
      "args": [
        "C:\\path\\to\\auto-scheduler\\mcp\\public-api\\dist\\index.js"
      ],
      "env": {
        "AUTO_SCHEDULER_PUBLIC_API_BASE_URL": "https://api.autoschedulers.com",
        "AUTO_SCHEDULER_API_KEY": "YOUR_API_KEY"
      }
    },
    "auto-scheduler-api-docs": {
      "command": "node",
      "args": [
        "C:\\path\\to\\auto-scheduler\\mcp\\api-docs\\dist\\index.js"
      ]
    }
  }
}

Gemini cloud console connectors target remote MCP. Use Gemini CLI for local stdio.

GPT (ChatGPT / OpenAI)

ChatGPT connector settings (remote MCP only). There is no local mcp.json for ChatGPT.

  1. ChatGPT (web / desktop connectors) cannot spawn local stdio MCP processes
  2. This product ships local stdio MCP only, so ChatGPT cannot connect to it directly
  3. 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)
  • update_schedule_defaults (organization-wide defaults)
  • create_staff / update_staff
  • upsert_weekly_shift_wish (full replace of weekly wish)
  • submit_emergency_shift (paid emergency clone; new condition)
  • confirm_schedule_assignments (persist confirmed assignments)
  • cancel_schedule_condition (cancel a condition)
  • put_schedule_actual (full replace of actuals)

Confirm destructive writes with the human before calling. The docs MCP (api-docs) is Resources-only, not Tools.

Create a key: API management

Read endpoints (GET)

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).

    Query: limit, cursor.

    Example response

    {
      "items": [
        {
          "conditionId": "cond-11111111-1111-1111-1111-111111111111",
          "status": "completed",
          "candidateCount": 3,
          "createdAt": "2026-04-07T10:00:00.000Z",
          "updatedAt": "2026-04-07T10:05:00.000Z",
          "entityType": "SCHEDULE_RESULT"
        }
      ],
      "nextCursor": "eyJwayI6Ii4uLiJ9"
    }
  • GET{baseUrl}/v1/schedule-results/history

    History search over recent + archive data (same shape as the browser history view).

    Query: from, to (ISO 8601; default service start→now), keyword, eventName (comma-separated INSERT,MODIFY,REMOVE), limit, cursor.

    Example response

    {
      "items": [
        {
          "conditionId": "cond-11111111-1111-1111-1111-111111111111",
          "status": "completed",
          "candidateCount": 2,
          "createdAt": "2026-04-01T09:00:00.000Z",
          "updatedAt": "2026-04-01T09:10:00.000Z",
          "entityType": "SCHEDULE_RESULT"
        }
      ],
      "nextCursor": "eyJwayI6Ii4uLiJ9"
    }
  • GET{baseUrl}/v1/schedule-results/{conditionId}

    One schedule-result parent row (PK/SK stripped).

    Example response

    {
      "scheduleResult": {
        "conditionId": "cond-11111111-1111-1111-1111-111111111111",
        "status": "completed",
        "candidateCount": 3,
        "entityType": "SCHEDULE_RESULT",
        "createdAt": "2026-04-07T10:00:00.000Z",
        "updatedAt": "2026-04-07T10:05:00.000Z"
      }
    }
  • GET{baseUrl}/v1/schedule-results/{conditionId}/resolution

    Resolved optimization output for the condition.

    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.

    Example response

    {
      "resolution": {
        "status": "completed",
        "candidates": [
          {
            "rank": 1,
            "assignments": [
              {
                "personId": "550e8400-e29b-41d4-a716-446655440000",
                "personName": "Example Staff",
                "date": "2026-04-07",
                "slotId": "09:00",
                "slotLabel": "09:00"
              }
            ]
          }
        ],
        "confirmedAt": "2026-04-07T10:06:00.000Z",
        "confirmedAssignments": [
          {
            "personId": "550e8400-e29b-41d4-a716-446655440000",
            "personName": "Example Staff",
            "date": "2026-04-07",
            "slotId": "09:00",
            "slotLabel": "09:00"
          }
        ]
      }
    }
  • GET{baseUrl}/v1/schedule-results/{conditionId}/review

    Review payload for manual confirmation UI / clients.

    Example response

    {
      "review": {
        "conditionId": "cond-11111111-1111-1111-1111-111111111111",
        "status": "completed",
        "timeZone": "Asia/Tokyo",
        "calendarDates": ["2026-04-07"],
        "slotIds": ["09:00", "10:00"],
        "slotLabels": ["09:00", "10:00"],
        "requiredByCellKey": {
          "2026-04-07__09:00": 2
        },
        "candidates": [
          {
            "rank": 1,
            "assignments": [
              {
                "personId": "550e8400-e29b-41d4-a716-446655440000",
                "personName": "Example Staff",
                "date": "2026-04-07",
                "slotId": "09:00",
                "slotLabel": "09:00"
              }
            ]
          }
        ],
        "staff": [
          {
            "staffId": "550e8400-e29b-41d4-a716-446655440000",
            "displayName": "Example Staff",
            "allowedCellKeys": ["2026-04-07__09:00", "2026-04-07__10:00"]
          }
        ]
      }
    }
  • GET{baseUrl}/v1/schedule-results/{conditionId}/emergency-shift-context

    Context for building an emergency-shift request. Paid subscription required.

    When the condition is missing, emergencyShiftContext is null.

    Example response

    {
      "emergencyShiftContext": {
        "sourceConditionId": "cond-11111111-1111-1111-1111-111111111111",
        "timeZone": "Asia/Tokyo",
        "calendarDates": ["2026-04-07", "2026-04-08"],
        "staff": [
          {
            "staffId": "550e8400-e29b-41d4-a716-446655440000",
            "displayName": "Example Staff",
            "schedulingRoleIds": [],
            "availabilityByDateYmd": {}
          }
        ],
        "slots": [
          { "slotId": "09:00", "slotLabel": "09:00" }
        ],
        "dayRequirements": [],
        "roleLabelById": {},
        "rosterCandidates": []
      }
    }
  • GET{baseUrl}/v1/schedule-conditions

    Lists schedule-condition index summaries.

    Query: limit, cursor.

    Example response

    {
      "items": [
        {
          "conditionId": "cond-11111111-1111-1111-1111-111111111111",
          "createdAt": "2026-04-07T10:00:00.000Z",
          "scheduleStartDate": "2026-04-07",
          "scheduleEndDate": "2026-04-13"
        }
      ],
      "nextCursor": "eyJwayI6Ii4uLiJ9"
    }
  • GET{baseUrl}/v1/schedule-conditions/{conditionId}

    Condition parent plus reqDays and staffSnapshots (PK/SK stripped).

    Example response

    {
      "condition": {
        "conditionId": "cond-11111111-1111-1111-1111-111111111111",
        "entityType": "SCHEDULE_CONDITION",
        "scheduleStartDate": "2026-04-07",
        "scheduleEndDate": "2026-04-13",
        "timeZone": "Asia/Tokyo"
      },
      "reqDays": [
        {
          "entityType": "SCHEDULE_CONDITION_REQ_DAY",
          "date": "2026-04-07"
        }
      ],
      "staffSnapshots": [
        {
          "entityType": "SCHEDULE_CONDITION_STAFF",
          "staffId": "550e8400-e29b-41d4-a716-446655440000",
          "displayName": "Example Staff"
        }
      ]
    }
  • GET{baseUrl}/v1/schedule-defaults

    Tenant schedule defaults, or null when not saved yet (still HTTP 200).

    Example response

    {
      "scheduleDefaults": {
        "entityType": "SCHEDULE_DEFAULTS",
        "scheduleStartDate": "2026-04-07",
        "scheduleEndDate": "2026-04-13",
        "timeRangeStart": "09:00",
        "timeRangeEnd": "17:00",
        "slotGranularityMinutes": 60,
        "planningHorizon": "week",
        "requirementsByCellKey": {
          "2026-04-07__09:00": 2
        },
        "paidOptimization": {
          "laborLawCompliance": true,
          "balanceStaffLoad": false,
          "honorShiftWishes": true
        }
      }
    }
  • GET{baseUrl}/v1/schedule-actual/{conditionId}

    Saved actual assignments for the condition, or null if none.

    Example response

    {
      "actual": {
        "entityType": "SCHEDULE_ACTUAL",
        "conditionId": "cond-11111111-1111-1111-1111-111111111111",
        "assignments": [
          {
            "personId": "550e8400-e29b-41d4-a716-446655440000",
            "personName": "Example Staff",
            "date": "2026-04-07",
            "slotId": "09:00",
            "slotLabel": "09:00"
          }
        ]
      }
    }
  • GET{baseUrl}/v1/staff/{staffId}/weekly-shift-wish

    Weekly wish row for one staff member, or null if none. Soft-deleted staff → 404.

    Example response

    {
      "weeklyShiftWish": {
        "entityType": "WEEKLY_SHIFT_WISH",
        "staffId": "550e8400-e29b-41d4-a716-446655440000",
        "scheduleStartDate": "2026-04-07",
        "scheduleEndDate": "2026-04-13",
        "timeRangeStart": "09:00",
        "timeRangeEnd": "11:00",
        "wishByCellKey": {
          "2026-04-07__09:00": "HIGH",
          "2026-04-07__10:00": "NONE"
        }
      }
    }
  • GET{baseUrl}/v1/staff

    Active staff members for the tenant (sorted by displayName).

    Example response

    {
      "staff": [
        {
          "staffId": "550e8400-e29b-41d4-a716-446655440000",
          "displayName": "Example Staff",
          "createdAt": "2026-03-01T00:00:00.000Z",
          "updatedAt": "2026-04-01T00:00:00.000Z",
          "laborLawCompliance": true,
          "schedulingRoleIds": [
            "550e8400-e29b-41d4-a716-446655440099"
          ]
        }
      ]
    }
  • GET{baseUrl}/v1/staff/{staffId}

    One staff member including labor / Soft fields when present. Soft-deleted → 404.

    Example response

    {
      "staff": {
        "staffId": "550e8400-e29b-41d4-a716-446655440000",
        "displayName": "Example Staff",
        "createdAt": "2026-03-01T00:00:00.000Z",
        "updatedAt": "2026-04-01T00:00:00.000Z",
        "laborLawCompliance": true,
        "laborFlsaExempt": false,
        "personalMaxWeeklyMinutes": 2400,
        "laborConstraintMask": {
          "schema": "v1"
        },
        "schedulingRoleIds": [
          "550e8400-e29b-41d4-a716-446655440099"
        ],
        "preferredSchedulingRoleIds": [],
        "avoidedSchedulingRoleIds": []
      }
    }

Write endpoints (PATCH / POST / PUT / DELETE)

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.

    Example request body

    {
      "scheduleStartDate": "2026-04-07",
      "scheduleEndDate": "2026-04-07",
      "timeRangeStart": "09:00",
      "timeRangeEnd": "11:00",
      "slotGranularityMinutes": 60,
      "planningHorizon": "week",
      "requirementsByCellKey": {
        "2026-04-07__09:00": 2,
        "2026-04-07__10:00": 1
      },
      "requiredRolesByCellKey": {
        "2026-04-07__09:00": {
          "550e8400-e29b-41d4-a716-446655440099": 1
        }
      },
      "paidOptimization": {
        "laborLawCompliance": true,
        "balanceStaffLoad": false,
        "honorShiftWishes": true
      }
    }
  • POST{baseUrl}/v1/staff

    Creates a staff member. Returns 201 with staff JSON.

    Only displayName is accepted.

    Example request body

    {
      "displayName": "Example Staff"
    }
  • POST{baseUrl}/v1/schedule-results/{conditionId}/emergency-shift

    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).

    Example request body

    {
      "absentDayPairs": [
        {
          "staffId": "550e8400-e29b-41d4-a716-446655440000",
          "dateYmd": "2026-04-08"
        }
      ],
      "substituteDayPairs": [
        {
          "staffId": "660e8400-e29b-41d4-a716-446655440001",
          "dateYmd": "2026-04-08"
        }
      ]
    }
  • POST{baseUrl}/v1/schedule-results/{conditionId}/confirm-assignments

    Validates and saves manually edited assignments. Returns 200 with { ok: true }.

    assignments is required. relaxAvailability is optional (boolean).

    Example request body

    {
      "assignments": [
        {
          "personId": "550e8400-e29b-41d4-a716-446655440000",
          "personName": "Example Staff",
          "date": "2026-04-07",
          "slotId": "09:00",
          "slotLabel": "09:00"
        }
      ],
      "relaxAvailability": false
    }
  • PATCH{baseUrl}/v1/staff/{staffId}

    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.

    Example request body

    {
      "displayName": "Example Staff",
      "laborLawCompliance": true,
      "laborFlsaExempt": false,
      "personalMaxWeeklyMinutes": 2400,
      "personalMaxMonthlyMinutes": null,
      "personalMaxThreeMonthMinutes": null,
      "laborConstraintMask": {
        "schema": "v1"
      },
      "schedulingRoleIds": [
        "550e8400-e29b-41d4-a716-446655440099"
      ],
      "preferredSchedulingRoleIds": [
        "550e8400-e29b-41d4-a716-446655440099"
      ],
      "avoidedSchedulingRoleIds": []
    }
  • DELETE{baseUrl}/v1/staff/{staffId}

    Soft-deletes a staff member (sets deletedAt).

    No request body.

  • DELETE{baseUrl}/v1/schedule-conditions/{conditionId}

    Marks the condition as cancelled (publicApiCancelledAt); does not delete child rows.

    No request body.

  • PUT{baseUrl}/v1/schedule-actual/{conditionId}

    Saves actual shift assignments (validated against the condition grid).

    Same assignment object shape as confirm-assignments.

    Example request body

    {
      "assignments": [
        {
          "personId": "550e8400-e29b-41d4-a716-446655440000",
          "personName": "Example Staff",
          "date": "2026-04-07",
          "slotId": "09:00",
          "slotLabel": "09:00"
        }
      ]
    }
  • PUT{baseUrl}/v1/staff/{staffId}/weekly-shift-wish

    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.

    Example request body

    {
      "scheduleStartDate": "2026-04-07",
      "scheduleEndDate": "2026-04-13",
      "timeRangeStart": "09:00",
      "timeRangeEnd": "11:00",
      "wishByCellKey": {
        "2026-04-07__09:00": "HIGH",
        "2026-04-07__10:00": "NONE",
        "2026-04-08__09:00": "LOW",
        "2026-04-08__10:00": "NONE"
      }
    }

Completion webhooks

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

typeWhen
schedule.result.terminalSchedule result status becomes completed, no_solution, failed, error, or canceled

Request body

status is terminal only. customerId is omitted from the body.

{
  "type": "schedule.result.terminal",
  "conditionId": "019ee481-e855-7000-abd8-977cbfd64ffc",
  "status": "completed",
  "occurredAt": "2026-07-16T13:00:54.694Z",
  "links": {
    "result": "{baseUrl}/v1/schedule-results/019ee481-e855-7000-abd8-977cbfd64ffc"
  }
}

Request headers

  • Content-Type

    application/json

  • User-Agent

    AutoScheduler-Webhook/1.0

  • X-AutoScheduler-Timestamp

    Unix seconds as a string

  • X-AutoScheduler-Signature

    v1=<hex> (see verification below)

HeaderDescription
Content-Typeapplication/json
User-AgentAutoScheduler-Webhook/1.0
X-AutoScheduler-TimestampUnix seconds as a string
X-AutoScheduler-Signaturev1=<hex> (see verification below)

Signature verification

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.

Register & enable: API management

Headers

  • x-api-key

    API key tied to your paid subscription (identifies your tenant and plan limits).

  • Content-Type

    application/json

HeaderDescription
x-api-keyAPI key tied to your paid subscription (identifies your tenant and plan limits).
Content-Typeapplication/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

SettingPaid API (typical)
Billing modelStripe subscription; product charges typically scale with registered staff seats per the pricing page (not pay-per API call).
maxScheduleDaysUp to 14 calendar days per submission for paid two-week detailed planning (hard cap; not extended by contract).
maxStaffPerScheduleUp to 500 staff rows per POST /v1/schedule (PUBLIC_SCHEDULE_MAX_STAFF; inline staff IDs in the request body).
maxRosterStaffUp 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.
maxPayloadBytes524288 (512 KiB) UTF-8 unless a higher cap is granted.
strictUnknownRootKeystrue — 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.

  • apiVersion
  • timeZone
  • scheduleStartDate
  • scheduleEndDate
  • timeRangeStart
  • timeRangeEnd
  • slotGranularityMinutes
  • requirementsByCell
  • requiredRolesByCell (2026-09-09+)
  • paidOptimization (2026-09-13+)
  • preferredSchedulingRoleIds / avoidedSchedulingRoleIds (2026-09-14 staff)
  • staff

Request body (overview)

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.

  • apiVersion — Optional. Omit or "2026-04-01" = legacy. "2026-09-09" = roles; "2026-09-13" = roles + paidOptimization; "2026-09-14" = Soft prefer/avoid. Latest is 2026-09-14.
  • 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).

FieldTypeRequiredNotes
timeZonestringYesIANA time zone for interpreting dates and slots.
scheduleStartDate / scheduleEndDatestring (YYYY-MM-DD)YesInclusive calendar range; start ≤ end.
timeRangeStart / timeRangeEndstring (HH:mm)YesWall-clock range that defines the slot column; must yield at least one slot.
slotGranularityMinutes60 | 15YesPaid API: 60 (hourly) or 15 (15-minute slots). Must be in your allowedGranularities.
requirementsByCellobjectYesKeys = cellKey in the computed grid; values = integer 0–15.
requiredRolesByCellobjectNo — 2026-09-09 onlycellKey → roleId→need. Sum of needs per cell ≤ requirementsByCell. Legacy versions → UNKNOWN_FIELD.
staffarrayYesNon-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

Allowed values

  • schedulingRoleIds: accepted on apiVersion 2026-09-09+. Soft preferredSchedulingRoleIds / avoidedSchedulingRoleIds: 2026-09-14 only. Older versions → UNKNOWN_FIELD.
  • JSON arrays of UUID strings. Duplicates are removed. Soft prefer ∩ avoid must be empty.
  • Empty array / omit = no Hard role-frame / Soft preference on the condition snapshot.
  • Submit path does not filter against the tenant catalog; PATCH /v1/staff/{staffId} does (Soft PATCH is paid).

Behavior

  • Values are normalized (UUID strings; duplicates removed). Soft overlap → INVALID_STAFF.
  • 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.

  • INVALID_JSON

    Body is not valid JSON.

  • PAYLOAD_TOO_LARGE

    UTF-8 byte length exceeds maxPayloadBytes (default 512 KiB).

  • TYPE_ERROR

    Wrong JSON type, or unsupported apiVersion string.

  • UNKNOWN_FIELD

    Strict mode: disallowed property on root or staff object.

  • INVALID_TIME_ZONE

    timeZone missing or not a valid IANA name.

  • INVALID_DATE_RANGE

    Dates not YYYY-MM-DD, invalid range, or empty enumeration.

  • SCHEDULE_SPAN_TOO_LONG

    Too many days between start and end (see maxScheduleDays).

  • INVALID_TIME_RANGE

    Time range missing or yields zero slots.

  • INVALID_GRANULARITY

    slotGranularityMinutes not 60/15 or not allowed for plan.

  • INVALID_REQUIREMENTS

    requirementsByCell not an object, or count not integer 0–15.

  • UNKNOWN_CELL_KEY

    Key not in the computed schedule grid.

  • INVALID_STAFF

    staff array empty, too large, bad row, or failed sanitization.

  • DUPLICATE_STAFF_ID

    Duplicate staffId values.

  • INVALID_AVAILABLE_CELL

    Empty or unknown entry in availableCells.

  • SHORTFALL_CELLS

    Insufficient staff availability vs required counts.

  • DYNAMODB_ERROR

    Transient or persistence error after validation (handler-dependent).

  • SCHEDULE_DEFAULTS_SAVE_FAILED

    PATCH /v1/schedule-defaults: validation or business rules failed (see error.details).

  • LABOR_COMPLIANCE_REQUIRED

    PATCH /v1/staff/{staffId}: laborConstraintMask sent while laborLawCompliance is false.

CodeMeaning
INVALID_JSONBody is not valid JSON.
PAYLOAD_TOO_LARGEUTF-8 byte length exceeds maxPayloadBytes (default 512 KiB).
TYPE_ERRORWrong JSON type, or unsupported apiVersion string.
UNKNOWN_FIELDStrict mode: disallowed property on root or staff object.
INVALID_TIME_ZONEtimeZone missing or not a valid IANA name.
INVALID_DATE_RANGEDates not YYYY-MM-DD, invalid range, or empty enumeration.
SCHEDULE_SPAN_TOO_LONGToo many days between start and end (see maxScheduleDays).
INVALID_TIME_RANGETime range missing or yields zero slots.
INVALID_GRANULARITYslotGranularityMinutes not 60/15 or not allowed for plan.
INVALID_REQUIREMENTSrequirementsByCell not an object, or count not integer 0–15.
UNKNOWN_CELL_KEYKey not in the computed schedule grid.
INVALID_STAFFstaff array empty, too large, bad row, or failed sanitization.
DUPLICATE_STAFF_IDDuplicate staffId values.
INVALID_AVAILABLE_CELLEmpty or unknown entry in availableCells.
SHORTFALL_CELLSInsufficient staff availability vs required counts.
DYNAMODB_ERRORTransient or persistence error after validation (handler-dependent).
SCHEDULE_DEFAULTS_SAVE_FAILEDPATCH /v1/schedule-defaults: validation or business rules failed (see error.details).
LABOR_COMPLIANCE_REQUIREDPATCH /v1/staff/{staffId}: laborConstraintMask sent while laborLawCompliance is false.

Minimal JSON example

{
  "apiVersion": "2026-04-01",
  "timeZone": "Asia/Tokyo",
  "scheduleStartDate": "2026-04-07",
  "scheduleEndDate": "2026-04-13",
  "timeRangeStart": "08:00",
  "timeRangeEnd": "20:00",
  "slotGranularityMinutes": 60,
  "requirementsByCell": {
    "2026-04-07__slot-0": 2
  },
  "staff": [
    {
      "staffId": "550e8400-e29b-41d4-a716-446655440000",
      "displayName": "Example",
      "availableCells": ["2026-04-07__slot-0"]
    }
  ]
}

On this page. POST /v1/schedule body: Field reference and Quick reference table. Tenant defaults: Write endpoints → PATCH /v1/schedule-defaults. Staff labor / Soft prefer-avoid: Write endpoints → PATCH /v1/staff/{staffId}. Plans and seats: Pricing page.