Auto Scheduler
요금플레이그라운드사용 방법블로그자주 묻는 질문API 문서
로그인

이 페이지의 목차

개요대상 범위구독 및 과금인증LLM용 복사엔드포인트사용 방법(빠른 시작)MCP(에이전트)읽기 API (GET)쓰기 API (PATCH / POST / PUT / DELETE)완료 알림 Webhook헤더한도 및 기본값허용 루트 키요청 본문 개요빠른 참조 표필드 레퍼런스(허용 값)apiVersiontimeZonescheduleStartDate / scheduleEndDatetimeRangeStart / timeRangeEndslotGranularityMinutesrequirementsByCellrequiredRolesByCellstaffavailableCellsstaff[].schedulingRoleIds / Soft prefer-avoid노동법 및 유료 옵션(서버 기본값)정리(Sanitization)성공 응답오류 코드 전체JSON 예시추가 참고

Auto Scheduler

유료 API · 구독

개발자 API 레퍼런스

유료 구독에 연결된 API 키로 운영 일정 제출 API에 연동합니다. 테넌트 스케줄 기본값은 PATCH /v1/schedule-defaults로 저장하고, 직원별 주간 희망은 GET/PUT /v1/staff/{staffId}/weekly-shift-wish로 조회·전체 교체할 수 있습니다. 과금은 구독 및 계약에 따릅니다(API 호출 건수 기반 추가 과금 없음). 유료 권한, 검증 규칙, 오류 코드를 정리했으며 사이드바 검색으로 필드로 이동할 수 있습니다.

본 문서의 대상 범위

본 페이지는 외부 연동용 제품 REST API를 설명합니다.

스케줄 결과 완료 Webhook은 API 관리에서 등록하는 아웃바운드 HTTPS 알림이며 REST CRUD가 아닙니다. 페이로드·서명은 본 페이지의 완료 알림 Webhook을 보세요.

사이트 내 브라우저용 경로(결제 콜백·측정 등)는 이 제품 API를 대체하지 않습니다. 외부 시스템은 본 페이지의 /v1/*와 완료 알림 Webhook을 사용하세요.

구독 과금 및 액세스

본 HTTP API는 유료 제품의 일부입니다. 활성 상업 계약과 온보딩 후 조직에 발급된 키가 필요합니다.

과금은 구독 및 계약에 기반합니다. 유료 플랜의 청구는 가격표에 따라 등록 직원 수(석)에 비례하는 월액이 될 수 있습니다.

플레이그라운드/무료 한도는 유료 API 키에 적용되지 않습니다. 일정 기간, 직원 수, 15분 단위 사용 여부 등은 유료 권한으로 결정됩니다. 자세한 내용은 본 페이지의 한도 및 기본값을 참고하세요.

인증

유료 구독으로 발급된 유효한 API 키를 요청에 포함해야 합니다. 로그인 후 계정 영역에서 키를 생성·교체·폐기할 수 있습니다.

키는 x-api-key 헤더로 전송합니다. 키는 조직에 연결되어 테넌트 식별 및 플랜 기반 이용 한도에 사용됩니다. 키 scopes 밖 작업은 403(API_KEY_SCOPE_DENIED)입니다. scopes 속성이 없는 기존 키는 모든 작업이 허용됩니다.

키 관리: API 관리

Auto Scheduler

근무표 최적화와 자동 스케줄링을 지원하는 클라우드 서비스. 규칙과 제약을 반영해 공정하고 운용하기 쉬운 근무표 작성을 돕습니다.

이용약관|개인정보 처리방침|전자상거래법에 따른 표기

솔버

LLM용 복사

MCP를 설정하지 않고 채팅형 LLM에 이 제품 API 전제를 전달할 때 아래 블록을 붙여넣으세요.

base URL·인증·주요 작업·쓰기 주의사항을 요약합니다. 실제 API 키는 포함하지 않습니다.

에이전트에서 API를 실제로 호출하려면 이 붙여넣기만이 아니라 MCP(에이전트) 를 설정하세요.

붙여넣을 컨텍스트

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.

엔드포인트

일정 조건을 JSON으로 제출합니다. 유료 연동 표면이며, API 액세스가 부여된 뒤 환경별로 기본 URL이 안내됩니다.

기본 URL

공개 REST API(/v1/*) 호스트이며 브라우저 사이트 URL이 아닙니다. MCP 환경 변수 AUTO_SCHEDULER_PUBLIC_API_BASE_URL과 curl BASE_URL에 이 값을 설정하세요.

AUTO_SCHEDULER_PUBLIC_API_BASE_URL — 이 문서 환경에 설정된 기본 URL입니다. MCP와 curl에 같은 값을 사용하세요.

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

API 액세스 개통 시 또는 운영 담당자가 안내한 기본 URL을 사용하세요.

사용 방법(빠른 시작)

**BASE_URL**에는 액세스 개통 시 안내된 API 기본 URL(경로 접두사)을 넣습니다.

API 키는 **x-api-key** 헤더로 보냅니다. 조직(테넌트)은 키로 식별되므로 쿼리 문자열에 별도 테넌트 ID를 넘길 필요가 없습니다.

유료 기능(긴급 시프트·노무 상세·일부 스케줄 기본값 등)에는 유효한 유료 구독이 필요합니다. 테넌트에 결제가 구성되지 않은 경우 **503**(예: 코드 **STRIPE_NOT_CONFIGURED**)을 받을 수 있습니다. 회사 정보·감사 로그·사용자(계정)·회사 정보·감사 로그는 웹 앱 소관이며 공개 API에 포함되지 않습니다.

curl 예 (bash / macOS / Linux / WSL)

Windows PowerShell: 줄 끝의 `\`는 **줄 이어쓰기가 아닙니다**. 한 줄로 쓰거나, 줄 끝을 **백틱(`)**으로 이어 주세요.

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(에이전트)

Cursor, Claude, Gemini, GPT 같은 에이전트에서 MCP로 동일한 제품 REST API를 호출할 수 있습니다. 작업은 이 페이지의 /v1과 같습니다.

동봉 MCP 서버는 로컬 stdio를 사용합니다. 각 클라이언트 설정 파일에 command / args / env를 추가하세요. API 관리에서 키를 발급하세요(공개 채팅·저장소에 올리지 마세요).

문서만 볼 경우 Resources MCP는 로그인·API 키가 필요 없습니다(mcp/api-docs). 이 페이지(/{locale}/api-docs)도 로그인 없이 볼 수 있으며, 키 발급은 계정 → API 관리가 필요합니다.

가장 빠른 경로(권장)

  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.

무엇을 쓸지

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

필수 환경 변수(Tools MCP)

  • AUTO_SCHEDULER_PUBLIC_API_BASE_URL

    공개 REST API(/v1/*)의 기본 URL—웹사이트 도메인이 아닙니다. 안내받은 API 호스트를 사용하세요(끝 슬래시 선택).

  • AUTO_SCHEDULER_API_KEY

    API 관리에서 발급한 키(커밋하거나 공개하지 마세요)

변수설명
AUTO_SCHEDULER_PUBLIC_API_BASE_URL공개 REST API(/v1/*)의 기본 URL—웹사이트 도메인이 아닙니다. 안내받은 API 호스트를 사용하세요(끝 슬래시 선택).
AUTO_SCHEDULER_API_KEYAPI 관리에서 발급한 키(커밋하거나 공개하지 마세요)

환경 변수 스니펫(복사)

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

공통 준비

  1. API 관리에서 API 키 발급(Tools MCP만; 문서 MCP에는 불필요)
  2. 저장소에서 cd mcp/public-api → npm install && npm run build(Resources가 필요하면 mcp/api-docs도)
  3. 아래 클라이언트별 단계를 따르고 경로와 env를 환경에 맞게 조정하세요
  4. 클라이언트를 재시작(또는 MCP 재연결)하고 tools / resources가 보이는지 확인하세요

클라이언트별 설정

JSON 형태는 거의 같습니다. 차이는 설정 파일 위치와 ChatGPT(GPT)가 로컬 stdio 서버를 실행하지 않는다는 점입니다.

Cursor

사용자 mcp.json(예: Windows %USERPROFILE%\.cursor\mcp.json). Cursor Settings → MCP에서도 편집할 수 있습니다.

  1. mcpServers 아래에 아래 스니펫을 추가하세요(기존 서버 유지)
  2. 절대 경로와 YOUR_BASE_URL / YOUR_API_KEY를 바꾸세요
  3. Cursor를 재시작하고 list_staff 같은 Tools와 api-docs Resources가 보이는지 확인하세요

Windows에서는 JSON에서 백슬래시를 \\로 이스케이프하세요. macOS / Linux는 / 경로를 사용하세요.

{
  "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는 Tools(API 키 필요)를, api-docs는 Resources(키 불필요)를 제공합니다.

Claude(Desktop)

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

  1. claude_desktop_config.json의 mcpServers에 아래 스니펫을 추가하세요
  2. 절대 경로와 환경 변수를 환경에 맞게 조정하세요
  3. Claude Desktop을 완전히 종료한 뒤 재시작하고 Connectors / 도구를 확인하세요

Cursor와 같은 mcpServers 형식입니다. Anthropic의 "Connect to local MCP servers" 흐름을 따르세요.

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

Claude Code를 쓰면 프로젝트 .mcp.json 또는 claude mcp add로도 등록할 수 있습니다.

Gemini(CLI)

사용자: ~/.gemini/settings.json / 프로젝트: .gemini/settings.json(둘 다 있으면 프로젝트 우선)

  1. mcpServers에 스니펫을 추가하세요(또는 gemini mcp add)
  2. 하이픈 구분 서버 이름을 권장합니다(Gemini CLI 제약)
  3. CLI를 시작하고 /mcp로 연결·도구를 확인하세요

command / args / env는 Cursor·Claude와 같습니다. 선택 필드: 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 클라우드 콘솔 커넥터는 원격 MCP용입니다. 로컬 stdio는 Gemini CLI를 사용하세요.

GPT(ChatGPT / OpenAI)

ChatGPT 커넥터 설정(원격 MCP만). ChatGPT용 로컬 mcp.json은 없습니다.

  1. ChatGPT(웹 / 데스크톱 커넥터)는 로컬 stdio MCP 프로세스를 실행할 수 없습니다
  2. 본 제품은 로컬 stdio MCP만 제공하므로 ChatGPT에서 직접 연결할 수 없습니다
  3. GPT급 에이전트 사용: (1) Cursor / Claude / Gemini CLI에서 같은 MCP를 실행하거나 (2) Custom Actions / API 클라이언트에서 x-api-key로 이 페이지의 REST API를 호출

원격 HTTPS MCP를 직접 호스팅하는 경우에만 ChatGPT 커넥터를 등록할 수 있습니다(표준 제품 제공 아님).

OpenAI Agents / Responses API도 원격 MCP(HTTP)를 전제로 합니다. 동봉 서버는 stdio 클라이언트용입니다.

도구 개요(public-api)

읽기·쓰기 도구가 있습니다. 쓰기는 과금 작업을 시작하거나 조직 설정을 바꿀 수 있으니 호출 전에 확인하세요.

읽기

  • list_schedule_results / get_schedule_result(결과)
  • list_schedule_conditions(조건)
  • list_staff / get_staff(직원)
  • get_schedule_defaults(스케줄 기본값)
  • get_emergency_shift_context

쓰기(먼저 확인)

  • submit_schedule(스케줄 작업 실행; 요금 발생 가능)
  • update_schedule_defaults(조직 전체 기본값)
  • create_staff / update_staff(직원)
  • upsert_weekly_shift_wish(주간 희망 전체 교체)
  • submit_emergency_shift
  • confirm_schedule_assignments
  • cancel_schedule_condition
  • put_schedule_actual

Confirm destructive writes before calling. Docs MCP (api-docs) is Resources-only.

키 발급: API 관리

읽기 엔드포인트 (GET)

POST /v1/schedule와 동일한 베이스 URL·API 키입니다. 각 카드에 계약 초안으로 쓸 수 있는 최소 응답 JSON 예가 있습니다.

목록은 쿼리 limit(1–100, 기본 50)과 cursor(이전 nextCursor의 불투명 토큰)를 받습니다. 다음 페이지가 없으면 nextCursor는 생략됩니다.

  • GET{baseUrl}/v1/schedule-results

    스케줄 결과 부모 행 요약 목록(최신 index 순).

    쿼리: limit, cursor.

    응답 예

    {
      "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

    최근+아카이브를 통합한 이력 검색(브라우저 이력과 동일 형태).

    쿼리: from, to(ISO 8601; 기본 서비스 시작→현재), keyword, eventName(INSERT,MODIFY,REMOVE 쉼표 구분), limit, cursor.

    응답 예

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

    스케줄 결과 부모 행 1건(PK/SK 제거).

    응답 예

    {
      "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

    조건의 해석된 최적화 결과.

    status가 completed이고 미확정이면 이 GET에서 rank-1이 자동 확정될 수 있습니다(부작용). 명시 확정은 POST …/confirm-assignments.

    응답 예

    {
      "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": {
        "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

    긴급 시프트 요청을 만들기 위한 컨텍스트. 유료 구독 필요.

    조건이 없으면 emergencyShiftContext는 null.

    응답 예

    {
      "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

    스케줄 조건 index 요약 목록.

    쿼리: limit, cursor.

    응답 예

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

    조건 부모 행 + reqDays + staffSnapshots(PK/SK 제거).

    응답 예

    {
      "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

    테넌트 스케줄 기본값. 미저장이어도 HTTP 200이며 scheduleDefaults: null.

    응답 예

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

    조건의 실적 배정. 없으면 actual: null.

    응답 예

    {
      "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

    직원 1명의 주간 희망. 없으면 weeklyShiftWish: null. 논리 삭제 직원은 404.

    응답 예

    {
      "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

    테넌트 활성 직원 목록(displayName 순).

    응답 예

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

    직원 1건(노무/Soft 필드 포함 가능). 논리 삭제 → 404.

    응답 예

    {
      "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": []
      }
    }

쓰기 엔드포인트 (PATCH / POST / PUT / DELETE)

다른 /v1 라우트와 동일한 API 키와 JSON 본문입니다. 각 카드에 복사 가능한 최소 요청 본문 예가 있습니다. 계정 사용자·회사 프로필·감사 로그는 웹 앱 관할이며 이 API에 포함되지 않습니다. POST /v1/staff는 명단이 테넌트 상한을 넘으면 409 STAFF_LIMIT을 반환합니다.

노무 및 Soft prefer/avoid 필드는 유료 구독이 필요합니다. 예제의 UUID·날짜·셀 키를 테넌트 실데이터로 바꾸세요.

  • PATCH{baseUrl}/v1/schedule-defaults

    테넌트 스케줄 기본값을 저장합니다. 유료 항목은 구독에 따릅니다.

    requirementsByCellKey는 기간×슬롯의 모든 셀이 필요합니다(누락/초과 → 400). 셀 키: YYYY-MM-DD__HH:mm. planningHorizon: week | two_weeks(month_attendance 거부). 선택: requiredRolesByCellKey, laborLawJurisdiction, usLaborStateCode, linkedMonthPlanStartDate / linkedMonthPlanEndDate, laborModelVersion.

    요청 본문 예

    {
      "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

    직원을 생성합니다. 201과 staff JSON.

    displayName만 허용됩니다.

    요청 본문 예

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

    완료된 참조 시프트를 복제하고 결근을 반영합니다. 201과 새 conditionId. 유료 구독 필요.

    결근은 absentDayPairs 권장. 호환: absentStaffIds + absentDatesYmd(데카르트). absentSlots도 가능. 선택 substituteDayPairs / substituteSlots(참조 시프트 완료 필요).

    요청 본문 예

    {
      "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

    수동 편집 배정을 검증·저장합니다. 200과 { ok: true }.

    assignments 필수. relaxAvailability 선택(boolean).

    요청 본문 예

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

    직원 1명을 업데이트합니다. 예제 필드를 임의 조합으로 보낼 수 있습니다.

    개인 상한은 정수 또는 null로 해제. laborConstraintMask는 laborLawCompliance가 true일 때만. schedulingRoleIds / Soft prefer-avoid는 UUID 배열(카탈로그 외 필터, prefer·avoid 중복 불가). 노무·Soft는 유료.

    요청 본문 예

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

    직원을 논리 삭제합니다(deletedAt 설정).

    요청 본문 없음.

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

    조건을 취소합니다(publicApiCancelledAt). 자식 행은 삭제하지 않습니다.

    요청 본문 없음.

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

    실적 배정을 저장합니다(조건 그리드 검증).

    배정 객체 형태는 confirm-assignments와 같습니다.

    요청 본문 예

    {
      "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

    직원 1명의 주간 희망 그리드를 전체 교체합니다.

    날짜·시간대는 테넌트 주간 희망 내비 기간(스케줄 기본값)과 일치해야 합니다. wishByCellKey는 해당 기간 전 셀 필수. 값: NONE | LOW | HIGH.

    요청 본문 예

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

완료 알림 Webhook

스케줄 결과가 종료 상태에 도달하면 HTTPS URL로 서명된 JSON 알림을 POST합니다. 본문에는 전체 결과가 없으며 GET /v1/schedule-results/{conditionId}로 상세를 가져오세요.

플레이그라운드 결과는 전달되지 않습니다. webhook이 비활성이거나 URL이 없으면 전송하지 않습니다.

설정

  • 로그인 후 API 관리를 열고 HTTPS 알림 URL을 저장하세요.
  • 「알림 사용」을 켭니다.
  • 한 번만 표시되는 서명 시크릿을 복사하세요(재표시 불가; 분실 시 재생성).
  • 수신측에서 서명을 검증한 뒤 필요하면 API 키로 links.result를 GET하세요.

이벤트

이벤트 유형과 전송 시점.

  • schedule.result.terminal

    스케줄 결과 상태가 completed, no_solution, failed, error 또는 canceled가 될 때

type시점
schedule.result.terminal스케줄 결과 상태가 completed, no_solution, failed, error 또는 canceled가 될 때

요청 본문

status는 종료 값만. 본문에 customerId는 포함되지 않음.

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

요청 헤더

  • Content-Type

    application/json

  • User-Agent

    AutoScheduler-Webhook/1.0

  • X-AutoScheduler-Timestamp

    Unix 초(문자열)

  • X-AutoScheduler-Signature

    v1=<hex>(아래 검증 참고)

헤더설명
Content-Typeapplication/json
User-AgentAutoScheduler-Webhook/1.0
X-AutoScheduler-TimestampUnix 초(문자열)
X-AutoScheduler-Signaturev1=<hex>(아래 검증 참고)

서명 검증

원시 본문(JSON 파싱 전)과 Timestamp로 다이제스트를 계산해 Signature와 비교하세요. |now − timestamp|가 300초(5분)를 초과하면 거부하세요(더 넓은 시계 오차를 의도적으로 허용하지 않는 한).

HMAC-SHA256(signingSecret, `${timestamp}.${rawBody}`) → hex; 헤더 값은 v1=<hex>

재시도

5xx 또는 타임아웃 시 자동 재시도합니다. 지속 실패는 데드레터 큐로 갑니다. 브라우저 푸시 알림은 별도 경로입니다.

등록 및 활성화: API 관리

헤더

  • x-api-key

    유료 구독에 연결된 API 키(테넌트 및 플랜 한도 식별).

  • Content-Type

    application/json

헤더설명
x-api-key유료 구독에 연결된 API 키(테넌트 및 플랜 한도 식별).
Content-Typeapplication/json

응답 헤더

일부 응답 헤더(요청 ID 등)는 추적용 메타데이터이며 API 키 자체가 아닙니다. 회사·사용자 데이터가 포함된 JSON **본문**은 기밀로 다루세요.

유료 한도(권한)

계약에 따른 일반적인 검증 한도(15분 단위, 일정 기간, 등록 직원 수 및 관련 상한).

  • 과금 모델

    Stripe 구독; 제품 요금은 일반적으로 가격 페이지와 같이 등록 직원(시트) 수에 비례합니다(API 호출 건당 과금 아님).

  • maxScheduleDays

    유료 2주 상세 계획에서 제출당 최대 14일(고정 상한; 계약으로 연장하지 않음).

  • maxStaffPerSchedule

    POST /v1/schedule의 staff 배열 최대 500행(PUBLIC_SCHEDULE_MAX_STAFF; 본문의 인라인 staffId).

  • maxRosterStaff

    POST /v1/staff 명부 등록 최대 30명(STAFF_ROSTER_MAX_PAID); 초과 시 409 STAFF_LIMIT.

  • allowedGranularities

    [60, 15] — 15분 슬롯은 유료 기능, 60분(시간)도 지원.

  • maxPayloadBytes

    524288(512 KiB) UTF-8, 더 높은 상한이 없는 한.

  • strictUnknownRootKeys

    true — 알 수 없는 최상위 또는 staff 키는 거부

설정유료 API(일반적)
과금 모델Stripe 구독; 제품 요금은 일반적으로 가격 페이지와 같이 등록 직원(시트) 수에 비례합니다(API 호출 건당 과금 아님).
maxScheduleDays유료 2주 상세 계획에서 제출당 최대 14일(고정 상한; 계약으로 연장하지 않음).
maxStaffPerSchedulePOST /v1/schedule의 staff 배열 최대 500행(PUBLIC_SCHEDULE_MAX_STAFF; 본문의 인라인 staffId).
maxRosterStaffPOST /v1/staff 명부 등록 최대 30명(STAFF_ROSTER_MAX_PAID); 초과 시 409 STAFF_LIMIT.
allowedGranularities[60, 15] — 15분 슬롯은 유료 기능, 60분(시간)도 지원.
maxPayloadBytes524288(512 KiB) UTF-8, 더 높은 상한이 없는 한.
strictUnknownRootKeystrue — 알 수 없는 최상위 또는 staff 키는 거부

셀당 필요 인원은 0~15의 정수입니다. 요청 빈도 한도를 초과하면 오류 응답이 반환될 수 있습니다.

허용 루트 키(엄격 모드)

엄격 모드가 켜져 있으면 다음 최상위 키만 허용됩니다. 그 외는 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

요청 본문(개요)

페이로드는 유료 공개 일정 요청 모델을 따릅니다: 달력 날짜, 시간대, 슬롯 그리드(유료 API에서 60분 또는 15분), 셀별 필요 인원, 직원 가용성. 노동법 관할은 JSON에 없습니다. paidOptimization은 apiVersion 2026-09-13+에서 선택(생략 시 테넌트 기본값). Soft prefer/avoid 직원 필드는 2026-09-14 필요. 「노동법 및 유료 옵션」 참고. DynamoDB 키와 내부 엔티티 유형은 본문에 넣지 마세요.

  • apiVersion — 선택. 생략 또는 "2026-04-01" = 레거시. "2026-09-09" = 역할; "2026-09-13" = 역할 + paidOptimization; "2026-09-14" = Soft prefer/avoid. 최신: 2026-09-14.
  • cellKey 형식 — requirementsByCell과 availableCells의 키는 범위 내 YYYY-MM-DD 날짜와 시간 범위·분할에서 열거된 slotId로 만든 `${date}__${slotId}`와 일치해야 합니다(플레이그라운드 `cellKey`와 동일).

빠른 참조 표

  • timeZone

    타입
    string
    필수
    예

    날짜와 슬롯 해석에 사용하는 IANA 시간대.

  • scheduleStartDate / scheduleEndDate

    타입
    string (YYYY-MM-DD)
    필수
    예

    포함 달력 범위, 시작 ≤ 종료.

  • timeRangeStart / timeRangeEnd

    타입
    string (HH:mm)
    필수
    예

    슬롯 열을 정의하는 시계 범위, 최소 1개 슬롯 생성.

  • slotGranularityMinutes

    타입
    60 | 15
    필수
    예

    유료 API: 60(시간) 또는 15(15분). allowedGranularities에 포함.

  • requirementsByCell

    타입
    object
    필수
    예

    키 = 계산된 그리드의 cellKey, 값 = 0~15 정수.

  • requiredRolesByCell

    타입
    object
    필수
    아니오 — 2026-09-09만

    cellKey → roleId→need. 셀별 need 합계 ≤ requirementsByCell. 레거시 버전 → UNKNOWN_FIELD.

  • staff

    타입
    array
    필수
    예

    비어 있지 않음, 최대 크기는 한도 참고, 각 항목: staffId, displayName, 선택 availableCells / laborConstraintMask / schedulingRoleIds (2026-09-09+) / Soft prefer-avoid (2026-09-14).

필드타입필수비고
timeZonestring예날짜와 슬롯 해석에 사용하는 IANA 시간대.
scheduleStartDate / scheduleEndDatestring (YYYY-MM-DD)예포함 달력 범위, 시작 ≤ 종료.
timeRangeStart / timeRangeEndstring (HH:mm)예슬롯 열을 정의하는 시계 범위, 최소 1개 슬롯 생성.
slotGranularityMinutes60 | 15예유료 API: 60(시간) 또는 15(15분). allowedGranularities에 포함.
requirementsByCellobject예키 = 계산된 그리드의 cellKey, 값 = 0~15 정수.
requiredRolesByCellobject아니오 — 2026-09-09만cellKey → roleId→need. 셀별 need 합계 ≤ requirementsByCell. 레거시 버전 → UNKNOWN_FIELD.
staffarray예비어 있지 않음, 최대 크기는 한도 참고, 각 항목: staffId, displayName, 선택 availableCells / laborConstraintMask / schedulingRoleIds (2026-09-09+) / Soft prefer-avoid (2026-09-14).

필드 레퍼런스(허용 값)

아래 필드 규칙은 공개 API 검증과 일치합니다. JSON 숫자는 실제 number로 보내세요(문자열 금지). 검증 후 서버가 요청 키가 아닌 부모 행 기본값(유료 최적화·노동법 관할·모델 버전)을 저장할 수 있습니다 — 「노동법 및 유료 플래그」 참고.

apiVersion

string | 생략 · 필수: 아니요 — 선택

허용 값

  • 생략: 허용(최신 동작).
  • 있을 경우 "2026-04-01", "2026-09-09", "2026-09-13", "2026-09-14". 최신 게시 버전은 2026-09-14.
  • 그 외 문자열은 거부(TYPE_ERROR).

동작

  • sanitizeApiVersion에서 trim 등 처리 후 비교.
  • 버전에 따라 허용되는 최상위 및 직원 객체 키 집합이 달라집니다(역할 2026-09-09+; paidOptimization 2026-09-13+; Soft prefer/avoid 2026-09-14).

대표 오류

  • TYPE_ERROR지원하지 않는 apiVersion 값.

timeZone

string · 필수: 예

허용 값

  • 와이어 형식은 단일 문자열. API는 모든 허용 문자열을 닫힌 enum으로 제공하지 않으며 서버가 동적으로 검증합니다.
  • 검증은 코드베이스의 `isValidIanaTimeZone`과 동일: Luxon `DateTime.now().setZone(zone).isValid`가 true여야 합니다.
  • IANA 표준 식별자를 사용하세요. 예: `Asia/Tokyo`, `America/New_York`, `Europe/Berlin`, `UTC`.
  • `EST`, `JST`, `GMT` 등 약어만으로는 안정적인 IANA ID가 아니며 거부될 수 있습니다.
  • 목록 참고: IANA 배포(https://www.iana.org/time-zones) 또는 플랫폼 시간대 API.

동작

  • 하류 정규화에서 날짜·슬롯 시간에 사용.
  • 클라이언트에 고정 목록이 필요하면 같은 규칙(IANA ID)으로 도출하고, JSON 스키마의 서버 enum에 의존하지 마세요.

대표 오류

  • INVALID_TIME_ZONE누락, 빈 값, 또는 유효하지 않은 IANA 구역.

scheduleStartDate, scheduleEndDate

string, string · 필수: 예 — 둘 다

허용 값

  • /^\d{4}-\d{2}-\d{2}$/(trim 후)와 일치.
  • 실제 달력 날짜, 포함 범위가 enumerateDates로 최소 1일.
  • 범위 일수가 maxScheduleDays 이하(유료 API는 2주 상세 계획 최대 14일·고정 상한).

동작

  • scheduleStartDate ≤ scheduleEndDate.

대표 오류

  • INVALID_DATE_RANGE형식 오류, 잘못된 날짜, 빈 범위 등.
  • SCHEDULE_SPAN_TOO_LONGmaxScheduleDays 초과.

timeRangeStart, timeRangeEnd

string, string · 필수: 예 — 둘 다

허용 값

  • 비어 있지 않은 문자열(trim), UI와 동일한 슬롯 열거(`enumerateSlotsByGranularity`).
  • 쌍이 최소 1개 슬롯을 만들어야 하며, 아니면 검증 실패.

동작

  • slotGranularityMinutes와 함께 cellKey의 slotId 정의.

대표 오류

  • INVALID_TIME_RANGE값 누락 또는 범위에 슬롯 0개.

slotGranularityMinutes

number(JSON 숫자) · 필수: 예

허용 값

  • 정확히 60 또는 15(문자열 아님).
  • allowedGranularities에도 있어야 함. 유료 API에서는 보통 둘 다 허용, 테넌트에서 15가 꺼져 있으면 INVALID_GRANULARITY.

동작

  • 시간 범위와 함께 슬롯 개수 정의.

대표 오류

  • INVALID_GRANULARITY60/15가 아니거나 플랜에서 불허.

requirementsByCell

Record<string, number> · 필수: 예

허용 값

  • 평범한 JSON 객체(배열 아님).
  • 각 키는 예상 그리드(날짜×slotId)의 cellKey. 알 수 없는 키 → UNKNOWN_CELL_KEY.
  • 각 값은 JSON 숫자 정수 0~15.
  • 생략 셀은 필요 0으로 간주(SHORTFALL은 필요≥1인 셀만).

동작

  • 키는 sanitizeCellKey로 trim 후 비교.

대표 오류

  • INVALID_REQUIREMENTS객체가 아니거나 키의 숫자가 잘못됨.
  • UNKNOWN_CELL_KEY계산된 날짜×슬롯 그리드 밖 키.

requiredRolesByCell

object | omitted · 필수: 아니오 — 선택; apiVersion 2026-09-09만

허용 값

  • apiVersion이 "2026-09-09"일 때만 허용. 생략 / 2026-04-01 → UNKNOWN_FIELD.
  • 일반 JSON 객체여야 합니다. 각 키는 requirements 격자의 cellKey입니다.
  • 각 값은 roleId(UUID) → 정수 need ≥ 1 매핑 객체입니다.
  • 각 셀에서 sum(need) ≤ requirementsByCell[cell](생략 시 0)이어야 합니다.
  • 제출 경로는 UUID 형태와 합계만 검증합니다(카탈로그 조회 없음).

동작

  • 값을 정규화·검증합니다(역할 ID는 UUID; need 합계 ≤ 인원).
  • 일별 필요 인원 행의 requiredRolesBySlot으로 저장됩니다.

대표 오류

  • UNKNOWN_FIELD레거시 apiVersion에서 전송됨.
  • INVALID_REQUIREMENTS잘못된 형태, UUID가 아닌 roleId, 또는 합계가 인원을 초과.
  • UNKNOWN_CELL_KEYcellKey가 계산된 격자에 없음.

staff

array · 필수: 예 — 비어 있지 않은 배열

허용 값

  • 길이 1~maxStaffPerSchedule(POST /v1/schedule 기본 500; POST /v1/staff 명부 상한과 별도).
  • 각 요소는 apiVersion별 허용 키만: staffId, displayName, availableCells, laborConstraintMask; schedulingRoleIds는 2026-09-09+; preferredSchedulingRoleIds / avoidedSchedulingRoleIds는 2026-09-14(엄격 모드).
  • staffId·displayName은 정리 통과 필요(정리 섹션).
  • staffId는 배열 내 중복 불가(DUPLICATE_STAFF_ID).

동작

  • 순서는 가용성 집계에 유지됨.

대표 오류

  • INVALID_STAFF빈 배열, 초과, 잘못된 객체, 정리 실패 등.
  • UNKNOWN_FIELD엄격 모드에서 staff 객체에 알 수 없는 키.
  • DUPLICATE_STAFF_IDstaffId 중복.

staff[].availableCells

string[] | 생략 · 필수: 아니요 — 선택

허용 값

  • 생략: 해당 직원은 그리드 전 셀에 근무 가능(내부적으로 null «전체 셀»).
  • 있을 때: JSON 문자열 배열, 각 비어 있지 않은 문자열은 예상 그리드의 cellKey.
  • 배열 안 빈 문자열은 INVALID_AVAILABLE_CELL.

SHORTFALL 검사

  • 필요≥1인 각 셀에서 가용으로 세는 직원 수가 필요 이상이어야 하며, 아니면 SHORTFALL_CELLS.

대표 오류

  • INVALID_AVAILABLE_CELL알 수 없는 셀 키 또는 빈 항목.
  • SHORTFALL_CELLS필요 인원 대비 가용 인원 부족.

staff[].schedulingRoleIds / Soft prefer-avoid

string[] | omitted · 필수: 아니오 — 선택; 역할 2026-09-09+; Soft 2026-09-14

허용 값

  • schedulingRoleIds: apiVersion 2026-09-09+에서 허용. Soft preferredSchedulingRoleIds / avoidedSchedulingRoleIds: 2026-09-14만. 이전 버전 → UNKNOWN_FIELD.
  • UUID 문자열 JSON 배열. 중복 제거. Soft prefer ∩ avoid는 비어 있어야 함.
  • 빈 배열 / 생략 = 조건 스냅샷에 Hard 역할 프레임 / Soft 선호 없음.
  • 제출 경로는 테넌트 카탈로그 필터 없음. PATCH /v1/staff/{staffId}는 필터(Soft PATCH 유료).

동작

  • 값을 정규화합니다(UUID 문자열; 중복 제거). Soft 겹침 → INVALID_STAFF.
  • 비어 있지 않으면 직원 조건 스냅샷 행에 복사됩니다.

대표 오류

  • UNKNOWN_FIELD키를 허용하지 않는 apiVersion에서 전송됨.
  • INVALID_STAFF배열 아님, UUID 아닌 요소, 또는 Soft prefer/avoid 겹침.

노동법 및 유료 최적화(JSON 본문에 없음)

해당 없음 — 서버에 저장 · 요청에 포함: 아니요 — 이 키는 보내지 않음

부모 행에 저장되는 내용

  • 공개 API는 apiVersion별 허용 최상위 키와 staff 객체 키만 받습니다. laborLawJurisdiction, usLaborStateCode, laborModelVersion은 보내지 마세요. paidOptimization은 apiVersion 2026-09-13+에서 선택. Soft prefer/avoid 직원 필드는 2026-09-14 필요.
  • 제출 시 서버가 공유 기본값으로 부모 SCHEDULE_CONDITION을 설정합니다. paidOptimization은 본문(apiVersion 2026-09-13+) 또는 생략 시 테넌트 SCHEDULE_DEFAULTS; 없으면 DEFAULT_PAID_OPTIMIZATION(부모 laborLawCompliance는 항상 true). laborLawJurisdiction / usLaborStateCode / laborModelVersion은 테넌트 SCHEDULE_DEFAULTS를 상속(없으면 JP / GENERIC / 관할에 맞는 모델 버전).
  • 옵티마이저의 법정 스타일 노동 제약은 부모·직원 모두 근무 준수 플래그가 켜져 있을 때만 적용됩니다. 직원 플래그는 JSON 본문에 없고, 서버가 테넌트 명단에서 상속합니다(명단에 없는 staffId는 기본 켜짐). POST /v1/staff 생성도 기본 켜짐. 인원별 끄기는 유료 PATCH /v1/staff/{staffId}.
  • 노동 모델은 일정용 근사이며 법률 자문이 아닙니다. 카탈로그: .

문서

  • 자세한 내용: this page 및 §3.1.

허용되지 않은 키를 보낸 경우

  • UNKNOWN_FIELD엄격 모드가 알 수 없는 루트 또는 staff 속성을 거부.

정리 규칙

검증 전후에 적용됩니다. 값이 INVALID_STAFF로 거절되는 이유를 설명합니다.

staffId

  • trim, 길이 1~128.
  • 문자는 /^[a-zA-Z0-9._-]+$/ (슬래시·백슬래시 불가).

displayName

  • 제어 문자·제로폭 제거, 공백 축소, trim.
  • 괄호 < > 불가.
  • 정리 후 최대 128자, 비어 있으면 안 됨.

셀 키(requirements / availableCells)

  • 키는 trim 후 계산된 그리드의 cellKey 문자열과 정확히 일치.

성공 응답

202 Accepted

본문이 검증을 통과하고 조건이 기록되었으며, 최적화는 비동기로 계속될 수 있습니다. 핸들러가 conditionId 등이 포함된 JSON을 반환할 수 있습니다.

오류 코드 전체

검증 오류는 안정적인 `code`가 있는 구조화된 정보를 반환합니다. 검증 전에 JSON 파싱(INVALID_JSON, PAYLOAD_TOO_LARGE). 저장 실패는 DYNAMODB_ERROR로 나올 수 있습니다. PATCH /v1/schedule-defaults는 실패 시 HTTP 400과 SCHEDULE_DEFAULTS_SAVE_FAILED(상세는 details)를 반환할 수 있습니다.

  • INVALID_JSON

    본문이 유효한 JSON이 아님.

  • PAYLOAD_TOO_LARGE

    UTF-8 바이트 길이가 maxPayloadBytes(기본 512 KiB) 초과.

  • TYPE_ERROR

    잘못된 JSON 타입 또는 지원하지 않는 apiVersion 문자열.

  • UNKNOWN_FIELD

    엄격 모드: 루트 또는 staff 객체에 허용되지 않은 속성.

  • INVALID_TIME_ZONE

    timeZone 누락 또는 유효한 IANA 이름이 아님.

  • INVALID_DATE_RANGE

    YYYY-MM-DD가 아님, 잘못된 범위, 빈 열거.

  • SCHEDULE_SPAN_TOO_LONG

    시작~종료 사이 일수가 maxScheduleDays 초과.

  • INVALID_TIME_RANGE

    시간 범위 누락 또는 슬롯 0개.

  • INVALID_GRANULARITY

    slotGranularityMinutes가 60/15가 아니거나 플랜에서 불허.

  • INVALID_REQUIREMENTS

    requirementsByCell이 객체가 아니거나 값이 0~15 정수가 아님.

  • UNKNOWN_CELL_KEY

    계산된 일정 그리드에 없는 키.

  • INVALID_STAFF

    staff가 비어 있음, 너무 큼, 잘못된 행, 정리 실패 등.

  • DUPLICATE_STAFF_ID

    staffId 중복.

  • INVALID_AVAILABLE_CELL

    availableCells의 빈 항목 또는 알 수 없는 항목.

  • SHORTFALL_CELLS

    필요 인원 대비 가용 인원 부족.

  • DYNAMODB_ERROR

    검증 후 일시적 또는 저장 오류(핸들러에 따라 다름).

  • SCHEDULE_DEFAULTS_SAVE_FAILED

    PATCH /v1/schedule-defaults: 검증 또는 비즈니스 규칙 실패(error.details 참고).

  • LABOR_COMPLIANCE_REQUIRED

    PATCH /v1/staff/{staffId}: laborLawCompliance가 false일 때 laborConstraintMask를 보냄.

코드의미
INVALID_JSON본문이 유효한 JSON이 아님.
PAYLOAD_TOO_LARGEUTF-8 바이트 길이가 maxPayloadBytes(기본 512 KiB) 초과.
TYPE_ERROR잘못된 JSON 타입 또는 지원하지 않는 apiVersion 문자열.
UNKNOWN_FIELD엄격 모드: 루트 또는 staff 객체에 허용되지 않은 속성.
INVALID_TIME_ZONEtimeZone 누락 또는 유효한 IANA 이름이 아님.
INVALID_DATE_RANGEYYYY-MM-DD가 아님, 잘못된 범위, 빈 열거.
SCHEDULE_SPAN_TOO_LONG시작~종료 사이 일수가 maxScheduleDays 초과.
INVALID_TIME_RANGE시간 범위 누락 또는 슬롯 0개.
INVALID_GRANULARITYslotGranularityMinutes가 60/15가 아니거나 플랜에서 불허.
INVALID_REQUIREMENTSrequirementsByCell이 객체가 아니거나 값이 0~15 정수가 아님.
UNKNOWN_CELL_KEY계산된 일정 그리드에 없는 키.
INVALID_STAFFstaff가 비어 있음, 너무 큼, 잘못된 행, 정리 실패 등.
DUPLICATE_STAFF_IDstaffId 중복.
INVALID_AVAILABLE_CELLavailableCells의 빈 항목 또는 알 수 없는 항목.
SHORTFALL_CELLS필요 인원 대비 가용 인원 부족.
DYNAMODB_ERROR검증 후 일시적 또는 저장 오류(핸들러에 따라 다름).
SCHEDULE_DEFAULTS_SAVE_FAILEDPATCH /v1/schedule-defaults: 검증 또는 비즈니스 규칙 실패(error.details 참고).
LABOR_COMPLIANCE_REQUIREDPATCH /v1/staff/{staffId}: laborLawCompliance가 false일 때 laborConstraintMask를 보냄.

최소 JSON 예시

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

이 페이지. POST /v1/schedule 본문: 필드 참조와 요약 표. 테넌트 기본값: 쓰기 엔드포인트 → PATCH /v1/schedule-defaults. 노무 / Soft: PATCH /v1/staff/{staffId}. 요금: 요금 페이지.