{
  "openapi": "3.0.3",
  "info": {
    "title": "카비네이터(Kavinator) 공개 API",
    "description": "KCD-9 한의 임상 전용 상병 네비게이터 카비네이터의 외부 개발자용 공개 API. 인증 없음, GET 전용, CORS 전면 허용. 실제 환자 개인정보는 포함되지 않은 순수 상병마스터 데이터입니다.",
    "version": "1.1.0",
    "contact": {
      "email": "elaminus1@gmail.com"
    }
  },
  "servers": [
    { "url": "https://kavinator.com" }
  ],
  "paths": {
    "/api/kcd/public-search": {
      "get": {
        "summary": "상병 검색",
        "description": "검색어(상병코드 또는 상병명)로 관련도 순 상병 목록을 최대 30건 반환합니다.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": { "type": "string" },
            "description": "검색어. 상병코드(점 유무 무관) 또는 상병명 일부. 띄어쓰기로 여러 단어를 넣으면 모두 포함된 결과만 매치. 비우면 count=0인 빈 결과를 반환(에러 아님)."
          },
          {
            "name": "fields",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "enum": ["0", "1", "2", "3"], "default": "0" },
            "description": "응답 필드 선택. 0=코드+상병명+청구건수(기본), 1=코드만, 2=상병명만, 3=상병명+청구건수. 그 외 값은 0으로 처리됨."
          }
        ],
        "responses": {
          "200": {
            "description": "검색 결과",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "query": { "type": "string", "description": "요청한 검색어 원문" },
                    "count": { "type": "integer", "description": "반환된 결과 건수 (최대 30)" },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": { "type": "string", "description": "상병코드 (fields=0 또는 1일 때)" },
                          "name": { "type": "string", "description": "상병명 (fields=0, 2, 3일 때)" },
                          "claim_count": { "type": "integer", "description": "한방(HIRA) 청구건수, 정렬/참고용 (fields=0 또는 3일 때)" }
                        }
                      }
                    }
                  },
                  "required": ["query", "count", "results"]
                },
                "example": {
                  "query": "요통",
                  "count": 29,
                  "results": [
                    { "code": "M54.5", "name": "요통", "claim_count": 13179873 },
                    { "code": "M54.57", "name": "요통, 요천부", "claim_count": 13179873 }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/kcd/public-mst-filter": {
      "get": {
        "summary": "근골격·외상 MST 다차원 소거 필터",
        "description": "5대 대부위 → 발병기전(외상유무) → 세부분절 → 병변조직군 → 청구안전도 순 다차원 소거 필터링. 모든 파라미터는 선택이며 생략하면 해당 단계는 필터 없이 전체를 봅니다. major/sub는 정수 코드(가운데점 섞인 한글 값을 직접 보내지 않도록). sub는 major 지정 여부와 무관하게 항상 전역 고정 번호(코드 하나=값 하나)이며, major와 함께 보낸 sub가 그 대부위에 속하지 않으면 400.",
        "parameters": [
          {
            "name": "major",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "enum": [0, 1, 2, 3, 4] },
            "description": "5대 대부위 코드: 0=체간, 1=상지, 2=하지, 3=두경부, 4=전신·다발"
          },
          {
            "name": "trauma",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "enum": ["ALL", "TRAUMA", "NON_TRAUMA"], "default": "ALL" },
            "description": "발병기전. TRAUMA=급성외상(S/T코드), NON_TRAUMA=만성·퇴행(M코드 등)"
          },
          {
            "name": "sub",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 0, "maximum": 28 },
            "description": "세부분절 코드(전역 고정, major 무관). 0=목/경추, 1=등/흉추, 2=허리/요추, 3=골반·엉치, 4=가슴·갈비뼈, 5=척추 전체·다발, 6=어깨,위팔, 7=팔꿈치,아래팔, 8=손목, 9=손·손가락, 10=팔저림(방사통), 11=고관절·대퇴, 12=무릎·하퇴, 13=발목·발, 14=다리저림·좌골신경통(방사통), 15=턱관절, 16=안면·안면신경, 17=두부·두통, 18=두개·안면외상, 19=전신대사·자가면역·골다공증, 20=다발성·여러부위, 21=전신_기타, 22=기타 외상·독성, 23=상세불명_전신, 24=처치·수술 합병증, 25=다발성 손상, 26=광범위·다발 화상, 27=상세불명 외상, 28=다발·기타 외상 후유증"
          },
          {
            "name": "tissue",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": ["BONE", "SKIN_WOUND", "AUTOIMMUNE", "TENDON_LIGAMENT", "CARTILAGE", "DEFORMITY", "MUSCLE", "NERVE", "CRYSTAL", "VASCULAR", "SUBLUXATION", "CLINICAL"]
            },
            "description": "병변조직군"
          },
          {
            "name": "diag",
            "in": "query",
            "required": false,
            "schema": { "type": "string" },
            "description": "청구안전도. CLINICAL(임상단독)/IMAGING(영상필요)/BLOOD(혈액검사)/SPECIAL(특수검사)/DUAL(복합) 중 콤마로 복수 지정 가능(예: IMAGING,BLOOD). 생략 또는 ALL이면 전체."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": { "type": "string" },
            "description": "상병코드/상병명 추가 검색어 (다른 필터와 AND 결합)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "default": 100, "maximum": 300 },
            "description": "반환 건수 (최대 300)"
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "default": 0 },
            "description": "페이지네이션 오프셋"
          }
        ],
        "responses": {
          "200": {
            "description": "필터링된 상병 후보군",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MstFilterResponse" }
              }
            }
          },
          "400": {
            "description": "major/sub/trauma/tissue/diag 중 유효하지 않은 값 — 허용 코드/값 목록이 담긴 에러",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/api/kcd/public-internal-filter": {
      "get": {
        "summary": "한방내과 지능형 필터",
        "description": "7대 분과 → 청구안전도 → 세부분절 순 소거 필터링. division/sub는 정수 코드(가운데점 섞인 한글 값을 직접 보내지 않도록). sub는 division 지정 여부와 무관하게 항상 전역 고정 번호이며, division과 함께 보낸 sub가 그 분과에 속하지 않으면 400.",
        "parameters": [
          {
            "name": "division",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "enum": [0, 1, 2, 3, 4, 5, 6], "default": 0 },
            "description": "7대 분과 코드: 0=소화기계, 1=호흡기계, 2=피부계, 3=부인·비뇨생식, 4=정신·신경, 5=이비인후·안과, 6=전신·대사·순환"
          },
          {
            "name": "diag",
            "in": "query",
            "required": false,
            "schema": { "type": "string" },
            "description": "청구안전도. ALL(전체,기본)/CLINICAL/IMAGING/BLOOD/SPECIAL 중 콤마로 복수 지정 가능."
          },
          {
            "name": "sub",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 0, "maximum": 46 },
            "description": "세부분절 코드(전역 고정, division 무관). 0=구강·혀·턱, 1=위·식도, 2=대장·배변, 3=항문·치질, 4=간·담·췌, 5=기타·복벽·흡수, 6=치과전문, 7=코·비동, 8=인후·편도·후두, 9=기관지·기침, 10=폐·천식·COPD, 11=흉막·간질·기타, 12=습진·아토피·가려움, 13=두드러기·홍반·건선·주사, 14=여드름·모낭·피지, 15=탈모·모발·손발톱, 16=색소·각화·흉터·티눈, 17=감염·수포·자가면역·기타, 18=생리·월경통·PMS, 19=갱년기·폐경·난임, 20=자궁·난소·질염·골반, 21=임신·입덧·산후조리, 22=방광·요실금·배뇨, 23=콩팥·전립선·비뇨기타, 24=수면·불면, 25=화병·우울·불안·스트레스, 26=두통·안면마비·삼차신경, 27=말초신경·저림·손떨림, 28=소아발달·틱·ADHD, 29=퇴행·기질뇌신경, 30=중증정신·기질·중독, 31=이명·난청·귀먹먹, 32=어지럼·메니에르·전정, 33=외이도·중이염·이관, 34=안구건조·눈피로·결막, 35=눈꺼풀·다래끼·안과기타, 36=만성피로·기허·체력저하, 37=다한증·도한·체온조절, 38=부종·수분대사·어지럼, 39=비만·대사증후군·고지혈, 40=말초순환·혈압·수족냉증, 41=자각증상·호소증상, 42=뇌혈관·중풍, 43=심장질환, 44=기타혈관질환, 45=내분비질환, 46=검사소견·이상소견"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": { "type": "string" },
            "description": "상병코드/상병명 추가 검색어"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "default": 100, "maximum": 300 },
            "description": "반환 건수 (최대 300)"
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "default": 0 },
            "description": "페이지네이션 오프셋"
          }
        ],
        "responses": {
          "200": {
            "description": "필터링된 상병 후보군",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/InternalFilterResponse" }
              }
            }
          },
          "400": {
            "description": "division/sub/diag 중 유효하지 않은 값 — 허용 코드/값 목록이 담긴 에러",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "description": "허용 코드/값 목록이 포함된 사람이 읽을 수 있는 에러 메시지" }
        },
        "required": ["error"]
      },
      "CodeValuePair": {
        "type": "object",
        "nullable": true,
        "description": "해당 파라미터를 생략하면 null. 보냈으면 {code, value} 형태로 보낸 코드와 해석된 한글 값을 함께 echo.",
        "properties": {
          "code": { "type": "string" },
          "value": { "type": "string" }
        }
      },
      "MstCandidate": {
        "type": "object",
        "properties": {
          "code": { "type": "string", "description": "상병코드" },
          "name": { "type": "string", "description": "상병명" },
          "synonyms": { "type": "string", "description": "동의어/영문명 묶음(| 구분)" },
          "eng_name": { "type": "string", "description": "영문명(추출 실패 시 빈 문자열)" },
          "chapter": { "type": "string", "description": "챕터(M/S/T 등)" },
          "major_part": { "type": "string" },
          "sub_part": { "type": "string" },
          "target_tissue": { "type": "string" },
          "diagnostic_req": { "type": "string" },
          "required_test": { "type": "string", "description": "요구 검사/진찰 설명(사람이 읽는 문장)" },
          "claim_count": { "type": "integer", "nullable": true, "description": "한방 청구빈도 — 정렬/참고용, 양방 값과 다름" },
          "claim_count_western": { "type": "integer", "nullable": true },
          "is_primary": { "type": "boolean" },
          "cross_reason": { "type": "string", "nullable": true, "description": "다른 분과와 이중매핑된 사유" },
          "is_site_unspecified": { "type": "boolean" },
          "claim_caution": { "type": "string" },
          "is_caution": { "type": "boolean" }
        },
        "required": ["code", "name", "diagnostic_req"]
      },
      "InternalCandidate": {
        "type": "object",
        "properties": {
          "code": { "type": "string" },
          "name": { "type": "string" },
          "synonyms": { "type": "string" },
          "eng_name": { "type": "string" },
          "chapter": { "type": "string", "description": "챕터(J/K/L 등)" },
          "division": { "type": "string" },
          "sub_part": { "type": "string" },
          "diagnostic_req": { "type": "string" },
          "required_test": { "type": "string" },
          "claim_count": { "type": "integer", "nullable": true },
          "claim_count_western": { "type": "integer", "nullable": true },
          "is_mst_cross": { "type": "boolean", "description": "근골격 MST 트랙과 이중매핑된 코드인지 여부" },
          "claim_caution": { "type": "string" },
          "is_caution": { "type": "boolean" }
        },
        "required": ["code", "name", "diagnostic_req"]
      },
      "MstFilterResponse": {
        "type": "object",
        "properties": {
          "total_count": { "type": "integer" },
          "selected": {
            "type": "object",
            "properties": {
              "major": { "$ref": "#/components/schemas/CodeValuePair" },
              "trauma": { "type": "string" },
              "sub": { "$ref": "#/components/schemas/CodeValuePair" },
              "diag": { "type": "string" },
              "tissue": { "type": "string" },
              "chapter": { "type": "string" }
            }
          },
          "major_summary": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": { "major_part": { "type": "string" }, "count": { "type": "integer" } }
            }
          },
          "trauma_summary": {
            "type": "object",
            "properties": {
              "total": { "type": "integer" },
              "trauma": { "type": "integer" },
              "non_trauma": { "type": "integer" }
            }
          },
          "sub_summary": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": { "sub_part": { "type": "string" }, "count": { "type": "integer" } }
            },
            "description": "major 지정 시에만 채워짐"
          },
          "tissue_summary": {
            "type": "object",
            "additionalProperties": { "type": "integer" },
            "description": "{조직군코드: count}"
          },
          "diag_summary": {
            "type": "object",
            "additionalProperties": { "type": "integer" },
            "description": "{청구안전도코드: count}"
          },
          "limit": { "type": "integer" },
          "offset": { "type": "integer" },
          "candidates": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/MstCandidate" }
          }
        }
      },
      "InternalFilterResponse": {
        "type": "object",
        "properties": {
          "total_count": { "type": "integer" },
          "selected": {
            "type": "object",
            "properties": {
              "division": { "$ref": "#/components/schemas/CodeValuePair" },
              "diag": { "type": "string" },
              "sub": { "$ref": "#/components/schemas/CodeValuePair" }
            }
          },
          "division_summary": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "division": { "type": "string" },
                "name": { "type": "string" },
                "icon": { "type": "string" },
                "chapters": { "type": "array", "items": { "type": "string" } },
                "preview": { "type": "string" },
                "desc": { "type": "string" },
                "count": { "type": "integer" }
              }
            }
          },
          "diag_summary": {
            "type": "object",
            "additionalProperties": { "type": "integer" }
          },
          "sub_summary": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": { "sub_part": { "type": "string" }, "count": { "type": "integer" } }
            }
          },
          "limit": { "type": "integer" },
          "offset": { "type": "integer" },
          "candidates": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/InternalCandidate" }
          }
        }
      }
    }
  }
}
