Auto Scheduler
PricingPlaygroundFlowBlogFAQAPI docs
Sign In

On this page

OverviewScopeSubscription & billingAuthenticationEndpointHow to call (quick start)Read endpoints (GET)Write endpoints (PATCH / POST / PUT / DELETE)HeadersLimits & defaultsAllowed root keysRequest body overviewQuick reference tableField reference (allowed values)apiVersiontimeZonescheduleStartDate / scheduleEndDatetimeRangeStart / timeRangeEndslotGranularityMinutesrequirementsByCellstaffavailableCellsLabor law & paid flags (server defaults)SanitizationSuccess responseError codes (full list)JSON exampleFurther reading
Paid API · Subscription

Paid developer API reference

Integrate with the production schedule submission API using an API key tied to your paid subscription. Billing follows your plan and contract (not per-API-call usage pricing). You can also read and update tenant schedule defaults (PATCH /v1/schedule-defaults), and read or replace per-staff weekly shift wishes (GET/PUT /v1/staff/{staffId}/weekly-shift-wish). This page documents paid-tier entitlements, validation rules, and error codes — use the sidebar search to jump to a field.

What this documentation covers

This page documents only the product REST API on API Gateway ({stage}/v1/*, authenticated with x-api-key).

Next.js /api/* routes (Stripe webhooks, OG images, Web Vitals, and similar) are internal to the web application and are not part of this developer API. Do not use them for external integrations.

GET /v1/processing-history is not offered (removed with the legacy UI) and is not listed among the endpoints on this page.

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.

Manage keys: API management

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.

POST {baseUrl}/v1/schedule

Use the base URL provided when your API access was provisioned or by your operations contact.

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.

Company read/update (GET/PATCH /v1/company) requires an active paid subscription. You may receive **503** (e.g. code **STRIPE_NOT_CONFIGUREED**) when billing is not configured for your tenant.

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" \
  "BASE_URL/v1/schedule-results?limit=10"

curl -sS -D - \
  -H "x-api-key: YOUR_API_KEY" \
  "BASE_URL/v1/company"

Read endpoints (GET)

Same base URL and API key as POST /v1/schedule. Optional query: limit (1–100, default 50), cursor (opaque token from the previous response field nextCursor).

GET /v1/company and company updates require an active paid subscription. You may receive **503 / STRIPE_NOT_CONFIGURED** when billing is not configured.

MethodPathDescription
GET{baseUrl}/v1/schedule-resultsLists SCHEDULE_RESULT parent rows: conditionId, status, candidateCount, createdAt/updatedAt. DynamoDB parent-row pagination.
GET{baseUrl}/v1/schedule-results/historySchedule result history search (same as the browser history view). Optional from / to (ISO 8601; default service start to now), keyword, eventName (comma-separated INSERT,MODIFY,REMOVE), limit, cursor. Merges recent and archive data automatically; returns only items and nextCursor.
GET{baseUrl}/v1/schedule-results/{conditionId}Gets one SCHEDULE_RESULT parent row by conditionId (JSON attributes; PK/SK omitted).
GET{baseUrl}/v1/schedule-results/{conditionId}/resolutionResolved optimization output: status, candidates, confirmedAssignments when present. If status is completed and not yet confirmed, rank-1 may be auto-confirmed on this GET (side effect). Use POST …/confirm-assignments for manual confirmation.
GET{baseUrl}/v1/schedule-results/{conditionId}/reviewReview payload for manual confirmation (candidates, requiredByCellKey, staff allowedCellKeys, etc.).
GET{baseUrl}/v1/schedule-results/{conditionId}/emergency-shift-contextEmergency-shift context (calendar dates, staff, slots). Paid subscription required; null when condition missing.
GET{baseUrl}/v1/schedule-conditionsLists SCHEDULE_CONDITION parent rows (conditionId, dates, optional publicApiCancelledAt).
GET{baseUrl}/v1/schedule-conditions/{conditionId}Condition parent row plus reqDays and staffSnapshots (PK/SK stripped).
GET{baseUrl}/v1/audit-logsLists audit log entries (newest first; cursor pagination).
GET{baseUrl}/v1/schedule-defaultsTenant schedule defaults row (SCHEDULE_DEFAULTS) or null if not yet saved.
GET{baseUrl}/v1/schedule-actual/{conditionId}SCHEDULE_ACTUAL row for edited/actual assignments, or null if none.
GET{baseUrl}/v1/staff/{staffId}/weekly-shift-wishWeekly shift wish row for the staff member, or null if none.
GET{baseUrl}/v1/companyReturns Stripe Customer fields (name, email, phone, address) for the customerId on your API key.
GET{baseUrl}/v1/usersLists tenant users (optional: limit, cursor, userName, permissionProfileId, scope fullAccess|nonFullAccess).
GET{baseUrl}/v1/users/{userId}Gets one user by userId in the path.
GET{baseUrl}/v1/staffLists active staff members for the tenant (display name sorted).
GET{baseUrl}/v1/staff/{staffId}Gets one staff member by staffId, including labor details (laborLawCompliance, laborFlsaExempt, personal caps, laborConstraintMask). Soft-deleted rows return 404.

Write endpoints (PATCH / POST / PUT / DELETE)

Use the same API key with JSON bodies. PATCH /v1/company rejects empty strings for fields such as company name (aligned with the web app). PATCH /v1/schedule-defaults saves tenant-wide defaults (requirementsByCellKey, planning horizon, etc.); paid-tier fields follow your subscription. PUT /v1/staff/{staffId}/weekly-shift-wish replaces the entire wish grid for that staff member (see validation note). DELETE /v1/users/{userId} returns 409 if the tenant would have zero active users, or when removing the last admin. POST /v1/staff returns 409 STAFF_LIMIT when the roster would exceed the tenant cap.

POST /v1/users requires userName, email, permissionProfileId (UUID from your tenant permission profiles); optional locale, password. The public API skips the verification-code email on create. PATCH user: include only fields to change; empty strings are rejected when a key is present. POST /v1/staff requires JSON { "displayName": "..." }. PATCH /v1/staff/{staffId} accepts any of displayName, laborLawCompliance, laborFlsaExempt, personalMaxWeeklyMinutes / personalMaxMonthlyMinutes / personalMaxThreeMonthMinutes (integer or null to clear), laborConstraintMask (object or null; only when laborLawCompliance is true). Labor fields require a paid subscription. POST /v1/schedule-results/{conditionId}/emergency-shift: absentStaffIds + absentDatesYmd and/or absentSlots (paid; source schedule must be completed). POST …/confirm-assignments: assignments array and optional relaxAvailability. PATCH /v1/schedule-defaults: see PUBLIC_API_SCHEDULE_REQUEST_BODY.md §12. PUT /v1/staff/{staffId}/weekly-shift-wish: scheduleStartDate, scheduleEndDate, timeRangeStart, timeRangeEnd, wishByCellKey.

MethodPathDescription
PATCH{baseUrl}/v1/companyUpdates company (billing customer) profile fields.
PATCH{baseUrl}/v1/schedule-defaultsSaves tenant schedule defaults (JSON body matches saveSchema in schedule-defaults-actions). Paid-tier fields follow your subscription.
POST{baseUrl}/v1/usersCreates a user. Returns 201 with JSON user.
PATCH{baseUrl}/v1/users/{userId}Updates user fields (userName, email, permissionProfileId, locale).
DELETE{baseUrl}/v1/users/{userId}Deletes a user. 409 CONFLICT if the organization would have no users or last-admin rules apply.
POST{baseUrl}/v1/staffCreates a staff member (displayName). Returns 201 with staff JSON.
POST{baseUrl}/v1/schedule-results/{conditionId}/emergency-shiftClones a completed source schedule with absences applied; returns 201 with new conditionId. Paid subscription required.
POST{baseUrl}/v1/schedule-results/{conditionId}/confirm-assignmentsValidates and saves manually edited assignments (200 with { ok: true }).
PATCH{baseUrl}/v1/staff/{staffId}Updates displayName and labor details (laborLawCompliance, laborFlsaExempt, personal caps, laborConstraintMask) for one staff member.
DELETE{baseUrl}/v1/staff/{staffId}Soft-deletes a staff member (sets deletedAt).
DELETE{baseUrl}/v1/schedule-conditions/{conditionId}Marks the condition as cancelled (publicApiCancelledAt); does not delete child rows.
PUT{baseUrl}/v1/schedule-actual/{conditionId}Saves actual shift assignments JSON { "assignments": [ ... ] } (validated against the condition grid).
PUT{baseUrl}/v1/staff/{staffId}/weekly-shift-wishReplaces weekly wish for the staff member: full grid (1–7 days), wishByCellKey per cell (NONE|LOW|HIGH); granularity from tenant schedule defaults.
POST{baseUrl}/v1/users/{userId}/restoreRestores a soft-deleted user.

Headers

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

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 standard paid two-week detailed planning (wider spans only if your contract allows).
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.
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. Any other key returns UNKNOWN_FIELD.

  • apiVersion
  • timeZone
  • scheduleStartDate
  • scheduleEndDate
  • timeRangeStart
  • timeRangeEnd
  • slotGranularityMinutes
  • requirementsByCell
  • 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, paid optimization flags, and staff-level labor fields are not part of the JSON — the server writes defaults on the parent SCHEDULE_CONDITION row when persisting (see Labor law & paid flags). DynamoDB keys and internal entity types must not appear in the body.

  • apiVersion — Optional. Only the supported schema version string is accepted when present.
  • 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

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.
staffarrayYesNon-empty; max size per limits; each item: staffId, displayName, optional availableCells.

Field reference (allowed values)

The following sections mirror the validator in `app/lib/public-schedule-api/validate.ts` and sanitizers in `sanitize.ts`, using the paid-tier defaults from `PUBLIC_SCHEDULE_SUBMIT_DEFAULTS` unless options are passed explicitly. Prefer sending JSON numbers as actual numbers (not strings). After validation, `build-schedule-transact-items.ts` adds parent-row fields (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 (latest behavior).
  • If present, must be exactly "2026-04-01" (PUBLIC_SCHEDULE_API_VERSION).
  • Any other string is rejected (TYPE_ERROR).

Behavior

  • Compared after trim-equivalent handling in sanitizeApiVersion.

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 typically up to 14 days for two-week planning; exact cap = tenant entitlements).

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.

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: staffId, displayName, availableCells (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.

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 ROOT_KEYS at the top level and STAFF_KEYS on each staff object. Do not send laborLawJurisdiction, usLaborStateCode, laborModelVersion, or paidOptimization.
  • On submit, the server sets the parent SCHEDULE_CONDITION from shared defaults: paidOptimization matches DEFAULT_PAID_OPTIMIZATION — laborLawCompliance false; balanceStaffLoad, honorShiftWishes, and preferConsecutiveShifts true (see schedule-defaults-shared.ts and build-schedule-transact-items.ts). laborLawJurisdiction JP, usLaborStateCode GENERIC, laborModelVersion v1.
  • Statutory-style labor constraints in the optimizer require both parent and per-staff labor compliance flags to be on; this path leaves them off, so do not expect statutory caps to apply from this JSON alone.
  • The labor model is an approximation for scheduling, not legal advice. Catalog: optimizer/config/labor_models.json.

Docs

  • Full detail: docs/data-and-api/PUBLIC_API_SCHEDULE_REQUEST_BODY.md §2.1.1 and §3.1.

If you send disallowed keys

  • UNKNOWN_FIELDStrict mode rejects unknown root or staff properties.

Sanitization rules

Applied before or during validation as implemented in `sanitize.ts`. 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.

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"]
    }
  ]
}

Further reading. Product plans & paid features: docs/architecture/NURSE_SCHEDULING_SERVICE_SPECIFICATION.md. POST /v1/schedule request body (and labor defaults on the parent row): docs/data-and-api/PUBLIC_API_SCHEDULE_REQUEST_BODY.md §1–§11. Tenant schedule defaults PATCH body: same file §12. Implementation: app/lib/public-schedule-api/ (submit), app/features/schedule-settings/schedule-defaults-actions.ts (defaults), app/lib/public-api-write/weekly-shift-wish-tenant.ts (weekly wish). Deployment: docs/aws/CDK_DEPLOY.md (public schedule API).

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