{"openapi":"3.0.3","info":{"title":"기회 API — 사주 계산","version":"0.3.5","description":"만세력·24절기·대운·음양력 계산 + 완성 풀이(기본 풀이·열한 가지 풀이·궁합·오늘의 기운). x-api-key 인증. 요청 본문(생년월일)은 기록하지 않습니다."},"servers":[{"url":"/v1"}],"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key"}},"schemas":{"Meta":{"type":"object","properties":{"responseMs":{"type":"integer"},"version":{"type":"string"},"plan":{"type":"string"}}},"ApiError":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}},"Pillar":{"type":"object","properties":{"ganji":{"type":"string","example":"癸亥"},"ganjiKo":{"type":"string","example":"계해"},"label":{"type":"string"},"stem":{"type":"string"},"stemKo":{"type":"string"},"branch":{"type":"string"},"branchKo":{"type":"string"},"stemElement":{"type":"string"},"tenGod":{"type":"string"},"branchTenGod":{"type":"string"}}},"ManseData":{"type":"object","properties":{"input":{"type":"object"},"lunarDate":{"type":"object","nullable":true,"properties":{"year":{"type":"integer"},"month":{"type":"integer"},"day":{"type":"integer"},"isLeapMonth":{"type":"boolean"}}},"pillars":{"type":"object","properties":{"year":{"$ref":"#/components/schemas/Pillar"},"month":{"$ref":"#/components/schemas/Pillar"},"day":{"$ref":"#/components/schemas/Pillar"},"hour":{"allOf":[{"$ref":"#/components/schemas/Pillar"}],"nullable":true}}},"dayMaster":{"type":"object","properties":{"stem":{"type":"string"},"stemKo":{"type":"string"},"element":{"type":"string"},"yinYang":{"type":"string"}}},"elements":{"type":"object","description":"오행 이름을 키로 한 개수","additionalProperties":{"type":"integer"},"example":{"목":0,"화":1,"토":3,"금":1,"수":3}},"assumptions":{"type":"object","description":"assumedNoon·termBoundary·dayBoundary·hourBoundary·historicClockApplied 등 판정 전제"}}},"DaeunData":{"type":"object","description":"/v1/daeun 응답의 data — lib/saju-tables.mjs 의 daeun() 결과에 input·tables 를 더한 것. isFemale 이 없으면 400 이라 이 스키마는 늘 채워져 나갑니다(만세력 detail=full 안의 daeun 은 성별이 없으면 null).","properties":{"input":{"type":"object","description":"되돌려 주는 입력","properties":{"date":{"type":"string","example":"2001-04-30"},"time":{"type":"string","nullable":true,"example":"13:30","description":"시각 미상이면 null"},"isFemale":{"type":"boolean"}}},"direction":{"type":"string","enum":["순행","역행"],"description":"양남음녀 순행 · 음남양녀 역행"},"rule":{"type":"string","description":"적용한 규칙 문장","example":"양남음녀 순행 · 음남양녀 역행, 대운수 = 절까지 일수 ÷ 3 (사사오입, 최소 1)"},"anchorTerm":{"type":"object","description":"대운수를 잰 기준 절 — 순행은 출생 직후, 역행은 출생 직전","properties":{"name":{"type":"string","example":"입하"},"at":{"type":"string","description":"한국시 YYYY-MM-DD HH:MM","example":"2001-05-05 19:36"}}},"daysToTerm":{"type":"number","description":"그 절까지의 일수 — 소수 둘째 자리까지(셋째 자리에서 반올림)","example":5.25},"startAgeExact":{"type":"number","description":"정수로 반올림하기 전 대운수 = 절까지 일수 ÷ 3, 소수 둘째 자리까지","example":1.75},"startAge":{"type":"integer","description":"대운수 — 사사오입, 최소 1","example":2},"startAgeBoundary":{"type":"boolean","description":"true 면 반올림 경계라 다른 만세력과 대운수가 1 다를 수 있음"},"periods":{"type":"array","description":"10년 대운 열 개","items":{"type":"object","properties":{"order":{"type":"integer","example":1},"startAge":{"type":"integer","example":2},"endAge":{"type":"integer","example":11},"ganji":{"type":"string","example":"癸巳"},"ganjiKo":{"type":"string","example":"계사"},"tenGod":{"type":"string","description":"일간에서 본 십신","example":"비견"},"stage":{"type":"string","description":"일간에서 본 12운성","example":"태"}}}},"assumptions":{"type":"object","description":"판정 전제","properties":{"assumedNoon":{"type":"boolean","description":"시각이 없어 정오로 가정했는지"},"variants":{"type":"string","example":"대운수 반올림·시작 나이 세는 법은 유파 차이가 있다. startAgeBoundary 가 true 면 다른 만세력과 대운수가 1 다를 수 있다"}}},"tables":{"type":"string","description":"표 판(TABLES_VERSION)","example":"saju-tables-v0.1"}}},"SignupData":{"type":"object","properties":{"apiKey":{"type":"string","description":"이 응답에서 한 번만 표시"},"plan":{"type":"string"},"scope":{"type":"string"},"monthlyLimit":{"type":"integer"},"perMinute":{"type":"integer"},"requestId":{"type":"string"},"termsVersion":{"type":"string"},"message":{"type":"string"}}},"RotateData":{"type":"object","properties":{"apiKey":{"type":"string","description":"새 키, 한 번만 표시. 이전 키는 즉시 폐기"},"plan":{"type":"string"},"scope":{"type":"string"},"monthlyLimit":{"type":"integer"},"perMinute":{"type":"integer"},"carriedMonthlyUsage":{"type":"integer"},"message":{"type":"string"}}},"BirthInput":{"type":"object","required":["birthYear","birthMonth","birthDay"],"properties":{"birthYear":{"type":"integer","minimum":1900,"maximum":2100},"birthMonth":{"type":"integer","minimum":1,"maximum":12},"birthDay":{"type":"integer","minimum":1,"maximum":31},"birthHour":{"type":"integer","minimum":0,"maximum":23,"nullable":true},"birthMinute":{"type":"integer","minimum":0,"maximum":59},"trueSolarTime":{"type":"boolean","default":false},"longitude":{"type":"number","description":"출생지 경도(124~132). 기본 서울 126.98"},"locale":{"type":"string","enum":["ko","han"],"default":"ko"},"isLunar":{"type":"boolean","default":false,"description":"입력을 음력으로 해석 (true/false 만 인정)"},"isLeapMonth":{"type":"boolean","description":"음력 입력(isLunar=true)일 때 필수 — 윤달 여부를 모르면 변환하지 않고 400 을 돌려줍니다 (평달 추정 안 함)"},"isFemale":{"type":"boolean","description":"대운 계산에 필요(양남음녀 순행)"},"detail":{"type":"string","enum":["basic","full"],"default":"basic","description":"full 이면 지장간·12운성·납음·공망·신살·형파해·대운 포함"}}},"Error":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}},"security":[{"apiKey":[]}],"paths":{"/health":{"get":{"security":[],"summary":"상태","responses":{"200":{"description":"ok"}}}},"/me":{"get":{"summary":"내 키·사용량(호출 수) + 들어 있는 옵션(features) + 이용 기간 + 이벤트 칸 + 회사 연령 기준. byFeature·people 은 늘 0 — 계약 단위는 호출입니다","responses":{"200":{"description":"ok — {label, plan, monthlyLimit, perMinute, features[], featuresKo[], paidUntil, usage{month,count,byFeature}, events, policy{minAge}}"}}}},"/keys/signup":{"post":{"summary":"운영에서는 닫힘(403 NO_FREE_KEY) — 무료 체험 키 없음. 키는 결제(/pricing)로 발급. 로컬 검사용","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","service","agree"],"properties":{"email":{"type":"string"},"service":{"type":"string","maxLength":80},"purpose":{"type":"string","maxLength":500},"agree":{"type":"boolean"}}}}}},"responses":{"201":{"description":"issued","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/SignupData"}}}}}}}}},"/manse":{"get":{"summary":"만세력(년·월·일·시주)","parameters":[{"name":"date","in":"query","required":true,"schema":{"type":"string","example":"2001-04-30"}},{"name":"time","in":"query","schema":{"type":"string","example":"13:30"}},{"name":"trueSolarTime","in":"query","schema":{"type":"boolean"}},{"name":"longitude","in":"query","schema":{"type":"number"}},{"name":"locale","in":"query","schema":{"type":"string","enum":["ko","han"]}}],"responses":{"200":{"description":"ok","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/ManseData"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"VALIDATION_ERROR"}}},"post":{"summary":"만세력(본문 입력)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BirthInput"}}}},"responses":{"200":{"description":"ok","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/ManseData"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}}}}},"/reading":{"get":{"summary":"완성 풀이 — 신강약·용신·격국·오행 치우침·십신을 근거와 함께, 화면용 일곱 칸(outline)과 완성 문장(prose)까지","description":"재료가 아니라 완성 풀이를 드립니다. 규칙 조립(생성 모델 없음)이라 같은 생년월일이면 같은 글. 판정마다 근거·기준이 함께 실리고 길흉·시기는 말하지 않습니다. category 를 주면 열한 가지 풀이(data.category)가 덧붙습니다. 응답에 minor{under14,under19,asOf}·events[]·readingVersion·proseVersion 이 있습니다. 호출 셈: short(분야 없음) 0회 · normal/long 1회 · 분야당 1회.","parameters":[{"name":"date","in":"query","required":true,"schema":{"type":"string","example":"2001-04-30"}},{"name":"time","in":"query","schema":{"type":"string","example":"13:30"}},{"name":"isFemale","in":"query","schema":{"type":"boolean"}},{"name":"isLunar","in":"query","schema":{"type":"boolean"},"description":"음력 입력이면 isLeapMonth 필수(모르면 400)"},{"name":"isLeapMonth","in":"query","schema":{"type":"boolean"}},{"name":"length","in":"query","schema":{"type":"string","enum":["short","normal","long"],"default":"short"},"description":"short=맛보기(무료, 호출 0회) · normal=기본 요금제부터(1회) · long=확장 요금제"},{"name":"tone","in":"query","schema":{"type":"string","enum":["talk","calm","plain"]},"description":"말투 — 문장만 바뀌고 판정·근거는 같음"},{"name":"name","in":"query","schema":{"type":"string"},"description":"이름을 부르는 풀이 — 기본 요금제부터. 저장하지 않음"},{"name":"category","in":"query","schema":{"type":"string","enum":["overview","love","money","career","children","parents","relationships","friends","partner","elements","health"]},"description":"열한 가지 풀이(어느 요금제나, 풀이마다 1회): 나의 성향·연애·돈재물·직장사업·가족자녀·부모·친구인간관계·친구·배우자·오행·건강. 배우자는 isFemale 을 주면 관행대로(여성=관성·남성=재성) 별을 잡고, 없으면 두 별을 함께 읽습니다. 건강은 병·수명·몸의 부위·진단·치료를 말하지 않고 기운의 씀씀이만 읽습니다(의학 정보 아님)"},{"name":"signature","in":"query","schema":{"type":"string","enum":["off"]},"description":"풀이 끝 서명 숨김 — 유료 요금제만"},{"name":"trueSolarTime","in":"query","schema":{"type":"boolean"}},{"name":"longitude","in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"ok"},"400":{"description":"VALIDATION_ERROR"},"403":{"description":"PLAN_REQUIRED(요금제 밖 옵션) · AGE_RESTRICTED(회사 연령 기준 미만, 호출 셈 안 함)"},"404":{"description":"CATEGORY_NOT_READY"}}},"post":{"summary":"완성 풀이(본문 입력) — 같은 옵션을 JSON 본문으로","requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BirthInput"},{"type":"object","properties":{"length":{"type":"string","enum":["short","normal","long"]},"tone":{"type":"string","enum":["talk","calm","plain"]},"name":{"type":"string"},"category":{"type":"string","enum":["overview","love","money","career","children","parents","relationships","friends","partner","elements","health"]},"focus":{"type":"string","description":"초점 — 고객이 고른 상황(선택 질문 답). love: crush·some·dating·single / friends: hurt·newbond·drift·curious / parents: unspoken·apart·friction·health / none 또는 생략 = 기존 풀이. 계산값은 그대로이고 해석 각도만 바뀐다. 응답 data.category.focus 로 에코된다."},"signature":{"type":"boolean"}}}]}}}},"responses":{"200":{"description":"ok"},"400":{"description":"VALIDATION_ERROR"},"403":{"description":"PLAN_REQUIRED · AGE_RESTRICTED"},"404":{"description":"CATEGORY_NOT_READY"}}}},"/solar-terms":{"get":{"summary":"24절기 절입 시각(한국시)","parameters":[{"name":"year","in":"query","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/day-pillar":{"get":{"summary":"일진","parameters":[{"name":"date","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/calendar/month":{"get":{"summary":"한 달 만세력 표","parameters":[{"name":"year","in":"query","required":true,"schema":{"type":"integer"}},{"name":"month","in":"query","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/daeun":{"get":{"summary":"대운(10년 운) — isFemale 필수","parameters":[{"name":"date","in":"query","required":true,"schema":{"type":"string"}},{"name":"time","in":"query","schema":{"type":"string"}},{"name":"isFemale","in":"query","required":true,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"ok","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/DaeunData"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}}}},"post":{"summary":"대운(본문 입력)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BirthInput"}}}},"responses":{"200":{"description":"ok","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/DaeunData"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}}}}},"/calendar/convert":{"get":{"summary":"음양력 변환","parameters":[{"name":"direction","in":"query","schema":{"type":"string","enum":["solar-to-lunar","lunar-to-solar"],"default":"solar-to-lunar"}},{"name":"year","in":"query","required":true,"schema":{"type":"integer"}},{"name":"month","in":"query","required":true,"schema":{"type":"integer"}},{"name":"day","in":"query","required":true,"schema":{"type":"integer"}},{"name":"isLeapMonth","in":"query","schema":{"type":"boolean"},"description":"lunar-to-solar 방향에서는 필수 (true/false, 평달 추정 안 함)"}],"responses":{"200":{"description":"ok"}}}},"/readings":{"post":{"summary":"여러 풀이를 한 묶음으로 — categories 배열. 풀이가 몇이든 호출 1회. 한 번에 5개까지","description":"앱 결제의 30초 제한 때문에 왕복을 줄이는 주소. 응답은 input·pillars·prose(한 번)·categories{풀이별 글}·order(부른 순서 그대로)·counted(이번에 센 호출 수, 늘 1). 한 묶음이 한 호출이다 — 풀이가 셋이든 다섯이든 1회. 따로 /v1/reading 을 세 번 부르면 3회다. 받은 묶음을 어떻게 나눠 쓰든 쓰는 쪽 몫이고 셈은 1이다. 안 든 옵션이 하나라도 섞이면 403 이고 호출을 세지 않는다.","requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BirthInput"},{"type":"object","required":["categories"],"properties":{"categories":{"type":"array","maxItems":5,"items":{"type":"string","enum":["money","career","love","partner","children","relationships","health","elements","overview"]}},"length":{"type":"string","enum":["short","normal","long"]},"tone":{"type":"string","enum":["talk","calm","plain"]},"name":{"type":"string"}}}]}}}},"responses":{"200":{"description":"ok"},"400":{"description":"VALIDATION_ERROR"},"403":{"description":"FEATURE_NOT_INCLUDED · AGE_RESTRICTED"},"404":{"description":"CATEGORY_NOT_READY"}}}},"/chances":{"post":{"summary":"세 번의 기회(지나간·지금·다가올 해) — 내부 전용, 외부(기본 정보) 키는 403 FORBIDDEN","requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BirthInput"},{"type":"object","required":["category"],"properties":{"category":{"type":"string","enum":["money","career","love","partner","children","relationships","health","elements","overview"]},"asOfYear":{"type":"integer"},"frame":{"type":"string","enum":["threeTimes","lifeStage"],"description":"기본 threeTimes(지나간·지금·다가올), lifeStage 는 초년·중년·말년 프레임"},"sections":{"type":"boolean","description":"true 면 응답에 reading(섹션 계약) 추가: category·source·ruleVersion·contentStatus·reviewStatus 와 sections[]{sectionId,title,status,text,basis}. 완성 원고는 공급하지 않으므로 text 는 항상 null, contentStatus=basis_only"}}}]}}}},"responses":{"200":{"description":"ok"}}}},"/keys/rotate":{"post":{"summary":"키 회전 — 현재 키로 인증하면 새 키 발급(응답에 한 번만 표시), 이전 키 즉시 폐기, 월 사용량 이월","security":[{"apiKey":[]}],"responses":{"200":{"description":"ok","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/RotateData"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}}}}},"/match":{"get":{"summary":"궁합(두 사람) — 쿼리 dateA·timeA·nameA·dateB·timeB·nameB·length","description":"일간 관계·곁 자리·서로 채우는 것·묶임과 부딪힘을 근거와 완성 문장(prose)으로. 정책상 점수·판정 없음(data.policy). 호출 2회. 회사 연령 기준은 두 사람 모두에 적용.","parameters":[{"name":"dateA","in":"query","required":true,"schema":{"type":"string"}},{"name":"timeA","in":"query","schema":{"type":"string"}},{"name":"dateB","in":"query","required":true,"schema":{"type":"string"}},{"name":"timeB","in":"query","schema":{"type":"string"}},{"name":"nameA","in":"query","schema":{"type":"string"}},{"name":"nameB","in":"query","schema":{"type":"string"}},{"name":"length","in":"query","schema":{"type":"string","enum":["short","normal","long"]}}],"responses":{"200":{"description":"ok"},"400":{"description":"VALIDATION_ERROR"},"403":{"description":"PLAN_REQUIRED · AGE_RESTRICTED"}}},"post":{"summary":"궁합(본문 입력) {a, b, length?}","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["a","b"],"properties":{"a":{"type":"object","required":["date"],"properties":{"date":{"type":"string","example":"2001-04-30"},"time":{"type":"string","example":"13:30"},"isFemale":{"type":"boolean"},"name":{"type":"string"}}},"b":{"type":"object","required":["date"],"properties":{"date":{"type":"string","example":"2001-04-30"},"time":{"type":"string","example":"13:30"},"isFemale":{"type":"boolean"},"name":{"type":"string"}}},"length":{"type":"string","enum":["short","normal","long"]},"signature":{"type":"boolean"}}}}}},"responses":{"200":{"description":"ok"},"400":{"description":"VALIDATION_ERROR"},"403":{"description":"PLAN_REQUIRED · AGE_RESTRICTED"}}}},"/daily":{"get":{"summary":"오늘의 기운(오늘의 운세) — 그날의 일진이 이 사주에 어떻게 닿는지","description":"세 문단(오늘 닿는 기운·곁 자리와 오늘·오늘 살릴 것)과 근거. on 을 비우면 오늘(한국 날짜). 같은 사람·같은 날이면 같은 글(규칙 조립). 하루의 길흉을 말하지 않고 숫자를 쓰지 않는다. '오늘의 기운' 묶음이 든 키. 전체 키는 호출 0회(월 계약에 포함), 맞춤 키에 넣는 값은 협의.","parameters":[{"name":"date","in":"query","required":true,"schema":{"type":"string","example":"2001-04-30"}},{"name":"time","in":"query","schema":{"type":"string","example":"13:30"}},{"name":"on","in":"query","schema":{"type":"string","example":"2026-09-20"},"description":"볼 날짜(1901~2099). 비우면 오늘"}],"responses":{"200":{"description":"ok"},"400":{"description":"VALIDATION_ERROR"},"403":{"description":"FEATURE_NOT_INCLUDED · AGE_RESTRICTED"}}},"post":{"summary":"오늘의 기운(본문 입력)","requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BirthInput"},{"type":"object","properties":{"on":{"type":"string","example":"2026-09-20"}}}]}}}},"responses":{"200":{"description":"ok"},"400":{"description":"VALIDATION_ERROR"},"403":{"description":"FEATURE_NOT_INCLUDED · AGE_RESTRICTED"}}}},"/me/policy":{"post":{"summary":"회사별 연령 기준 — {minAge: 0|14|19}. 기준 미만 생년월일은 403 AGE_RESTRICTED(호출 셈 안 함). 기본 0","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["minAge"],"properties":{"minAge":{"type":"integer","enum":[0,14,19]}}}}}},"responses":{"200":{"description":"ok"}}}},"/me/events":{"post":{"summary":"이벤트 칸 켜기/끄기 — {enabled: boolean}. 기본 켜짐(새 이벤트 자동 추가)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["enabled"],"properties":{"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"ok"}}}},"/support":{"post":{"summary":"문의 접수(키 불필요) — {email, category, message}","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","message"],"properties":{"email":{"type":"string"},"category":{"type":"string"},"message":{"type":"string"}}}}}},"responses":{"201":{"description":"received"}}}}}}