{"openapi":"3.1.0","info":{"title":"furstock OpenAPI","version":"1.0.0","summary":"퍼스톡(furstock.trade) 모의투자 REST API","description":"퍼스톡은 캐릭터를 주식처럼 상장해 가상 화폐로 거래하는 모의투자 서비스입니다. 이 API로\n퍼스톡 웹에서 할 수 있는 조작(시세 조회, 매수·매도, 계좌 조회, 커뮤니티 등)을 프로그램에서\n그대로 할 수 있습니다. 자기 트레이더봇이나 커스텀 클라이언트를 만들 때 쓰세요.\n\n- 사람이 읽는 가이드: https://furstock.trade/developers\n- AI 에이전트용 요약: https://furstock.trade/llms.txt ·\n  스킬: https://furstock.trade/skills/furstock-openapi/SKILL.md\n\n**인증 요약**: 프로필 페이지(https://furstock.trade/profile?tab=api)에서 API 키(client_id +\nclient_secret)를 발급 → `POST /oauth2/token`(client_credentials)으로 1시간짜리 access token\n교환 → 모든 요청에 `Authorization: Bearer <token>`.\n\n**금액**: 모든 가격·금액은 정수 \"원\"(가상 화폐, KRW 표기)입니다. **호가 단위**: 가격(현재가·시가·\n상하한가·지정가·평균 체결가)은 전부 5원 단위입니다. **시각**: ISO 8601(UTC) 문자열,\n단 시세 스냅샷의 `ts`/캔들의 `time`은 epoch(ms/초)입니다.\n","contact":{"name":"퍼스톡 문의","url":"https://furstock.trade/contact"}},"servers":[{"url":"https://api.furstock.trade","description":"production"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"auth","description":"API 키로 access token 받기, 내 인증 상태 확인"},{"name":"api-keys","description":"API 키 발급·조회·폐기 (브라우저 로그인 세션 전용 — API 키 토큰으로는 호출 불가)"},{"name":"stocks","description":"종목 목록·검색·상세·공시"},{"name":"market","description":"시세·호가·캔들·체결·순위 (전부 공개, 인증 불필요)"},{"name":"account","description":"내 잔고·보유종목·프로필"},{"name":"orders","description":"주문(시장가·지정가 매수/매도), 주문 이력, 매매 대기열, 취소"},{"name":"community","description":"종목 커뮤니티(종목토론) 글"},{"name":"watchlist","description":"관심종목·최근 본 종목·종목 그룹"},{"name":"journals","description":"종목 뉴스·공시(저널) — 상장주 소유자가 작성"},{"name":"gallery","description":"종목 캐릭터 갤러리 — 상장주 소유자가 사진 업로드, 누구나 좋아요·댓글"},{"name":"characters","description":"캐릭터 상장 신청과 내 상장종목 관리, 캐릭터 친구 목록"},{"name":"misc","description":"의견 보내기, 링크 미리보기, 스펙/문서"}],"paths":{"/oauth2/token":{"post":{"operationId":"issueAccessToken","tags":["auth"],"summary":"access token 발급 (client_credentials)","description":"OAuth 2.0 Client Credentials Grant(RFC 6749 §4.4). 발급된 토큰은 1시간 유효하며 갱신\n토큰은 없습니다 — 만료되면 같은 요청으로 다시 받으세요. 키를 폐기하면 이미 받은 토큰도\n즉시(최대 30초) 무효가 됩니다. client_id별 분당 30회 제한.\n","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/TokenRequest"}},"application/json":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}}},"responses":{"200":{"description":"발급 성공","content":{"application/json":{"schema":{"type":"object","required":["access_token","token_type","expires_in"],"properties":{"access_token":{"type":"string"},"token_type":{"type":"string","const":"Bearer"},"expires_in":{"type":"integer","example":3600,"description":"초 단위 수명"}}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"description":"잘못된 client_id/client_secret, 또는 폐기된 키","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_client"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/me":{"get":{"operationId":"getMe","tags":["auth"],"summary":"내 인증 상태","description":"토큰이 유효한지와 어떤 방식으로 인증됐는지 확인합니다. API 키 토큰이면 관리자 계정이라도 `isAdmin`은 항상 false입니다.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["userId","isAdmin","authMethod"],"properties":{"userId":{"type":"string","format":"uuid"},"isAdmin":{"type":"boolean"},"authMethod":{"type":"string","enum":["session","api_key"]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/api-keys":{"get":{"operationId":"listApiKeys","tags":["api-keys"],"summary":"내 API 키 목록","description":"폐기된 키 포함, 최신순. client_secret은 절대 다시 조회되지 않습니다. **브라우저 로그인 세션 전용.**","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/SessionRequired"}}},"post":{"operationId":"createApiKey","tags":["api-keys"],"summary":"API 키 발급","description":"계정당 활성 키 최대 5개. 응답의 `clientSecret`은 이때 한 번만 보입니다. **브라우저 로그인 세션 전용.**","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":40,"example":"내 스캘핑 봇"}}}}}},"responses":{"201":{"description":"발급됨","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreated"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/SessionRequired"},"409":{"description":"활성 키가 이미 5개","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"too_many_keys"}}}}}}},"/v1/api-keys/{clientId}":{"delete":{"operationId":"revokeApiKey","tags":["api-keys"],"summary":"API 키 폐기","description":"되돌릴 수 없습니다. 이 키로 받은 access token도 즉시 무효가 됩니다. **브라우저 로그인 세션 전용.**","parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","example":"fsk_0123456789abcdef0123456789abcdef"}}],"responses":{"200":{"description":"폐기됨(`revokedAt`이 채워진 키)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKey"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/SessionRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/stocks":{"get":{"operationId":"listStocks","tags":["stocks"],"summary":"종목 목록 / 종목 검색 / 여러 종목 조회","description":"- `symbol` 있음: 그 종목들만 한 번에. `symbol`을 1~100번 반복해서 넘깁니다(예: `?symbol=FOXX&symbol=WOLF`). `/v1/stocks/{symbol}`과 같은 규칙이라 관리종목도 포함되고, 없는 종목은 결과에서 빠지며, 순서는 요청 순서를 따릅니다. `q`·`limit`은 무시됩니다. 최대 1초 캐시됩니다.\n- `q` 있음: 종목 검색. 종목명·태그 부분일치(대소문자 무시), 관리종목 제외, **최대 50개**.\n- 둘 다 없음: 상장 종목 전체를 symbol 오름차순으로, **최대 1000개**. 관리종목(`hidden`)도 포함. 화면에 보이는 몇 종목만 필요하면 전체를 받지 말고 `symbol`을 쓰세요.\n","security":[],"parameters":[{"name":"symbol","in":"query","style":"form","explode":true,"schema":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string"}}},{"name":"q","in":"query","schema":{"type":"string","example":"여우"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000},"description":"기본값 = 최대값(목록 1000, 검색 50)"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Stock"}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"}}}},"/v1/stocks/{symbol}":{"get":{"operationId":"getStock","tags":["stocks"],"summary":"종목 상세","security":[],"parameters":[{"$ref":"#/components/parameters/Symbol"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stock"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/stocks/{symbol}/disclosures":{"get":{"operationId":"listDisclosures","tags":["stocks"],"summary":"거래소 공시 (거래정지·재개, 관리종목 지정·해제, 종목명 변경)","security":[],"parameters":[{"$ref":"#/components/parameters/Symbol"}],"responses":{"200":{"description":"최신순 최대 50개","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StockDisclosure"}}}}}}}},"/v1/stocks/{symbol}/related":{"get":{"operationId":"getRelatedStocks","tags":["stocks"],"summary":"같은 소유자의 다른 캐릭터 + 이 캐릭터의 친구","description":"종목정보 탭의 두 목록. `sameOwner`는 이 캐릭터를 올린 사람의 다른 상장 캐릭터(상장순, 최대 100개),\n`friends`는 소유자가 직접 등록한 친구(소유자가 정한 순서, 최대 100개). 둘 다 관리종목(`hidden`)은\n빠지고 거래정지 종목은 그대로 나옵니다. 가격은 조회 시점의 현재 세션 값입니다.\n","security":[],"parameters":[{"$ref":"#/components/parameters/Symbol"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["sameOwner","friends"],"properties":{"sameOwner":{"type":"array","items":{"$ref":"#/components/schemas/RelatedStock"}},"friends":{"type":"array","items":{"$ref":"#/components/schemas/RelatedStock"}}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/stocks/{symbol}/friends":{"post":{"operationId":"addCharacterFriend","tags":["characters"],"summary":"친구 추가 (캐릭터 소유자만)","description":"친구 목록 맨 끝에 추가하고 갱신된 친구 목록을 돌려줍니다. 이미 친구면 그대로 둡니다. 친구는 캐릭터에\n묶이며(종목명이 바뀌어도 유지), 같은 소유자의 다른 캐릭터도 등록할 수 있고 자기 자신만 안 됩니다.\n","parameters":[{"$ref":"#/components/parameters/Symbol"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["symbol"],"properties":{"symbol":{"$ref":"#/components/schemas/Symbol"}}}}}},"responses":{"200":{"description":"갱신된 친구 목록","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RelatedStock"}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"종목 또는 친구로 올릴 종목이 없음","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"friend_not_found"}}}},"409":{"description":"자기 자신(`cannot_friend_self`), 관리종목·비상장(`friend_unavailable`), 100명 초과(`too_many_friends`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"too_many_friends"}}}}}}},"/v1/stocks/{symbol}/friends/order":{"put":{"operationId":"reorderCharacterFriends","tags":["characters"],"summary":"친구 순서 변경 (캐릭터 소유자만)","description":"보낸 순서대로 다시 매깁니다. 목록에 없는 기존 친구는 지우지 않고 기존 순서대로 그 뒤에 붙습니다.","parameters":[{"$ref":"#/components/parameters/Symbol"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["symbols"],"properties":{"symbols":{"type":"array","items":{"$ref":"#/components/schemas/Symbol"},"maxItems":100}}}}}},"responses":{"200":{"description":"갱신된 친구 목록","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RelatedStock"}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/stocks/{symbol}/friends/{friendSymbol}":{"delete":{"operationId":"removeCharacterFriend","tags":["characters"],"summary":"친구 삭제 (캐릭터 소유자만)","description":"친구가 아니어도 성공합니다.","parameters":[{"$ref":"#/components/parameters/Symbol"},{"name":"friendSymbol","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Symbol"}}],"responses":{"200":{"description":"갱신된 친구 목록","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RelatedStock"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/market/price/{symbol}":{"get":{"operationId":"getPrice","tags":["market"],"summary":"현재가 스냅샷 (+ 합성 호가)","security":[],"parameters":[{"$ref":"#/components/parameters/Symbol"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceSnapshot"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/market/prices":{"get":{"operationId":"listPrices","tags":["market"],"summary":"여러 종목 현재가 스냅샷 한 번에","description":"`/v1/market/price/{symbol}`과 같은 스냅샷을 여러 종목 한꺼번에 받습니다. 종목을 여러 개 볼 때는 종목마다 따로 부르지 말고 이걸 쓰세요.\n`symbol`을 1~100번 반복해서 넘깁니다(예: `?symbol=FOXX&symbol=WOLF`). 없는 종목은 결과에서 빠지고, 순서는 요청 순서를 따릅니다.\n시세 조회는 최대 1초 캐시됩니다. 실시간 변동은 웹소켓(`stock:<symbol>` 채널)으로 받는 것이 좋습니다.\n","security":[],"parameters":[{"name":"symbol","in":"query","required":true,"style":"form","explode":true,"schema":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string"}}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PriceSnapshot"}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"}}}},"/v1/market/orderbook/{symbol}":{"get":{"operationId":"getOrderbook","tags":["market"],"summary":"호가","description":"실제 주문장이 아니라 가격 곡선에서 계산한 **합성 호가**입니다. 현재가에서 5원(호가 단위)씩 떨어진 가격마다 `[가격, 잔량]` — 잔량은 그 가격에 체결되는 수량입니다(매도 1호가 잔량만큼 사면 현재가가 그 호가가 됩니다).","security":[],"parameters":[{"$ref":"#/components/parameters/Symbol"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Orderbook"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/market/candles/{symbol}":{"get":{"operationId":"getCandles","tags":["market"],"summary":"캔들(봉) 차트","description":"빈틈없는 봉 최대 500개를 오래된 순으로 돌려줍니다. 최근 1~2일은 실시간 체결 기록에서, 그 이전은 아카이브에서 집계해 합칩니다. 봉 경계는 KST(Asia/Seoul) 기준입니다 — 120·240분봉과 일봉은 KST 00시, 주봉은 월요일, 월봉은 1일, 년봉은 1월 1일에 시작합니다. 진행 중인 봉(지금 분봉, 오늘 일봉 등)도 지금까지의 체결로 포함됩니다.\n\n체결이 없던 봉은 거래정지처럼 직전 종가의 1자 봉(open=high=low=close, volume 0)으로 채웁니다. 상장(첫 체결) 이전은 채우지 않습니다. 과거를 더 받으려면 가장 오래된 봉의 `time`을 `before`로 넘기세요. 500개보다 적게 오면 첫 봉까지 다 받은 것입니다.\n","security":[],"parameters":[{"$ref":"#/components/parameters/Symbol"},{"name":"unit","in":"query","description":"`Nm`(N분봉, N=1~1440 — 차트 화면은 1·3·5·10·15·30·60·120·240), `1d`(일), `1w`(주), `1M`(월), `1y`(년)","schema":{"type":"string","default":"1m","example":"10m"}},{"name":"before","in":"query","description":"봉 시작 시각(epoch 초). 이 시각보다 앞선 봉만 돌려줍니다(과거 페이지).","schema":{"type":"integer","minimum":1,"example":1790226000}}],"responses":{"200":{"description":"오래된 순, 빈틈없이 최대 500개(거래 없던 봉은 volume 0의 1자 봉)","headers":{"x-last-trade-seq":{"description":"이 응답에 반영된 가장 최근 체결의 `seq`(`GET /v1/market/trades`의 `seq`와 같은 값, 체결이 없으면 0). `before` 없는 최신 페이지에서만 의미가 있습니다. 이후 체결 테이프를 이어 붙여 봉을 실시간으로 갱신할 때 이 값 이하의 체결은 이미 반영돼 있으니 건너뛰세요.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Candle"}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/market/trades/{symbol}":{"get":{"operationId":"listTrades","tags":["market"],"summary":"최근 체결 (체결 테이프)","security":[],"parameters":[{"$ref":"#/components/parameters/Symbol"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"최신순","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Trade"}}}}}}}},"/v1/market/popular":{"get":{"operationId":"listPopular","tags":["market"],"summary":"인기 종목(직전 세션 거래대금 순)","description":"홈 화면의 인기순입니다. 3시간 세션이 마감될 때 그 세션의 거래대금(체결 금액 합) 순위를 한 번 정산해 두고, 다음 마감까지 같은 순위를 돌려줍니다. 관리종목(`hidden`)은 빠지고 `rank`는 1부터 다시 매깁니다.\n실시간 순위가 필요하면 `/v1/market/rankings`를 쓰세요. 시세 필드(`lastPrice` 등)는 응답 시점 값이며 최대 10초 캐시됩니다.\n","security":[],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"인기순","content":{"application/json":{"schema":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Stock"},{"type":"object","properties":{"rank":{"type":"integer","description":"인기 순위(1부터)"},"sessionTradingValue":{"type":"integer","description":"직전 마감 세션의 거래대금"}}}]}}}}}}}},"/v1/market/rankings":{"get":{"operationId":"getRankings","tags":["market"],"summary":"실시간 순위 (홈 화면 인기·거래량·급상승·급하락)","description":"관리종목(hidden)은 제외됩니다.","security":[],"parameters":[{"name":"type","in":"query","schema":{"type":"string","enum":["value","volume","gainers","losers"],"default":"value"},"description":"`value` 거래대금순(=인기), `volume` 거래량순, `gainers` 급상승, `losers` 급하락"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RankingItem"}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"}}}},"/v1/accounts/buying-power":{"get":{"operationId":"getBuyingPower","tags":["account"],"summary":"내 잔고(주문가능금액)","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["cashBalance"],"properties":{"cashBalance":{"type":"integer","description":"현금 잔고(원)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/accounts/holdings":{"get":{"operationId":"listHoldings","tags":["account"],"summary":"내 보유종목","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Holding"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/accounts/transactions":{"get":{"operationId":"listAccountTransactions","tags":["account"],"summary":"내 계좌 거래내역(통장)","description":"잔고가 바뀔 때마다 한 행씩 남는 통장입니다. 최신순이고, 행마다 `balanceBefore` → `balanceAfter`가\n이어집니다. 다음 페이지는 `before`에 받은 페이지 마지막 항목의 `id`를 넣으세요.\n- 체결은 한 건이 한 행입니다. `amount`는 원금(`principal`)에 수수료·세금을 반영한 순액입니다.\n  `fee`는 퍼스톡증권, `tax`는 정부 계좌 몫이고, 세션 마감 때 합계로 입금됩니다.\n- 2026-10-26 이전 내역은 없고, 그 시점 잔고가 첫 행 `opening`(잔고 이월)으로 들어 있습니다.\n","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"before","in":"query","schema":{"type":"integer","minimum":1},"description":"이 `id`보다 오래된 항목만"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AccountTransaction"}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/accounts/profile":{"get":{"operationId":"getProfile","tags":["account"],"summary":"내 프로필","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Profile"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"patch":{"operationId":"updateProfile","tags":["account"],"summary":"표시 이름 변경","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["displayName"],"properties":{"displayName":{"type":"string","minLength":1,"maxLength":40}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Profile"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/orders":{"post":{"operationId":"placeOrder","tags":["orders"],"summary":"주문 (시장가·지정가 매수/매도)","description":"- **시장가**(`type: market`): 즉시 체결됩니다. 가격 밴드(세션 시작가 ±30%)에 걸리면 일부만\n  체결(`partial`)되고 나머지는 거부됩니다.\n- **지정가**(`type: limit`, `limitPrice` 필수): `pending`으로 대기열에 들어가고, 가격이\n  조건에 닿는 순간 **전량 체결**됩니다(부분체결 없음). 대기 중엔 현금·주식을 묶어두지 않아서\n  체결 시점에 잔고가 모자라면 계속 대기합니다. 세션 정산(KST 3시간마다) 때 미체결분은\n  `cancelled`(`session_settlement`)됩니다.\n- `clientToken`(UUID)은 멱등키입니다. 네트워크 오류로 재시도할 땐 **같은 값**을 보내면\n  중복 체결 없이 최초 결과를 돌려받습니다. 새 주문마다 새 UUID.\n- 거부도 HTTP 200입니다 — `status: rejected`와 `rejectReason`을 확인하세요.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderRequest"},"examples":{"marketBuy":{"summary":"시장가 매수 10주","value":{"symbol":"FOXX","side":"buy","type":"market","quantity":10,"clientToken":"7f1c0a52-3c1e-4b0e-9a57-6f1d2b8e4c11"}},"limitSell":{"summary":"지정가 매도 5주 @1,500원","value":{"symbol":"FOXX","side":"sell","type":"limit","limitPrice":1500,"quantity":5,"clientToken":"0b9d3f1e-8a2c-4d6e-b1f0-2c7a9e5d3b44"}}}}}},"responses":{"200":{"description":"처리 결과(체결·대기·거부 모두 200)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"listOrders","tags":["orders"],"summary":"주문 이력 / 매매 대기열","description":"최신순. **매매 대기열**(미체결 지정가 주문)은 `status=pending`. 다음 페이지는 `before`에\n이전 페이지 마지막 항목의 `createdAt`을 넣으세요. 목록 항목의 `cashBalanceAfter`/\n`lastPriceAfter`는 항상 0입니다(주문 직후 스냅샷 전용 필드).\n","parameters":[{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/OrderStatus"}},{"name":"symbol","in":"query","schema":{"$ref":"#/components/schemas/Symbol"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"before","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Order"}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/orders/{orderId}":{"get":{"operationId":"getOrder","tags":["orders"],"summary":"주문 단건 조회","description":"오래된 주문은 콜드 아카이브에서 찾아옵니다(조금 느릴 수 있음).","parameters":[{"$ref":"#/components/parameters/OrderId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/orders/{orderId}/cancel":{"post":{"operationId":"cancelOrder","tags":["orders"],"summary":"대기 중인 지정가 주문 취소","description":"`pending` 상태일 때만 가능합니다. 성공하면 `status: cancelled`, `rejectReason: user_cancelled`.","parameters":[{"$ref":"#/components/parameters/OrderId"}],"responses":{"200":{"description":"취소됨","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"이미 체결·취소·거부된 주문","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_pending","message":"order is already filled"}}}}}}},"/v1/community/{symbol}":{"get":{"operationId":"listCommunityPosts","tags":["community"],"summary":"커뮤니티 글 목록","security":[],"parameters":[{"$ref":"#/components/parameters/Symbol"}],"responses":{"200":{"description":"최신순 최대 200개","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CommunityPost"}}}}}}},"post":{"operationId":"createCommunityPost","tags":["community"],"summary":"커뮤니티 글쓰기","parameters":[{"$ref":"#/components/parameters/Symbol"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostBody"}}}},"responses":{"200":{"description":"작성된 글","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPost"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/community/posts/{postId}":{"patch":{"operationId":"updateCommunityPost","tags":["community"],"summary":"내 커뮤니티 글 수정","description":"본인 글만(아니면 404). 서버가 `attributes.edited = true`를 붙입니다.","parameters":[{"name":"postId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostBody"}}}},"responses":{"200":{"description":"수정된 글","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPost"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/watchlist":{"get":{"operationId":"listWatchlist","tags":["watchlist"],"summary":"관심종목 목록","responses":{"200":{"description":"담은 순(최신순)","content":{"application/json":{"schema":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/QuoteItem"},{"type":"object","properties":{"addedAt":{"type":"string","format":"date-time"}}}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/watchlist/{symbol}":{"post":{"operationId":"addToWatchlist","tags":["watchlist"],"summary":"관심종목 담기 (멱등)","parameters":[{"$ref":"#/components/parameters/Symbol"}],"responses":{"200":{"$ref":"#/components/responses/Ok"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"removeFromWatchlist","tags":["watchlist"],"summary":"관심종목 빼기","parameters":[{"$ref":"#/components/parameters/Symbol"}],"responses":{"200":{"$ref":"#/components/responses/Ok"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/recently-viewed":{"get":{"operationId":"listRecentlyViewed","tags":["watchlist"],"summary":"최근 본 종목","responses":{"200":{"description":"최신순 최대 30개","content":{"application/json":{"schema":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/QuoteItem"},{"type":"object","properties":{"viewedAt":{"type":"string","format":"date-time"}}}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/recently-viewed/{symbol}":{"post":{"operationId":"recordRecentlyViewed","tags":["watchlist"],"summary":"최근 본 종목 기록","parameters":[{"$ref":"#/components/parameters/Symbol"}],"responses":{"200":{"$ref":"#/components/responses/Ok"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"removeRecentlyViewed","tags":["watchlist"],"summary":"최근 본 종목에서 제거","parameters":[{"$ref":"#/components/parameters/Symbol"}],"responses":{"200":{"$ref":"#/components/responses/Ok"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/item-groups":{"get":{"operationId":"listItemGroups","tags":["watchlist"],"summary":"종목 그룹 목록 (내 투자/관심 탭의 사용자 정의 그룹)","parameters":[{"name":"kind","in":"query","required":true,"schema":{"type":"string","enum":["holding","watchlist"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ItemGroup"}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"createItemGroup","tags":["watchlist"],"summary":"그룹 만들기","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["kind","name"],"properties":{"kind":{"type":"string","enum":["holding","watchlist"]},"name":{"type":"string","minLength":1,"maxLength":20}}}}}},"responses":{"200":{"description":"만든 그룹","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ItemGroup"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/item-groups/{groupId}":{"patch":{"operationId":"updateItemGroup","tags":["watchlist"],"summary":"그룹 이름·순서 변경","parameters":[{"$ref":"#/components/parameters/GroupId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":20},"sortOrder":{"type":"integer"}}}}}},"responses":{"200":{"$ref":"#/components/responses/Ok"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"deleteItemGroup","tags":["watchlist"],"summary":"그룹 삭제 (멤버는 미분류로 돌아감)","parameters":[{"$ref":"#/components/parameters/GroupId"}],"responses":{"200":{"$ref":"#/components/responses/Ok"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/item-groups/{groupId}/members":{"put":{"operationId":"setItemGroupMembers","tags":["watchlist"],"summary":"그룹 멤버 전체 교체 (이 순서로)","description":"다른 그룹에 있던 종목을 넣으면 이 그룹으로 옮겨집니다.","parameters":[{"$ref":"#/components/parameters/GroupId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["symbols"],"properties":{"symbols":{"type":"array","items":{"$ref":"#/components/schemas/Symbol"}}}}}}},"responses":{"200":{"$ref":"#/components/responses/Ok"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/stocks/{symbol}/journals":{"get":{"operationId":"listJournals","tags":["journals"],"summary":"종목 뉴스·공시 목록","security":[],"parameters":[{"$ref":"#/components/parameters/Symbol"}],"responses":{"200":{"description":"최신순 최대 200개","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StockJournal"}}}}}}},"post":{"operationId":"createJournal","tags":["journals"],"summary":"뉴스 작성 (상장주 소유자만)","description":"`linkUrl`을 주면 서버가 OG 미리보기를 채웁니다. 소유자는 `kind`를 보내도 항상 `news`.","parameters":[{"$ref":"#/components/parameters/Symbol"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockJournalBody"}}}},"responses":{"201":{"description":"작성됨","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockJournal"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/stocks/{symbol}/journals/{journalId}":{"parameters":[{"$ref":"#/components/parameters/Symbol"},{"name":"journalId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"getJournal","tags":["journals"],"summary":"뉴스·공시 단건","security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockJournal"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"operationId":"updateJournal","tags":["journals"],"summary":"내 뉴스 수정","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockJournalBody"}}}},"responses":{"200":{"description":"수정된 글","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockJournal"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"deleteJournal","tags":["journals"],"summary":"내 뉴스 삭제","responses":{"204":{"description":"삭제됨"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/link-preview":{"post":{"operationId":"getLinkPreview","tags":["misc"],"summary":"링크 미리보기 (OG 메타 스크래핑)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","pattern":"^https?://","description":"http:// 또는 https://로 시작해야 함"}}}}}},"responses":{"200":{"description":"없는 값은 null","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkPreview"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/stocks/{symbol}/gallery":{"get":{"operationId":"listGalleryPhotos","tags":["gallery"],"summary":"갤러리 사진 목록 (페이지당 24장)","description":"인증은 선택 — 토큰을 보내면 `heartedByMe`가 채워집니다.","security":[{},{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/Symbol"},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["items","page","pageSize","totalCount"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/GalleryPhoto"}},"page":{"type":"integer"},"pageSize":{"type":"integer"},"totalCount":{"type":"integer"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"post":{"operationId":"uploadGalleryPhoto","tags":["gallery"],"summary":"사진 올리기 (상장주 소유자만)","parameters":[{"$ref":"#/components/parameters/Symbol"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["title","image"],"properties":{"title":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":2000},"tags":{"type":"string","description":"JSON 문자열 배열, 예: [\"팬아트\",\"공식\"]"},"image":{"type":"string","format":"binary","description":"png/jpeg/webp/gif, 최대 25MB"}}}}}},"responses":{"201":{"description":"올라간 사진","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryPhoto"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/stocks/{symbol}/gallery/{photoId}":{"parameters":[{"$ref":"#/components/parameters/Symbol"},{"$ref":"#/components/parameters/PhotoId"}],"patch":{"operationId":"updateGalleryPhoto","tags":["gallery"],"summary":"사진 정보 수정 (소유자만)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":100},"description":{"type":["string","null"],"maxLength":2000},"tags":{"type":"array","items":{"type":"string","maxLength":20},"maxItems":20}}}}}},"responses":{"200":{"description":"수정된 사진","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryPhoto"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"deleteGalleryPhoto","tags":["gallery"],"summary":"사진 삭제 (소유자만)","responses":{"204":{"description":"삭제됨"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/stocks/{symbol}/gallery/{photoId}/view":{"post":{"operationId":"recordGalleryView","tags":["gallery"],"summary":"조회 기록 (사람당 1회만 집계)","parameters":[{"$ref":"#/components/parameters/Symbol"},{"$ref":"#/components/parameters/PhotoId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"viewCount":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/stocks/{symbol}/gallery/{photoId}/heart":{"parameters":[{"$ref":"#/components/parameters/Symbol"},{"$ref":"#/components/parameters/PhotoId"}],"post":{"operationId":"heartGalleryPhoto","tags":["gallery"],"summary":"좋아요","responses":{"200":{"$ref":"#/components/responses/HeartState"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"unheartGalleryPhoto","tags":["gallery"],"summary":"좋아요 취소","responses":{"200":{"$ref":"#/components/responses/HeartState"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/stocks/{symbol}/gallery/{photoId}/comments":{"parameters":[{"$ref":"#/components/parameters/Symbol"},{"$ref":"#/components/parameters/PhotoId"}],"get":{"operationId":"listGalleryComments","tags":["gallery"],"summary":"사진 댓글 목록(페이지 단위)","description":"`before` 없이 부르면 가장 최근 `limit`개, `before`에 받은 페이지의 첫(가장 오래된) 댓글 id를 넣으면 그보다 앞선 `limit`개를 줍니다. 각 페이지는 시간 오름차순(최신이 아래)입니다. 더 앞선 댓글이 있으면 응답 헤더 `x-has-more: true`입니다.\n","security":[],"parameters":[{"name":"before","in":"query","schema":{"type":"string","format":"uuid"},"description":"이 댓글보다 앞선 댓글을 받는다"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"OK","headers":{"x-has-more":{"description":"이 페이지보다 앞선 댓글이 더 있으면 `true`","schema":{"type":"string","enum":["true","false"]}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GalleryComment"}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"}}},"post":{"operationId":"createGalleryComment","tags":["gallery"],"summary":"댓글 쓰기","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryCommentBody"}}}},"responses":{"201":{"description":"작성된 댓글","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryComment"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/stocks/{symbol}/gallery/comments/{commentId}":{"parameters":[{"$ref":"#/components/parameters/Symbol"},{"name":"commentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"patch":{"operationId":"updateGalleryComment","tags":["gallery"],"summary":"댓글 수정 (작성자 또는 상장주 소유자)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryCommentBody"}}}},"responses":{"200":{"description":"수정된 댓글","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryComment"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"deleteGalleryComment","tags":["gallery"],"summary":"댓글 삭제 (작성자 또는 상장주 소유자)","responses":{"204":{"description":"삭제됨"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/characters":{"post":{"operationId":"applyForListing","tags":["characters"],"summary":"캐릭터 상장 신청","description":"관리자 심사 후 상장됩니다. 종목명(symbol)은 `name` 그대로(유니코드 문자·숫자 1~20자, 공백·특수문자 불가).","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["name","image"],"properties":{"name":{"$ref":"#/components/schemas/Symbol"},"tags":{"type":"string","description":"JSON 문자열 배열(최대 20개, 각 20자), 예: [\"여우\",\"수인\"]"},"bio":{"type":"string","maxLength":2000},"snsLinks":{"type":"string","description":"JSON 배열, 예: [{\"platform\":\"X\",\"url\":\"https://x.com/me\"}]. url은 http:// 또는 https://로 시작해야 함(아니면 400)"},"image":{"type":"string","format":"binary","description":"대표 이미지 png/jpeg/webp, 최대 5MB"}}}}}},"responses":{"201":{"description":"신청 접수(status=pending)","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"symbol":{"$ref":"#/components/schemas/Symbol"},"status":{"type":"string","example":"pending"},"createdAt":{"type":"string","format":"date-time"}}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"409":{"description":"이미 쓰이는 종목명","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"duplicate_symbol"}}}}}}},"/v1/characters/mine":{"get":{"operationId":"listMyListings","tags":["characters"],"summary":"내 상장 신청·상장종목","responses":{"200":{"description":"최신순","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MyListing"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/characters/{characterId}":{"patch":{"operationId":"updateMyListing","tags":["characters"],"summary":"내 상장종목 정보 수정 (태그·BIO·SNS·대표 이미지)","description":"종목명은 여기서 못 바꿉니다(종목명 변경 신청 사용). 이미지를 안 보내면 기존 유지.","parameters":[{"$ref":"#/components/parameters/CharacterId"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"tags":{"type":"string","description":"JSON 문자열 배열"},"bio":{"type":"string","maxLength":2000},"snsLinks":{"type":"string","description":"JSON 배열(POST와 같은 형식). url은 http:// 또는 https://로 시작해야 함(아니면 400)"},"image":{"type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/characters/{characterId}/symbol-change-requests":{"post":{"operationId":"requestSymbolChange","tags":["characters"],"summary":"종목명 변경 신청 (관리자 심사)","parameters":[{"$ref":"#/components/parameters/CharacterId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["newSymbol"],"properties":{"newSymbol":{"$ref":"#/components/schemas/Symbol"}}}}}},"responses":{"201":{"description":"접수됨","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"newSymbol":{"$ref":"#/components/schemas/Symbol"},"createdAt":{"type":"string","format":"date-time"}}}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"이미 심사 중인 신청이 있음(`already_pending`) 또는 쓰이는 이름","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/characters/{characterId}/cover-image":{"get":{"operationId":"getCoverImage","tags":["characters"],"summary":"캐릭터 대표 이미지 (레거시 — 종목의 `coverImageUrl`을 쓰세요)","security":[],"parameters":[{"$ref":"#/components/parameters/CharacterId"}],"responses":{"200":{"description":"이미지 바이너리"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/feedback":{"post":{"operationId":"sendFeedback","tags":["misc"],"summary":"의견 보내기 (만족도 1~5)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["rating"],"properties":{"rating":{"type":"integer","minimum":1,"maximum":5},"comment":{"type":"string","maxLength":2000}}}}}},"responses":{"200":{"$ref":"#/components/responses/Ok"},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/health":{"get":{"operationId":"health","tags":["misc"],"summary":"헬스체크","security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApiSpec","tags":["misc"],"summary":"이 스펙 (JSON)","security":[],"responses":{"200":{"description":"OpenAPI 3.1 문서"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"`POST /oauth2/token`으로 받은 access token(1시간). 퍼스톡 웹의 로그인 세션 토큰도 같은\n헤더로 받지만, 외부 프로그램은 API 키 토큰을 쓰세요. API 키 토큰으로는 API 키 관리와 회원\n탈퇴를 할 수 없고(403 `session_required`), 관리자 권한도 쓰이지 않습니다.\n"}},"parameters":{"Symbol":{"name":"symbol","in":"path","required":true,"description":"종목명(= symbol). 한글이면 URL 인코딩하세요.","schema":{"$ref":"#/components/schemas/Symbol"}},"OrderId":{"name":"orderId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"GroupId":{"name":"groupId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"PhotoId":{"name":"photoId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"CharacterId":{"name":"characterId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}},"responses":{"Ok":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}}}}}},"HeartState":{"description":"현재 좋아요 상태","content":{"application/json":{"schema":{"type":"object","properties":{"heartCount":{"type":"integer"},"heartedByMe":{"type":"boolean"}}}}}},"Unauthorized":{"description":"토큰 없음·무효·만료, 또는 폐기된 키","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"unauthorized"}}}},"Forbidden":{"description":"권한 없음(예. 상장주 소유자가 아님)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"forbidden"}}}},"SessionRequired":{"description":"브라우저 로그인 세션 전용 작업을 API 키 토큰으로 호출함","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"session_required"}}}},"NotFound":{"description":"대상 없음","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found"}}}},"InvalidRequest":{"description":"요청 형식 오류","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_request","message":"..."}}}},"RateLimited":{"description":"요청 한도 초과. `Retry-After`(초) 뒤에 다시 시도하세요.","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"rate_limited","retryAfterSeconds":1}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"기계가 읽는 오류 코드"},"message":{"type":"string","description":"사람이 읽는 설명(있을 때만)"},"issues":{"type":"array","items":{},"description":"검증 실패 상세(zod issues)"},"retryAfterSeconds":{"type":"integer"},"reqId":{"type":"string"}}},"TokenRequest":{"type":"object","required":["grant_type","client_id","client_secret"],"properties":{"grant_type":{"type":"string","const":"client_credentials"},"client_id":{"type":"string","example":"fsk_0123456789abcdef0123456789abcdef"},"client_secret":{"type":"string"}}},"ApiKey":{"type":"object","required":["clientId","name","createdAt","lastUsedAt","revokedAt"],"properties":{"clientId":{"type":"string"},"name":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time","description":"마지막 토큰 교환 시각"},"revokedAt":{"type":["string","null"],"format":"date-time"}}},"ApiKeyCreated":{"allOf":[{"$ref":"#/components/schemas/ApiKey"},{"type":"object","required":["clientSecret"],"properties":{"clientSecret":{"type":"string","description":"이 응답에서만 보이는 비밀키"}}}]},"Symbol":{"type":"string","description":"종목 식별자 = 캐릭터 풀네임(유니코드 문자/숫자 1~20자, 예. `FOXX`, `보팔래빗`)","pattern":"^[\\p{L}\\p{N}]{1,20}$","example":"FOXX"},"Stock":{"type":"object","properties":{"symbol":{"$ref":"#/components/schemas/Symbol"},"name":{"type":"string"},"species":{"type":["string","null"],"description":"종족 카테고리"},"status":{"type":"string","enum":["listed","delisted"]},"tags":{"type":"array","items":{"type":"string"}},"lastPrice":{"type":"integer","description":"현재가(원)"},"changeRate":{"type":"number","description":"세션 시작가 대비 등락률(0.05 = +5%)"},"volume":{"type":"integer","description":"현재 세션 누적 거래량"},"characterId":{"type":"string","format":"uuid"},"coverImageUrl":{"type":["string","null"],"format":"uri","description":"대표 이미지"},"listedAt":{"type":"string","format":"date-time"},"tradingHalted":{"type":"boolean","description":"관리자 거래정지 여부 — true면 주문이 거부됨"},"haltReason":{"type":["string","null"]},"hidden":{"type":"boolean","description":"관리종목(검색·순위에서 빠짐, 거래는 가능)"},"hideReason":{"type":["string","null"]},"bio":{"type":["string","null"],"description":"캐릭터 소개(마크다운)"}}},"StockDisclosure":{"type":"object","properties":{"kind":{"type":"string","enum":["trading_halted","trading_resumed","hidden","unhidden","symbol_changed"]},"message":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"RelatedStock":{"type":"object","required":["symbol","coverImageUrl","lastPrice","changeRate"],"properties":{"symbol":{"$ref":"#/components/schemas/Symbol"},"coverImageUrl":{"type":["string","null"],"format":"uri","description":"대표 이미지"},"lastPrice":{"type":"integer","description":"현재가(원)"},"changeRate":{"type":"number","description":"세션 시작가 대비 등락률(0.05 = +5%)"}}},"QuoteItem":{"type":"object","properties":{"symbol":{"$ref":"#/components/schemas/Symbol"},"name":{"type":"string"},"species":{"type":["string","null"]},"lastPrice":{"type":"integer"},"changeRate":{"type":"number"},"volume":{"type":"integer"}}},"RankingItem":{"allOf":[{"$ref":"#/components/schemas/QuoteItem"},{"type":"object","properties":{"tradingValue":{"type":"integer","description":"거래대금 = lastPrice × volume"}}}]},"Orderbook":{"type":"object","properties":{"bids":{"type":"array","description":"매수 호가(높은 가격부터)","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","description":"매도 호가(낮은 가격부터)","items":{"$ref":"#/components/schemas/OrderbookLevel"}}}},"OrderbookLevel":{"type":"array","description":"[가격, 잔량] — 가격은 5원 단위","prefixItems":[{"type":"integer"},{"type":"integer"}],"minItems":2,"maxItems":2},"PriceSnapshot":{"type":"object","properties":{"symbol":{"$ref":"#/components/schemas/Symbol"},"sessionId":{"type":["string","null"],"format":"uuid"},"seq":{"type":"integer","description":"단조 증가 번호"},"lastPrice":{"type":"integer"},"openPrice":{"type":"integer","description":"세션 시작가"},"high":{"type":"integer"},"low":{"type":"integer"},"bandLow":{"type":"integer","description":"하한가(시작가 −30%, 최저 500원)"},"bandHigh":{"type":"integer","description":"상한가(시작가 +30%)"},"volume":{"type":"integer"},"orderbook":{"$ref":"#/components/schemas/Orderbook"},"ts":{"type":"integer","description":"epoch ms"}}},"Candle":{"type":"object","properties":{"time":{"type":"integer","description":"봉 시작 시각(epoch 초)"},"open":{"type":"integer"},"high":{"type":"integer"},"low":{"type":"integer"},"close":{"type":"integer"},"volume":{"type":"integer"}}},"Trade":{"type":"object","properties":{"symbol":{"$ref":"#/components/schemas/Symbol"},"seq":{"type":"integer","description":"체결 고유 번호(단조 증가)"},"price":{"type":"integer"},"quantity":{"type":"integer"},"side":{"type":"string","enum":["buy","sell"],"description":"체결을 일으킨 주문 방향"},"ts":{"type":"integer","description":"epoch ms"}}},"Holding":{"type":"object","properties":{"symbol":{"$ref":"#/components/schemas/Symbol"},"species":{"type":["string","null"]},"quantity":{"type":"integer"},"avgCost":{"type":"number","description":"평균 매입단가"},"lastPrice":{"type":"integer"},"marketValue":{"type":"integer","description":"평가금액 = quantity × lastPrice"},"unrealizedPnl":{"type":"integer","description":"평가손익"}}},"Profile":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"displayName":{"type":"string"}}},"AccountTransaction":{"type":"object","required":["id","kind","direction","amount","balanceBefore","balanceAfter","counterpartyId","counterpartyName","symbol","quantity","principal","fee","tax","memo","loanBalanceAfter","createdAt"],"properties":{"id":{"type":"integer"},"kind":{"type":"string","enum":["opening","signup","buy","sell","fee_income","tax_income","issuance","public_fund_loan","public_fund_repayment","public_fund_reclaim","dividend","tax_refund","adjustment"],"description":"`opening` 잔고 이월, `signup` 가입 지원금(정부가 발행해 지급), `buy`/`sell` 체결, `fee_income` 세션 수수료 입금(퍼스톡증권),\n`tax_income` 세션 세금 입금(정부), `issuance` 통화 발행(정부), `public_fund_loan` 공적자금 대출,\n`public_fund_repayment` 대출 원금 상환, `public_fund_reclaim` 초과이익 환수, `dividend` 배당, `tax_refund` 세금 환급,\n`adjustment` 관리자 조정\n"},"direction":{"type":"string","enum":["deposit","withdrawal"],"description":"입금/출금"},"amount":{"type":"integer","description":"잔고 변화량. 입금 +, 출금 −"},"balanceBefore":{"type":"integer","description":"거래 전 잔고"},"balanceAfter":{"type":"integer","description":"거래 후 잔고"},"counterpartyId":{"type":["string","null"],"format":"uuid","description":"상대 계좌. 체결은 null(시장)"},"counterpartyName":{"type":["string","null"]},"symbol":{"type":["string","null"],"description":"체결 종목"},"quantity":{"type":["integer","null"],"description":"체결 수량(주)"},"principal":{"type":["integer","null"],"description":"체결 원금(수수료·세금 제외)"},"fee":{"type":"integer","description":"수수료(퍼스톡증권 몫)"},"tax":{"type":"integer","description":"세금(정부 몫)"},"memo":{"type":["string","null"]},"loanBalanceAfter":{"type":["integer","null"],"description":"공적자금 행만: 이 거래 뒤 대출 잔액"},"createdAt":{"type":"string","format":"date-time"}}},"OrderStatus":{"type":"string","enum":["filled","partial","rejected","pending","cancelled"],"description":"`filled` 전량체결 · `partial` 일부체결(나머지 거부) · `rejected` 거부 · `pending` 지정가 대기 ·\n`cancelled` 취소(세션 정산·거래정지·사용자 취소)\n"},"RejectReason":{"type":["string","null"],"enum":["market_closed","insufficient_cash","insufficient_shares","price_band_reached","circuit_breaker","stock_delisted","qty_not_positive","duplicate_client_token","session_settlement","admin_halted","user_cancelled",null]},"OrderRequest":{"type":"object","required":["symbol","side","quantity","clientToken"],"properties":{"symbol":{"$ref":"#/components/schemas/Symbol"},"side":{"type":"string","enum":["buy","sell"]},"type":{"type":"string","enum":["market","limit"],"default":"market"},"quantity":{"type":"integer","minimum":1},"limitPrice":{"type":"integer","minimum":5,"multipleOf":5,"description":"type=limit일 때 필수(원, 호가 단위 5원의 배수)"},"clientToken":{"type":"string","format":"uuid","description":"멱등키 — 새 주문마다 새 UUID, 재시도엔 같은 값"}}},"Order":{"type":"object","properties":{"orderId":{"type":"string","format":"uuid"},"symbol":{"$ref":"#/components/schemas/Symbol"},"sessionId":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["buy","sell"]},"type":{"type":"string","enum":["market","limit"]},"limitPrice":{"type":["integer","null"]},"status":{"$ref":"#/components/schemas/OrderStatus"},"rejectReason":{"$ref":"#/components/schemas/RejectReason"},"quantityRequested":{"type":"integer"},"quantityFilled":{"type":"integer"},"avgPrice":{"type":"integer","description":"평균 체결가(5원 단위","미체결이면 0)":null},"notional":{"type":"integer","description":"체결 금액"},"fee":{"type":"integer","description":"수수료(체결 금액의 0.15%)"},"tax":{"type":"integer","description":"매도 세금(체결 금액의 0.2%)"},"cashBalanceAfter":{"type":"integer","description":"주문 직후 현금 잔고(POST 응답에서만 의미 있음)"},"lastPriceAfter":{"type":"integer","description":"주문 직후 현재가(POST 응답에서만 의미 있음)"},"createdAt":{"type":"string","format":"date-time"}}},"CommunityPostBody":{"type":"object","required":["body"],"properties":{"body":{"type":"string","minLength":1,"maxLength":6000,"description":"마크다운 원문"}}},"CommunityPost":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"symbol":{"$ref":"#/components/schemas/Symbol"},"userId":{"type":"string","format":"uuid"},"authorName":{"type":"string"},"body":{"type":"string"},"attributes":{"type":"object","additionalProperties":true,"description":"자유 형식 확장 필드. 지금은 `edited`(수정 여부)만."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"ItemGroup":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["holding","watchlist"]},"name":{"type":"string"},"sortOrder":{"type":"integer"},"members":{"type":"array","items":{"type":"object","properties":{"symbol":{"$ref":"#/components/schemas/Symbol"},"sortOrder":{"type":"integer"}}}}}},"StockJournalBody":{"type":"object","required":["title","body"],"properties":{"title":{"type":"string","minLength":1,"maxLength":200},"body":{"type":"string","minLength":1,"maxLength":20000,"description":"마크다운"},"linkUrl":{"type":["string","null"],"format":"uri","pattern":"^https?://","description":"http:// 또는 https://로 시작해야 함(아니면 400)"},"kind":{"type":"string","enum":["news","disclosure"],"description":"관리자만 의미 있음"}}},"StockJournal":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"symbol":{"$ref":"#/components/schemas/Symbol"},"authorId":{"type":"string","format":"uuid"},"authorName":{"type":"string"},"authorType":{"type":"string","enum":["owner","admin"]},"kind":{"type":"string","enum":["news","disclosure"]},"title":{"type":"string"},"body":{"type":"string"},"linkUrl":{"type":["string","null"]},"linkTitle":{"type":["string","null"]},"linkDescription":{"type":["string","null"]},"linkImageUrl":{"type":["string","null"]},"linkSiteName":{"type":["string","null"]},"edited":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"LinkPreview":{"type":"object","properties":{"title":{"type":["string","null"]},"description":{"type":["string","null"]},"imageUrl":{"type":["string","null"]},"siteName":{"type":["string","null"]}}},"GalleryPhoto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"symbol":{"$ref":"#/components/schemas/Symbol"},"title":{"type":"string"},"description":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"category":{"type":["string","null"]},"imageUrl":{"type":"string","format":"uri"},"originalUrl":{"type":"string","format":"uri"},"uploaderId":{"type":"string","format":"uuid"},"viewCount":{"type":"integer"},"heartCount":{"type":"integer"},"heartedByMe":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}},"GalleryCommentBody":{"type":"object","required":["body"],"properties":{"body":{"type":"string","minLength":1,"maxLength":1000}}},"GalleryComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"imageId":{"type":"string","format":"uuid"},"authorId":{"type":"string","format":"uuid"},"authorName":{"type":"string"},"body":{"type":"string"},"edited":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"MyListing":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"symbol":{"$ref":"#/components/schemas/Symbol"},"name":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"bio":{"type":["string","null"]},"snsLinks":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string"},"url":{"type":"string","format":"uri","pattern":"^https?://"}}}},"status":{"type":"string","enum":["pending","listed","delisted","rejected"]},"rejectionReason":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"coverImageUrl":{"type":"string","format":"uri"},"pendingSymbolChange":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"newSymbol":{"$ref":"#/components/schemas/Symbol"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}