---
openapi: "3.0.1"
info:
  title: "Heymoa API"
  description: "Heymoa 서버 REST API"
  version: "1.0.0"
servers:
- url: "/"
tags: []
paths:
  /internal/v1/callbacks/analyses/{analysisId}:
    post:
      tags:
      - "AnalysisCallback"
      summary: "분석 결과 callback"
      description: "heymoa-ai가 분석 완료 또는 실패 결과를 status로 구분해 전송한다."
      operationId: "analysisCallback"
      parameters:
      - name: "analysisId"
        in: "path"
        description: "분석 잡의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "분석 잡의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0K9GVJT2C4Q1Z"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AnalysisResultCallback"
            examples:
              analysisCallback_Succeeded:
                value:
                  status: "SUCCEEDED"
                  analysisId: "0K9GVJT2C4Q1Z"
                  sections:
                  - kind: "OVERVIEW"
                    items:
                    - content: "결제 실패율이 3%로 올랐다"
                      evidence:
                      - "0HZX2K7M9Q4AH"
                  - kind: "ACTION_ITEM"
                    items:
                    - content: "이번 주 안에 원인을 좁힌다"
                      evidence: []
                  - kind: "DECISION"
                    items: []
              analysisCallback_Failed:
                value:
                  status: "FAILED"
                  error:
                    code: "TIMEOUT"
                    message: "시간 초과"
      responses:
        "204":
          description: "처리 성공"
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - internalToken: []
  /internal/v1/notes/{noteId}/context:
    get:
      tags:
      - "InternalAgentContext"
      summary: "AI 내부 — 노트 컨텍스트"
      description: "scope=note 챗봇 컨텍스트 주입용 전사+요약 통째 조회. heymoa-ai가 메시지 처리 시 당겨간다.\
        \ X-Internal-Token 공유 시크릿 필요 (+네트워크 차단은 추가 방어선)."
      operationId: "getInternalNoteContext"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트 식별자"
        required: true
        schema:
          type: "string"
          description: "노트 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      responses:
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InternalNoteContextResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      noteId: "0HZX2K7M9Q4AF"
                      title: "7월 3주차 스프린트 회의"
                      meetingStatus: "ENDED"
                      transcript:
                      - text: "이번 주 배포 이슈부터 볼게요"
                        startMs: 0
                      analysis:
                        overview: "배포 이슈 논의…"
                        actionItems: "- APP-123 수정…"
                        decisions: "- 릴리즈 주기…"
                    error: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - internalToken: []
  /internal/v1/workspaces/{workspaceId}/notes:
    get:
      tags:
      - "InternalAgentContext"
      summary: "AI 내부 — 워크스페이스 노트 목록"
      description: "heymoa-ai의 agentic 검색 인덱스용 노트 목록. X-Internal-Token 공유 시크릿 필요 (+네\
        트워크 차단은 추가 방어선)."
      operationId: "getInternalWorkspaceNotes"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: "워크스페이스 식별자"
        required: true
        schema:
          type: "string"
          description: "워크스페이스 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      responses:
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InternalWorkspaceNotesResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      notes:
                      - noteId: "0HZX2K7M9Q4AF"
                        title: "7월 3주차 스프린트 회의"
                        meetingStatus: "ENDED"
                        createdAt: "2026-07-22T00:00:00Z"
                        hasAnalysis: true
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - internalToken: []
  /v1/agent-chats:
    post:
      tags:
      - "AgentChat"
      summary: "채팅 세션 생성"
      description: "연결할 노트에 접근할 수 없으면 404로 응답한다."
      operationId: "createAgentChat"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAgentChatV2Request"
            examples:
              createAgentChat_NoteNotFoundException:
                value:
                  scope: "note"
                  noteId: "0HZX2K7M9Q4AF"
                  title: "회의 후속"
              createAgentChat_WorkspaceNotFoundException:
                value:
                  scope: "workspace"
                  workspaceId: "0HZX2K7M9Q4AD"
                  title: "워크스페이스 질문"
              createAgentChat_Success:
                value:
                  scope: "note"
                  noteId: "0HZX2K7M9Q4AF"
                  title: "회의 후속"
              createAgentChat_ProjectNotFoundException:
                value:
                  scope: "note"
                  noteId: "0HZX2K7M9Q4AF"
                  title: "회의 후속"
              createAgentChat_BadRequest:
                value:
                  scope: "workspace"
                  title: "워크스페이스 질문"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
        "201":
          description: "생성 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentChatV2Response"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      chatId: "0K9GVJT2C4Q2A"
                      scope: "note"
                      workspaceId: null
                      noteId: "0HZX2K7M9Q4AF"
                      title: "회의 후속"
                      createdAt: "2026-07-22T00:00:00Z"
                    error: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/notifications:
    get:
      tags:
      - "Notifications"
      summary: "알림 목록 조회"
      description: "현재 사용자의 알림을 최신순으로 최대 50개 조회하고 읽지 않은 알림 개수를 함께 반환한다."
      operationId: "getNotifications"
      responses:
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotificationListResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      notifications:
                      - notificationId: "0HZX2K7M9Q4AJ"
                        type: "WORKSPACE_INVITATION"
                        readAt: "2026-07-14T01:02:03Z"
                        createdAt: "2026-07-14T01:02:03Z"
                        invitation:
                          invitationId: "0HZX2K7M9Q4AK"
                          status: "PENDING"
                          role: "MEMBER"
                          workspaceId: "0HZX2K7M9Q4AD"
                          workspaceName: "제품 팀"
                          inviterName: "홍길동"
                          expiresAt: "2026-07-15T01:02:03Z"
                      unreadCount: 0
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces:
    get:
      tags:
      - "Workspaces"
      summary: "워크스페이스 목록 조회"
      description: "현재 사용자가 참여 중인 워크스페이스 목록을 합류한 순서로 조회한다."
      operationId: "getWorkspaces"
      responses:
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceListResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      workspaces:
                      - workspaceId: "0HZX2K7M9Q4AD"
                        name: "제품 팀"
                        description: "제품을 만드는 팀의 워크스페이스"
                        role: "ADMIN"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    post:
      tags:
      - "Workspaces"
      summary: "워크스페이스 생성"
      description: "현재 사용자가 관리할 새 워크스페이스를 생성한다."
      operationId: "createWorkspace"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWorkspaceRequest"
            examples:
              createWorkspace_BadRequest:
                value:
                  name: ""
                  description: null
              createWorkspace_Success:
                value:
                  name: "제품 팀"
                  description: "제품을 만드는 팀의 워크스페이스"
      responses:
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "201":
          description: "생성 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateWorkspaceResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      workspaceId: "0HZX2K7M9Q4AD"
                      name: "제품 팀"
                      description: "제품을 만드는 팀의 워크스페이스"
                      role: "ADMIN"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/agent-chats/active:
    get:
      tags:
      - "AgentChat"
      summary: "활성 세션 조회"
      description: "scope와 대상 식별자가 일치하지 않으면 400으로 응답한다 (workspace→workspaceId 필수,\
        \ note→noteId 필수)."
      operationId: "getActiveAgentChat"
      parameters:
      - name: "scope"
        in: "query"
        description: "컨텍스트 범위 (workspace: 워크스페이스 전체, note: 노트 단건)"
        required: true
        schema:
          type: "string"
          description: "컨텍스트 범위 (workspace: 워크스페이스 전체, note: 노트 단건)"
          nullable: false
          enum:
          - "workspace"
          - "note"
        example: "workspace"
      - name: "workspaceId"
        in: "query"
        description: "조회할 워크스페이스 TSID (scope=workspace일 때 필수)"
        required: false
        schema:
          type: "string"
          description: "조회할 워크스페이스 TSID (scope=workspace일 때 필수)"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      - name: "noteId"
        in: "query"
        description: "조회할 노트 TSID (scope=note일 때 필수)"
        required: false
        schema:
          type: "string"
          description: "조회할 노트 TSID (scope=note일 때 필수)"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      responses:
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentChatV2NullableResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      chatId: "0K9GVJT2C4Q2A"
                      scope: "workspace"
                      workspaceId: "0HZX2K7M9Q4AD"
                      noteId: null
                      title: "워크스페이스 질문"
                      createdAt: "2026-07-22T00:00:00Z"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/auth/logout:
    post:
      tags:
      - "Auth"
      summary: "로그아웃"
      description: "현재 세션을 폐기하고 access token과 refresh token 쿠키를 만료시킨다."
      operationId: "logout"
      parameters:
      - name: "refresh_token"
        in: "cookie"
        description: "refresh_token 쿠키"
        required: false
        schema:
          type: "string"
        example: "refresh-token-example"
      responses:
        "200":
          description: "요청 성공"
          headers:
            Set-Cookie:
              description: "만료된 access_token 및 refresh_token HttpOnly 쿠키"
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LogoutResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      message: "성공적으로 로그아웃했습니다."
                    error: null
      security: []
  /v1/auth/refresh:
    post:
      tags:
      - "Auth"
      summary: "토큰 갱신"
      description: "리프레시 토큰 쿠키로 access token과 refresh token을 갱신한다."
      operationId: "refreshTokens"
      parameters:
      - name: "refresh_token"
        in: "cookie"
        description: "refresh_token 쿠키"
        required: false
        schema:
          type: "string"
        example: "refresh-token-example"
      - name: "User-Agent"
        in: "header"
        description: "클라이언트 User-Agent"
        required: false
        schema:
          type: "string"
        example: "Heymoa Docs Client"
      - name: "X-Forwarded-For"
        in: "header"
        description: "프록시가 전달한 클라이언트 IP 목록"
        required: false
        schema:
          type: "string"
        example: "203.0.113.10, 10.0.0.1"
      responses:
        "401":
          description: "인증 필요"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVALID_REFRESH_TOKEN:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVALID_REFRESH_TOKEN"
                      message: "세션이 만료되었습니다. 다시 로그인해 주세요."
                      details: null
        "200":
          description: "요청 성공"
          headers:
            Set-Cookie:
              description: "갱신된 access_token 및 refresh_token HttpOnly 쿠키"
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RefreshTokensResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      message: "토큰이 갱신되었습니다."
                    error: null
      security: []
  /v1/invitations/accept-by-token:
    post:
      tags:
      - "Workspace Invitations"
      summary: "토큰으로 초대 수락"
      description: "초대 이메일 링크의 토큰으로 초대를 수락하고 워크스페이스 멤버가 된다. 로그인 계정 이메일이 초대받은 이메일과\
        \ 같아야 하며, 링크는 생성 1일 후 만료된다."
      operationId: "acceptInvitationByToken"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AcceptInvitationByTokenRequest"
            examples:
              acceptInvitationByToken_InvitationExpiredException:
                value:
                  token: "3q2wF0rQvXzYbA1cD2eF3gH4iJ5kL6mN7oP8rS9tUvw"
              acceptInvitationByToken_AlreadyWorkspaceMemberException:
                value:
                  token: "3q2wF0rQvXzYbA1cD2eF3gH4iJ5kL6mN7oP8rS9tUvw"
              acceptInvitationByToken_InvitationNotFoundException:
                value:
                  token: "3q2wF0rQvXzYbA1cD2eF3gH4iJ5kL6mN7oP8rS9tUvw"
              acceptInvitationByToken_BadRequest:
                value:
                  token: ""
              acceptInvitationByToken_Success:
                value:
                  token: "3q2wF0rQvXzYbA1cD2eF3gH4iJ5kL6mN7oP8rS9tUvw"
              acceptInvitationByToken_InvitationNotPendingException:
                value:
                  token: "3q2wF0rQvXzYbA1cD2eF3gH4iJ5kL6mN7oP8rS9tUvw"
              acceptInvitationByToken_InvitationEmailMismatchException:
                value:
                  token: "3q2wF0rQvXzYbA1cD2eF3gH4iJ5kL6mN7oP8rS9tUvw"
      responses:
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVITATION_EXPIRED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_EXPIRED"
                      message: "만료된 초대입니다."
                      details: null
                ALREADY_WORKSPACE_MEMBER:
                  value:
                    success: false
                    data: null
                    error:
                      code: "ALREADY_WORKSPACE_MEMBER"
                      message: "이미 워크스페이스 멤버입니다."
                      details: null
                INVITATION_NOT_PENDING:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_NOT_PENDING"
                      message: "이미 처리된 초대입니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVITATION_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_NOT_FOUND"
                      message: "초대를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceInvitationActionResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      invitationId: "0HZX2K7M9Q4AK"
                      workspaceId: "0HZX2K7M9Q4AD"
                      role: "MEMBER"
                      status: "ACCEPTED"
                    error: null
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVITATION_EMAIL_MISMATCH:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_EMAIL_MISMATCH"
                      message: "초대 대상 이메일이 아닙니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/notes/{noteId}:
    get:
      tags:
      - "Notes"
      summary: "노트 조회"
      description: "노트의 기본 정보를 조회한다."
      operationId: "getNote"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NoteResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      noteId: "0HZX2K7M9Q4AF"
                      projectId: "0HZX2K7M9Q4AE"
                      title: "주간 회의"
                      createdAt: "2026-07-14T01:02:03Z"
                      updatedAt: "2026-07-14T01:02:03Z"
                      meetingStatus: "IN_PROGRESS"
                      meetingStartedAt: "2026-07-14T01:02:03Z"
                      recordedDurationMs: 6500
                      activeSessionStartedAt: "2026-07-14T01:02:03Z"
                      meetingStartedBy:
                        userId: "0HZX2K7M9Q4AC"
                        name: "홍길동"
                        email: "hong@example.com"
                        image: "https://cdn.example.com/avatars/hong.png"
                      participants:
                      - userId: "0HZX2K7M9Q4AC"
                        name: "홍길동"
                        email: "hong@example.com"
                        image: "https://cdn.example.com/avatars/hong.png"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    delete:
      tags:
      - "Notes"
      summary: "노트 삭제"
      description: "노트를 삭제한다. 전사·요약·채팅이 함께 사라지며 되돌릴 수 없다."
      operationId: "deleteNote"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      responses:
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                MEETING_IN_PROGRESS:
                  value:
                    success: false
                    data: null
                    error:
                      code: "MEETING_IN_PROGRESS"
                      message: "기록 중인 회의는 삭제할 수 없습니다."
                      details: null
        "204":
          description: "처리 성공"
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    patch:
      tags:
      - "Notes"
      summary: "노트 수정"
      description: "노트 제목을 수정한다."
      operationId: "updateNote"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NoteRequest"
            examples:
              updateNote_Success:
                value:
                  title: "주간 회의"
              updateNote_WorkspaceNotFoundException:
                value:
                  title: "주간 회의"
              updateNote_ProjectNotFoundException:
                value:
                  title: "주간 회의"
              updateNote_NoteNotFoundException:
                value:
                  title: "주간 회의"
              updateNote_BadRequest:
                value:
                  title: ""
      responses:
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NoteResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      noteId: "0HZX2K7M9Q4AF"
                      projectId: "0HZX2K7M9Q4AE"
                      title: "주간 회의"
                      createdAt: "2026-07-14T01:02:03Z"
                      updatedAt: "2026-07-14T01:02:03Z"
                      meetingStatus: "IN_PROGRESS"
                      meetingStartedAt: "2026-07-14T01:02:03Z"
                      recordedDurationMs: 6500
                      activeSessionStartedAt: "2026-07-14T01:02:03Z"
                      meetingStartedBy:
                        userId: "0HZX2K7M9Q4AC"
                        name: "홍길동"
                        email: "hong@example.com"
                        image: "https://cdn.example.com/avatars/hong.png"
                      participants:
                      - userId: "0HZX2K7M9Q4AC"
                        name: "홍길동"
                        email: "hong@example.com"
                        image: "https://cdn.example.com/avatars/hong.png"
                    error: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/transcription-sessions/{sessionId}:
    get:
      tags:
      - "Transcription"
      summary: "전사 세션 조회"
      description: "전사 세션의 현재 상태와 종료 정보를 조회한다. READY는 아직 연결 전이므로 startedAt·endedAt·\
        endReason이 모두 null이다."
      operationId: "getTranscriptionSession"
      parameters:
      - name: "sessionId"
        in: "path"
        description: "전사 세션의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "전사 세션의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AG"
      responses:
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                TRANSCRIPTION_SESSION_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "TRANSCRIPTION_SESSION_NOT_FOUND"
                      message: "전사 세션을 찾을 수 없습니다."
                      details: null
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TranscriptionSessionResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      sessionId: "0HZX2K7M9Q4AG"
                      noteId: "0HZX2K7M9Q4AF"
                      status: "COMPLETED"
                      readyExpiresAt: "2026-07-14T01:02:03Z"
                      startedAt: "2026-07-14T01:02:03Z"
                      endedAt: "2026-07-14T01:02:03Z"
                      endReason: "CLIENT_DISCONNECTED"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/users/me:
    get:
      tags:
      - "Users"
      summary: "현재 사용자 조회"
      description: "현재 인증된 사용자의 기본 정보를 조회한다."
      operationId: "getCurrentUser"
      responses:
        "404":
          description: "사용자를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                USER_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "USER_NOT_FOUND"
                      message: "사용자를 찾을 수 없습니다."
                      details: null
        "200":
          description: "현재 사용자 조회 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CurrentUserResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      userId: "0HZX2K7M9Q4AC"
                      name: "홍길동"
                      email: "hong@example.com"
                      image: "https://cdn.heymoa.com/profiles/member.png"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces/{workspaceId}:
    get:
      tags:
      - "Workspaces"
      summary: "워크스페이스 조회"
      description: "현재 사용자가 참여 중인 워크스페이스를 조회한다."
      operationId: "getWorkspace"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: "워크스페이스의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "워크스페이스의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      responses:
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      workspaceId: "0HZX2K7M9Q4AD"
                      name: "제품 팀"
                      description: "제품을 만드는 팀의 워크스페이스"
                      role: "ADMIN"
                    error: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    put:
      tags:
      - "Workspaces"
      summary: "워크스페이스 수정"
      description: "관리자 권한으로 워크스페이스 이름과 설명을 수정한다."
      operationId: "updateWorkspace"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: "워크스페이스의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "워크스페이스의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateWorkspaceRequest"
            examples:
              updateWorkspace_BadRequest:
                value:
                  name: ""
                  description: null
              updateWorkspace_Success:
                value:
                  name: "제품 팀"
                  description: "제품을 만드는 팀의 워크스페이스"
              updateWorkspace_WorkspaceNotFoundException:
                value:
                  name: "제품 팀"
                  description: "제품을 만드는 팀의 워크스페이스"
              updateWorkspace_WorkspaceAccessDeniedException:
                value:
                  name: "제품 팀"
                  description: "제품을 만드는 팀의 워크스페이스"
      responses:
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UpdateWorkspaceResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      workspaceId: "0HZX2K7M9Q4AD"
                      name: "제품 팀"
                      description: "제품을 만드는 팀의 워크스페이스"
                    error: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_ACCESS_DENIED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_ACCESS_DENIED"
                      message: "워크스페이스를 변경할 권한이 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/agent-chats/{chatId}/messages:
    get:
      tags:
      - "AgentChat"
      summary: "채팅 히스토리 조회"
      description: "채팅 세션의 표시용 메시지를 시간순 조회한다. TOOL 메시지는 승인/도구 실행 기록(toolEvent)을 포함\
        한다."
      operationId: "getAgentChatMessages"
      parameters:
      - name: "chatId"
        in: "path"
        description: "채팅 세션의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "채팅 세션의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0K9GVJT2C4Q2A"
      responses:
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentChatMessagesResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      messages:
                      - role: "USER"
                        content: "질문"
                        toolEvent: null
                        createdAt: "2026-07-22T00:00:00Z"
                      - role: "ASSISTANT"
                        content: "답변"
                        toolEvent: null
                        createdAt: "2026-07-22T00:00:01Z"
                      - role: "TOOL"
                        content: "Linear APP-123 생성"
                        toolEvent:
                          tool: "linear.create_issue"
                          decision: null
                          status: "success"
                          url: "https://linear.app/heymoa/issue/APP-123"
                        createdAt: "2026-07-22T00:00:02Z"
                    error: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                AGENT_CHAT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "AGENT_CHAT_NOT_FOUND"
                      message: "채팅 세션을 찾을 수 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    post:
      tags:
      - "AgentChat"
      summary: "메시지 전송 (SSE 중계)"
      description: "메시지를 heymoa-ai로 중계하고 SSE(text/event-stream)로 passthrough한다. 이벤\
        트 페이로드 계약은 AsyncAPI(asyncapi-web-server.yml) 참고."
      operationId: "sendAgentChatMessage"
      parameters:
      - name: "chatId"
        in: "path"
        description: "채팅 세션의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "채팅 세션의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0K9GVJT2C4Q2A"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SendAgentChatMessageRequest"
            examples:
              sendAgentChatMessage_Success:
                value:
                  message: "안녕"
              sendAgentChatMessage_AgentChatNotFoundException:
                value:
                  message: "안녕"
      responses:
        "200":
          description: "요청 성공"
          content:
            text/event-stream:
              schema:
                type: "string"
                description: "SSE 이벤트 스트림. 이벤트별 payload는 AsyncAPI 계약을 따른다."
              examples:
                sendAgentChatMessage_Success:
                  value: "event:message_start\ndata:{\"chatId\":\"0K9GVJT2C4Q2A\"\
                    ,\"messageId\":\"m1\"}\n\nevent:token\ndata:{\"delta\":\"안\"}\n\
                    \nevent:message_end\ndata:{\"messageId\":\"m1\",\"content\":\"\
                    안녕하세요\"}\n\n"
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                AGENT_CHAT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "AGENT_CHAT_NOT_FOUND"
                      message: "채팅 세션을 찾을 수 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/integrations/{provider}/callback:
    get:
      tags:
      - "WorkspaceIntegration"
      summary: "워크스페이스 연동 콜백"
      description: "provider OAuth 콜백을 처리하고 웹으로 리다이렉트한다. (permitAll) redirect URI는\
        \ provider에 사전 등록된 고정 경로여야 하므로 워크스페이스 경로를 쓰지 않는다 — workspaceId는 서명된 state에\
        서 복원한다."
      operationId: "completeWorkspaceIntegration"
      parameters:
      - name: "provider"
        in: "path"
        description: "연동 provider (LINEAR, GITHUB)"
        required: true
        schema:
          type: "string"
          description: "연동 provider (LINEAR, GITHUB)"
          nullable: false
          enum:
          - "LINEAR"
          - "GITHUB"
        example: "LINEAR"
      - name: "code"
        in: "query"
        description: "provider가 발급한 authorization code (성공 콜백)"
        required: false
        schema:
          type: "string"
          description: "provider가 발급한 authorization code (성공 콜백)"
          nullable: false
        example: "auth-code"
      - name: "state"
        in: "query"
        description: "서명된 OAuth state (성공 콜백)"
        required: false
        schema:
          type: "string"
          description: "서명된 OAuth state (성공 콜백)"
          nullable: false
        example: "signed-state"
      - name: "error"
        in: "query"
        description: "provider가 거부·오류 시 전달하는 코드 (실패 콜백)"
        required: false
        schema:
          type: "string"
          description: "provider가 거부·오류 시 전달하는 코드 (실패 콜백)"
          nullable: false
        example: "access_denied"
      - name: "Cookie"
        in: "header"
        description: "integration_oauth_nonce_{provider} — 성공 콜백에 필요한 nonce 쿠키"
        required: false
        schema:
          type: "string"
      responses:
        "302":
          description: "302"
          headers:
            Location:
              description: "연동 결과를 담은 웹 리다이렉트 URL"
              schema:
                type: "string"
      security: []
  /v1/invitations/{invitationId}/accept:
    post:
      tags:
      - "Workspace Invitations"
      summary: "초대 수락"
      description: "받은 초대를 수락하고 워크스페이스 멤버가 된다. 초대받은 본인만 수락할 수 있다."
      operationId: "acceptWorkspaceInvitation"
      parameters:
      - name: "invitationId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "수락할 초대 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AK"
      responses:
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVITATION_ACCESS_DENIED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_ACCESS_DENIED"
                      message: "초대를 처리할 권한이 없습니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceInvitationActionResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      invitationId: "0HZX2K7M9Q4AK"
                      workspaceId: "0HZX2K7M9Q4AD"
                      role: "MEMBER"
                      status: "ACCEPTED"
                    error: null
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVITATION_NOT_PENDING:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_NOT_PENDING"
                      message: "이미 처리된 초대입니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVITATION_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_NOT_FOUND"
                      message: "초대를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/invitations/{invitationId}/decline:
    post:
      tags:
      - "Workspace Invitations"
      summary: "초대 거절"
      description: "받은 초대를 거절한다. 초대받은 본인만 거절할 수 있다."
      operationId: "declineWorkspaceInvitation"
      parameters:
      - name: "invitationId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "거절할 초대 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AK"
      responses:
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVITATION_ACCESS_DENIED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_ACCESS_DENIED"
                      message: "초대를 처리할 권한이 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVITATION_NOT_PENDING:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_NOT_PENDING"
                      message: "이미 처리된 초대입니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceInvitationActionResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      invitationId: "0HZX2K7M9Q4AK"
                      workspaceId: "0HZX2K7M9Q4AD"
                      role: "MEMBER"
                      status: "DECLINED"
                    error: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVITATION_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_NOT_FOUND"
                      message: "초대를 찾을 수 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/notes/{noteId}/analyses:
    post:
      tags:
      - "Analysis"
      summary: "재분석 요청"
      description: "종료된 회의의 노트를 다시 분석한다."
      operationId: "requestAnalysis"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      responses:
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                MEETING_NOT_ENDED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "MEETING_NOT_ENDED"
                      message: "회의가 아직 종료되지 않았습니다."
                      details: null
                ANALYSIS_IN_PROGRESS:
                  value:
                    success: false
                    data: null
                    error:
                      code: "ANALYSIS_IN_PROGRESS"
                      message: "이미 진행 중인 분석이 있습니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
        "202":
          description: "202"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AnalysisResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      analysisId: "0K9GVJT2C4Q1Z"
                      noteId: "0HZX2K7M9Q4AF"
                      status: "PENDING"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/notes/{noteId}/meeting-end:
    post:
      tags:
      - "Analysis"
      summary: "회의 종료"
      description: "노트의 회의를 종료하고 분석을 시작한다. PAUSED 회의도 이 API로 종료하며 별도 pause/resume\
        \ HTTP API는 없다."
      operationId: "endMeeting"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      responses:
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                ACTIVE_TRANSCRIPTION_SESSION:
                  value:
                    success: false
                    data: null
                    error:
                      code: "ACTIVE_TRANSCRIPTION_SESSION"
                      message: "이미 진행 가능한 전사 세션이 있습니다."
                      details: null
                MEETING_NOT_STARTED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "MEETING_NOT_STARTED"
                      message: "아직 시작하지 않은 회의입니다."
                      details: null
                MEETING_ALREADY_ENDED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "MEETING_ALREADY_ENDED"
                      message: "이미 종료된 회의입니다."
                      details: null
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                NOT_MEETING_STARTER:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOT_MEETING_STARTER"
                      message: "회의 시작자만 조작할 수 있습니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "202":
          description: "202"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AnalysisResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      analysisId: "0K9GVJT2C4Q1Z"
                      noteId: "0HZX2K7M9Q4AF"
                      status: "PENDING"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/notes/{noteId}/participants:
    put:
      tags:
      - "Notes"
      summary: "노트 참여자 교체"
      description: "노트의 참여자를 요청한 목록으로 통째로 교체한다. 부분 추가·삭제가 아니며, 빈 배열이면 참여자를 전원 지운다\
        . 워크스페이스 멤버만 참여자가 될 수 있고, 회의 상태와 무관하게 언제나 호출할 수 있다."
      operationId: "replaceNoteParticipants"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NoteParticipantsRequest"
            examples:
              replaceNoteParticipants_Success:
                value:
                  userIds:
                  - "0HZX2K7M9Q4AC"
              replaceNoteParticipants_NotWorkspaceMemberException:
                value:
                  userIds:
                  - "0HZX2K7M9Q4AC"
              replaceNoteParticipants_BadRequest:
                value:
                  userIds:
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
                  - "0HZX2K7M9Q4AC"
              replaceNoteParticipants_NoteNotFoundException:
                value:
                  userIds:
                  - "0HZX2K7M9Q4AC"
              replaceNoteParticipants_WorkspaceNotFoundException:
                value:
                  userIds:
                  - "0HZX2K7M9Q4AC"
      responses:
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NoteParticipantListResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      participants:
                      - userId: "0HZX2K7M9Q4AC"
                        name: "홍길동"
                        email: "hong@example.com"
                        image: "https://cdn.example.com/avatars/hong.png"
                    error: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                NOT_WORKSPACE_MEMBER:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOT_WORKSPACE_MEMBER"
                      message: "워크스페이스 멤버만 참여자로 등록할 수 있습니다."
                      details: null
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/notes/{noteId}/transcript:
    get:
      tags:
      - "Transcription"
      summary: "노트 전사 조회"
      description: "노트에 저장된 확정 전사 구간을 시간순으로 조회한다."
      operationId: "getNoteTranscript"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      responses:
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TranscriptResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      segments:
                      - segmentId: "0HZX2K7M9Q4AH"
                        transcriptionSessionId: "0HZX2K7M9Q4AG"
                        sequence: 1
                        text: "안녕하세요."
                        startedAtMs: 0
                        endedAtMs: 1200
                    error: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/notes/{noteId}/transcription-sessions:
    post:
      tags:
      - "Transcription"
      summary: "전사 세션 시작"
      description: "노트에 실시간 전사 세션을 READY로 생성한다. READY는 아직 연결 전이므로 startedAt·endedAt·\
        endReason이 모두 null이다."
      operationId: "startTranscriptionSession"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      responses:
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                NOT_MEETING_STARTER:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOT_MEETING_STARTER"
                      message: "회의 시작자만 조작할 수 있습니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                MEETING_ALREADY_ENDED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "MEETING_ALREADY_ENDED"
                      message: "이미 종료된 회의입니다."
                      details: null
                ACTIVE_TRANSCRIPTION_SESSION:
                  value:
                    success: false
                    data: null
                    error:
                      code: "ACTIVE_TRANSCRIPTION_SESSION"
                      message: "이미 진행 가능한 전사 세션이 있습니다."
                      details: null
        "201":
          description: "생성 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StartTranscriptionSessionResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      sessionId: "0HZX2K7M9Q4AG"
                      noteId: "0HZX2K7M9Q4AF"
                      status: "READY"
                      readyExpiresAt: "2026-07-14T01:02:03Z"
                      startedAt: null
                      endedAt: null
                      endReason: null
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/notifications/{notificationId}/read:
    put:
      tags:
      - "Notifications"
      summary: "알림 읽음 처리"
      description: "알림을 읽음 처리한다. 이미 읽은 알림은 처음 읽은 시각을 유지한다."
      operationId: "markNotificationRead"
      parameters:
      - name: "notificationId"
        in: "path"
        description: "읽음 처리할 알림 식별자"
        required: true
        schema:
          type: "string"
          description: "읽음 처리할 알림 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AJ"
      responses:
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MarkNotificationReadResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      notificationId: "0HZX2K7M9Q4AJ"
                    error: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                NOTIFICATION_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTIFICATION_NOT_FOUND"
                      message: "알림을 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/projects/{projectId}/notes:
    get:
      tags:
      - "Notes"
      summary: "노트 목록 조회"
      description: "프로젝트의 노트 목록을 조회한다. 회의를 기록하기 시작한 시각 내림차순이고, 한 번도 기록하지 않은 노트는 만든\
        \ 시각으로 선다."
      operationId: "getNotes"
      parameters:
      - name: "projectId"
        in: "path"
        description: "프로젝트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "프로젝트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AE"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NoteListResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      notes:
                      - noteId: "0HZX2K7M9Q4AF"
                        projectId: "0HZX2K7M9Q4AE"
                        title: "주간 회의"
                        createdAt: "2026-07-14T01:02:03Z"
                        updatedAt: "2026-07-14T01:02:03Z"
                        meetingStatus: "IN_PROGRESS"
                        meetingStartedAt: "2026-07-14T01:02:03Z"
                        lastRecordedAt: "2026-07-14T01:02:03Z"
                        recordedDurationMs: 6500
                        activeSessionStartedAt: "2026-07-14T01:02:03Z"
                        meetingStartedBy:
                          userId: "0HZX2K7M9Q4AC"
                          name: "홍길동"
                          email: "hong@example.com"
                          image: "https://cdn.example.com/avatars/hong.png"
                        participants:
                        - userId: "0HZX2K7M9Q4AC"
                          name: "홍길동"
                          email: "hong@example.com"
                          image: "https://cdn.example.com/avatars/hong.png"
                    error: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    post:
      tags:
      - "Notes"
      summary: "노트 생성"
      description: "프로젝트에 새 노트를 생성한다."
      operationId: "createNote"
      parameters:
      - name: "projectId"
        in: "path"
        description: "프로젝트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "프로젝트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AE"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NoteRequest"
            examples:
              createNote_Success:
                value:
                  title: "주간 회의"
              createNote_BadRequest:
                value:
                  title: ""
              createNote_WorkspaceNotFoundException:
                value:
                  title: "주간 회의"
              createNote_ProjectNotFoundException:
                value:
                  title: "주간 회의"
      responses:
        "201":
          description: "생성 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NoteResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      noteId: "0HZX2K7M9Q4AF"
                      projectId: "0HZX2K7M9Q4AE"
                      title: "주간 회의"
                      createdAt: "2026-07-14T01:02:03Z"
                      updatedAt: "2026-07-14T01:02:03Z"
                      meetingStatus: "NOT_STARTED"
                      meetingStartedAt: null
                      recordedDurationMs: 0
                      activeSessionStartedAt: null
                      meetingStartedBy: null
                      participants:
                      - userId: "0HZX2K7M9Q4AC"
                        name: "홍길동"
                        email: "hong@example.com"
                        image: "https://cdn.example.com/avatars/hong.png"
                    error: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces/{workspaceId}/integrations:
    get:
      tags:
      - "WorkspaceIntegration"
      summary: "워크스페이스 연동 상태 조회"
      description: "워크스페이스에 연결된 도구 연동 상태를 provider별로 조회한다. MEMBER 이상 열람 가능."
      operationId: "getWorkspaceIntegrations"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: "워크스페이스 식별자"
        required: true
        schema:
          type: "string"
          description: "워크스페이스 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolConnectionsResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      integrations:
                      - provider: "LINEAR"
                        connected: true
                        connectedBy: "김민수"
                        connectedAt: "2026-07-22T00:00:00Z"
                      - provider: "GITHUB"
                        connected: false
                        connectedBy: null
                        connectedAt: null
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces/{workspaceId}/invitations:
    get:
      tags:
      - "Workspace Invitations"
      summary: "대기 중인 초대 목록 조회"
      description: "워크스페이스의 PENDING 초대를 최신순으로 조회한다. ADMIN만 조회할 수 있다."
      operationId: "getWorkspaceInvitations"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "조회할 워크스페이스 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      responses:
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_ACCESS_DENIED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_ACCESS_DENIED"
                      message: "워크스페이스를 변경할 권한이 없습니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceInvitationListResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      invitations:
                      - invitationId: "0HZX2K7M9Q4AK"
                        inviteeName: "김멤버"
                        inviteeEmail: "kim@example.com"
                        inviteeImage: "https://cdn.heymoa.com/profiles/member.png"
                        role: "MEMBER"
                        inviterName: "홍길동"
                        createdAt: "2026-07-14T01:02:03Z"
                        expiresAt: "2026-07-15T01:02:03Z"
                    error: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    post:
      tags:
      - "Workspace Invitations"
      summary: "워크스페이스 초대 생성"
      description: "이메일로 워크스페이스에 초대한다. ADMIN만 초대할 수 있다. 가입자에게는 인앱 알림과 초대 이메일이, 미가입\
        자에게는 초대 이메일이 발송되며 링크로 가입 후 합류한다."
      operationId: "createWorkspaceInvitation"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "초대할 워크스페이스 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWorkspaceInvitationRequest"
            examples:
              createWorkspaceInvitation_DuplicatePendingInvitationException:
                value:
                  email: "kim@example.com"
                  role: "MEMBER"
              createWorkspaceInvitation_BadRequest:
                value:
                  email: "not-an-email"
                  role: "MEMBER"
              createWorkspaceInvitation_Success:
                value:
                  email: "kim@example.com"
                  role: "MEMBER"
              createWorkspaceInvitation_AlreadyWorkspaceMemberException:
                value:
                  email: "kim@example.com"
                  role: "MEMBER"
              createWorkspaceInvitation_WorkspaceNotFoundException:
                value:
                  email: "kim@example.com"
                  role: "MEMBER"
              createWorkspaceInvitation_Forbidden:
                value:
                  email: "kim@example.com"
                  role: "MEMBER"
      responses:
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                DUPLICATE_PENDING_INVITATION:
                  value:
                    success: false
                    data: null
                    error:
                      code: "DUPLICATE_PENDING_INVITATION"
                      message: "이미 대기 중인 초대가 있습니다."
                      details: null
                ALREADY_WORKSPACE_MEMBER:
                  value:
                    success: false
                    data: null
                    error:
                      code: "ALREADY_WORKSPACE_MEMBER"
                      message: "이미 워크스페이스 멤버입니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "201":
          description: "생성 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceInvitationActionResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      invitationId: "0HZX2K7M9Q4AK"
                      workspaceId: "0HZX2K7M9Q4AD"
                      role: "MEMBER"
                      status: "PENDING"
                    error: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_ACCESS_DENIED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_ACCESS_DENIED"
                      message: "워크스페이스를 변경할 권한이 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces/{workspaceId}/members:
    get:
      tags:
      - "Workspace Members"
      summary: "워크스페이스 멤버 목록 조회"
      description: "워크스페이스의 멤버를 가입순으로 조회한다. 멤버만 조회할 수 있다."
      operationId: "getWorkspaceMembers"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "조회할 워크스페이스 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceMemberListResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      members:
                      - userId: "0HZX2K7M9Q4AC"
                        name: "홍길동"
                        email: "hong@example.com"
                        image: "https://cdn.heymoa.com/profiles/member.png"
                        role: "ADMIN"
                        joinedAt: "2026-07-14T01:02:03Z"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces/{workspaceId}/projects:
    get:
      tags:
      - "Projects"
      summary: "프로젝트 목록 조회"
      description: "워크스페이스의 프로젝트 목록을 조회한다."
      operationId: "getProjects"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: "워크스페이스의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "워크스페이스의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      responses:
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectListResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      projects:
                      - projectId: "0HZX2K7M9Q4AE"
                        workspaceId: "0HZX2K7M9Q4AD"
                        name: "모바일 앱"
                        description: "Heymoa 모바일 앱 프로젝트"
                        createdAt: "2026-07-14T01:02:03Z"
                        updatedAt: "2026-07-14T01:02:03Z"
                    error: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    post:
      tags:
      - "Projects"
      summary: "프로젝트 생성"
      description: "워크스페이스에 새 프로젝트를 생성한다."
      operationId: "createProject"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: "워크스페이스의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "워크스페이스의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProjectRequest"
            examples:
              createProject_WorkspaceNotFoundException:
                value:
                  name: "모바일 앱"
                  description: "Heymoa 모바일 앱 프로젝트"
              createProject_BadRequest:
                value:
                  name: ""
                  description: null
              createProject_Success:
                value:
                  name: "모바일 앱"
                  description: "Heymoa 모바일 앱 프로젝트"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "201":
          description: "생성 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      projectId: "0HZX2K7M9Q4AE"
                      workspaceId: "0HZX2K7M9Q4AD"
                      name: "모바일 앱"
                      description: "Heymoa 모바일 앱 프로젝트"
                      createdAt: "2026-07-14T01:02:03Z"
                      updatedAt: "2026-07-14T01:02:03Z"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/agent-chats/{chatId}/approvals/{approvalId}:
    post:
      tags:
      - "AgentChat"
      summary: "도구 승인/거절"
      description: "tool_approval_request SSE 이벤트에 대한 응답. 공유·개인 챗봇 공용 (chatId 기준).\
        \ 해당 메시지의 입력자 본인만 호출할 수 있다 — 관전자는 403. 승인 시 heymoa-ai의 중단된 도구 실행이 재개되고, 거절\
        \ 시 도구를 실행하지 않고 스트림이 계속된다 (tool_approval_resolved 이벤트로 결과 전파)."
      operationId: "resolveToolApproval"
      parameters:
      - name: "chatId"
        in: "path"
        description: "채팅 세션의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "채팅 세션의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0K9GVJT2C4Q2A"
      - name: "approvalId"
        in: "path"
        description: "승인 요청의 13자 TSID (heymoa-ai 발급)"
        required: true
        schema:
          type: "string"
          description: "승인 요청의 13자 TSID (heymoa-ai 발급)"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0K9GVJT2C4Q7F"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ResolveToolApprovalRequest"
            examples:
              resolveToolApproval_Success:
                value:
                  decision: "APPROVED"
              resolveToolApproval_MeetingNotActiveException:
                value:
                  decision: "APPROVED"
              resolveToolApproval_BadRequest:
                value:
                  decision: "MAYBE"
              resolveToolApproval_ApprovalNotFoundException:
                value:
                  decision: "REJECTED"
              resolveToolApproval_NotApprovalOwnerException:
                value:
                  decision: "APPROVED"
      responses:
        "204":
          description: "처리 성공"
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                MEETING_NOT_ACTIVE:
                  value:
                    success: false
                    data: null
                    error:
                      code: "MEETING_NOT_ACTIVE"
                      message: "진행 중인 회의에서만 공유 챗봇을 사용할 수 있습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                APPROVAL_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "APPROVAL_NOT_FOUND"
                      message: "승인 요청을 찾을 수 없습니다."
                      details: null
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                NOT_APPROVAL_OWNER:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOT_APPROVAL_OWNER"
                      message: "승인 권한이 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/notes/{noteId}/analyses/latest:
    get:
      tags:
      - "Analysis"
      summary: "최신 분석 조회"
      description: "노트의 최신 분석 잡 상태·결과를 조회한다 (프론트 폴링 대상)."
      operationId: "getLatestAnalysis"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                ANALYSIS_JOB_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "ANALYSIS_JOB_NOT_FOUND"
                      message: "분석 잡을 찾을 수 없습니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AnalysisResultResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      analysisId: "0K9GVJT2C4Q1Z"
                      noteId: "0HZX2K7M9Q4AF"
                      status: "SUCCEEDED"
                      sections:
                      - kind: "OVERVIEW"
                        items:
                        - itemId: "0HZX2K7M9Q4AN"
                          content: "결제 실패율이 3%로 올랐다"
                          evidence:
                          - segmentId: "0HZX2K7M9Q4AH"
                            text: "그럼 결제 쪽부터 보죠"
                            startedAtMs: 252000
                      - kind: "ACTION_ITEM"
                        items:
                        - itemId: "0HZX2K7M9Q4AN"
                          content: "이번 주 안에 원인을 좁힌다"
                          evidence: []
                      - kind: "DECISION"
                        items: []
                      errorCode: null
                      errorMessage: null
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/notes/{noteId}/chat/messages:
    get:
      tags:
      - "NoteSharedChat"
      summary: "공유 챗봇 히스토리 + 잠금 상태 조회"
      description: "노트 공유 챗봇의 메시지를 시간순 조회한다. 노트 멤버 전원이 조회할 수 있고, 챗이 아직 없으면 조회 시점에\
        \ 생성한다(chatId는 항상 존재). 잠금 상태(현재 입력 중인 멤버)와 승인 대기 상태(pendingApproval)를 함께 반\
        환하며 관전자 표시용 폴링 대상이다. 접근할 수 없는 노트면 404다."
      operationId: "getNoteSharedChatMessages"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NoteSharedChatResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      chatId: "0K9GVJT2C4Q2A"
                      lock:
                        locked: true
                        lockedBy: "홍길동"
                        pendingApproval:
                          approvalId: "0K9GVJT2C4Q7F"
                          tool: "linear.create_issue"
                          summary: "Linear 이슈 생성"
                      messages:
                      - messageId: "0K9GVJT2C4Q3B"
                        role: "USER"
                        authorName: "홍길동"
                        content: "방금 논의한 배포 이슈 요약해줘"
                        toolEvent: null
                        createdAt: "2026-07-22T10:15:00Z"
                      - messageId: "0K9GVJT2C4Q4C"
                        role: "ASSISTANT"
                        authorName: null
                        content: "배포 관련 논의 요약입니다..."
                        toolEvent: null
                        createdAt: "2026-07-22T10:15:21Z"
                      - messageId: "0K9GVJT2C4Q5D"
                        role: "TOOL"
                        authorName: null
                        content: "홍길동님이 승인 → Linear 이슈 생성"
                        toolEvent:
                          tool: "linear.create_issue"
                          decision: "APPROVED"
                          status: null
                          url: null
                        createdAt: "2026-07-22T10:15:40Z"
                      - messageId: "0K9GVJT2C4Q6E"
                        role: "TOOL"
                        authorName: null
                        content: "Linear APP-123 생성"
                        toolEvent:
                          tool: "linear.create_issue"
                          decision: null
                          status: "success"
                          url: "https://linear.app/heymoa/issue/APP-123"
                        createdAt: "2026-07-22T10:15:45Z"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    post:
      tags:
      - "NoteSharedChat"
      summary: "공유 챗봇 메시지 전송 (SSE 중계)"
      description: "공유 챗봇에 메시지를 보내고 응답을 SSE(text/event-stream)로 passthrough한다. 이벤트\
        \ 페이로드 계약은 AsyncAPI(asyncapi-web-server.yml) 참고. 게이트는 스트림을 열기 전에 동기 검증한다 —\
        \ 접근할 수 없는 노트면 404, 회의 상태가 IN_PROGRESS가 아니면 409(MEETING_NOT_ACTIVE; NOT_STARTED·\
        PAUSED·ENDED), 다른 멤버가 입력 중이면 409(CHAT_LOCKED). 입력 잠금은 요청 시작에 획득하고 스트림 종료 시\
        \ 해제하며 TTL은 300초다."
      operationId: "sendNoteSharedChatMessage"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SendNoteSharedChatMessageRequest"
            examples:
              sendNoteSharedChatMessage_WorkspaceNotFoundException:
                value:
                  message: "방금 논의한 배포 이슈 요약해줘"
              sendNoteSharedChatMessage_NoteNotFoundException:
                value:
                  message: "방금 논의한 배포 이슈 요약해줘"
              sendNoteSharedChatMessage_Success:
                value:
                  message: "방금 논의한 배포 이슈 요약해줘"
              sendNoteSharedChatMessage_MeetingNotActiveException:
                value:
                  message: "방금 논의한 배포 이슈 요약해줘"
              sendNoteSharedChatMessage_BadRequest:
                value:
                  message: "   "
              sendNoteSharedChatMessage_ProjectNotFoundException:
                value:
                  message: "방금 논의한 배포 이슈 요약해줘"
              sendNoteSharedChatMessage_ChatLockedException:
                value:
                  message: "방금 논의한 배포 이슈 요약해줘"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            text/event-stream:
              schema:
                type: "string"
                description: "SSE 이벤트 스트림. 이벤트별 payload는 AsyncAPI 계약을 따른다."
              examples:
                sendNoteSharedChatMessage_Success:
                  value: "event:message_start\ndata:{\"chatId\":\"0K9GVJT2C4Q2A\"\
                    ,\"messageId\":\"0K9GVJT2C4Q4C\"}\n\nevent:token\ndata:{\"delta\"\
                    :\"배포\"}\n\nevent:message_end\ndata:{\"messageId\":\"0K9GVJT2C4Q4C\"\
                    ,\"content\":\"배포 관련 논의 요약입니다...\"}\n\n"
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                MEETING_NOT_ACTIVE:
                  value:
                    success: false
                    data: null
                    error:
                      code: "MEETING_NOT_ACTIVE"
                      message: "진행 중인 회의에서만 공유 챗봇을 사용할 수 있습니다."
                      details: null
                CHAT_LOCKED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "CHAT_LOCKED"
                      message: "다른 멤버가 입력 중입니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/notes/{noteId}/transcription-sessions/current:
    get:
      tags:
      - "Transcription"
      summary: "현재 전사 세션 조회"
      description: "노트의 현재 전사 세션(만료되지 않은 READY 또는 ACTIVE)을 조회한다. READY의 startedAt은\
        \ null이고, 현재 세션이 없으면 data가 null이다."
      operationId: "getCurrentTranscriptionSession"
      parameters:
      - name: "noteId"
        in: "path"
        description: "노트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "노트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AF"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                NOTE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "NOTE_NOT_FOUND"
                      message: "노트를 찾을 수 없습니다."
                      details: null
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CurrentTranscriptionSessionNullableResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      sessionId: "0HZX2K7M9Q4AG"
                      noteId: "0HZX2K7M9Q4AF"
                      status: "READY"
                      readyExpiresAt: "2026-07-14T01:02:03Z"
                      startedAt: null
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces/{workspaceId}/integrations/{provider}:
    delete:
      tags:
      - "WorkspaceIntegration"
      summary: "워크스페이스 연동 해제"
      description: "도구 연동을 해제한다. ADMIN만 가능."
      operationId: "disconnectWorkspaceIntegration"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: "워크스페이스 식별자"
        required: true
        schema:
          type: "string"
          description: "워크스페이스 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      - name: "provider"
        in: "path"
        description: "연동 provider (LINEAR, GITHUB)"
        required: true
        schema:
          type: "string"
          description: "연동 provider (LINEAR, GITHUB)"
          nullable: false
          enum:
          - "LINEAR"
          - "GITHUB"
        example: "LINEAR"
      responses:
        "204":
          description: "처리 성공"
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_ACCESS_DENIED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_ACCESS_DENIED"
                      message: "워크스페이스를 변경할 권한이 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INTEGRATION_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INTEGRATION_NOT_FOUND"
                      message: "연동을 찾을 수 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces/{workspaceId}/invitations/{invitationId}:
    delete:
      tags:
      - "Workspace Invitations"
      summary: "초대 취소"
      description: "대기 중인 초대를 취소한다. 해당 워크스페이스의 ADMIN 누구나 취소할 수 있다."
      operationId: "cancelWorkspaceInvitation"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "초대가 속한 워크스페이스 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      - name: "invitationId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "취소할 초대 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AK"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVITATION_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_NOT_FOUND"
                      message: "초대를 찾을 수 없습니다."
                      details: null
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_ACCESS_DENIED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_ACCESS_DENIED"
                      message: "워크스페이스를 변경할 권한이 없습니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceInvitationActionResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      invitationId: "0HZX2K7M9Q4AK"
                      workspaceId: "0HZX2K7M9Q4AD"
                      role: "MEMBER"
                      status: "CANCELED"
                    error: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                INVITATION_NOT_PENDING:
                  value:
                    success: false
                    data: null
                    error:
                      code: "INVITATION_NOT_PENDING"
                      message: "이미 처리된 초대입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces/{workspaceId}/members/me:
    delete:
      tags:
      - "Workspace Members"
      summary: "워크스페이스 나가기"
      description: "본인이 워크스페이스에서 나간다. 마지막 ADMIN은 나갈 수 없다."
      operationId: "leaveWorkspace"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "나갈 워크스페이스 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                LAST_WORKSPACE_ADMIN:
                  value:
                    success: false
                    data: null
                    error:
                      code: "LAST_WORKSPACE_ADMIN"
                      message: "워크스페이스에는 관리자가 최소 한 명 있어야 합니다."
                      details: null
        "204":
          description: "처리 성공"
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces/{workspaceId}/members/{userId}:
    delete:
      tags:
      - "Workspace Members"
      summary: "워크스페이스 멤버 추방"
      description: "멤버를 워크스페이스에서 내보낸다. ADMIN만 호출할 수 있고, 자기 자신은 대상이 될 수 없다."
      operationId: "removeWorkspaceMember"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "워크스페이스 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      - name: "userId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "내보낼 멤버의 사용자 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AM"
      responses:
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                WORKSPACE_MEMBER_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_MEMBER_NOT_FOUND"
                      message: "워크스페이스 멤버를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "204":
          description: "처리 성공"
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_ACCESS_DENIED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_ACCESS_DENIED"
                      message: "워크스페이스를 변경할 권한이 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    patch:
      tags:
      - "Workspace Members"
      summary: "워크스페이스 멤버 역할 변경"
      description: "멤버의 역할을 ADMIN·MEMBER로 바꾼다. ADMIN만 호출할 수 있고, 마지막 ADMIN은 강등할 수 없\
        다."
      operationId: "changeWorkspaceMemberRole"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "워크스페이스 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      - name: "userId"
        in: "path"
        description: ""
        required: true
        schema:
          type: "string"
          description: "역할을 바꿀 멤버의 사용자 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AM"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChangeWorkspaceMemberRoleRequest"
            examples:
              changeWorkspaceMemberRole_Forbidden:
                value:
                  role: "ADMIN"
              changeWorkspaceMemberRole_LastWorkspaceAdminException:
                value:
                  role: "MEMBER"
              changeWorkspaceMemberRole_Success:
                value:
                  role: "ADMIN"
              changeWorkspaceMemberRole_WorkspaceNotFoundException:
                value:
                  role: "ADMIN"
              changeWorkspaceMemberRole_BadRequest:
                value:
                  role: "OWNER"
              changeWorkspaceMemberRole_WorkspaceMemberNotFoundException:
                value:
                  role: "ADMIN"
      responses:
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_ACCESS_DENIED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_ACCESS_DENIED"
                      message: "워크스페이스를 변경할 권한이 없습니다."
                      details: null
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                LAST_WORKSPACE_ADMIN:
                  value:
                    success: false
                    data: null
                    error:
                      code: "LAST_WORKSPACE_ADMIN"
                      message: "워크스페이스에는 관리자가 최소 한 명 있어야 합니다."
                      details: null
        "204":
          description: "처리 성공"
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                WORKSPACE_MEMBER_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_MEMBER_NOT_FOUND"
                      message: "워크스페이스 멤버를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces/{workspaceId}/projects/{projectId}:
    get:
      tags:
      - "Projects"
      summary: "프로젝트 조회"
      description: "워크스페이스의 프로젝트를 조회한다."
      operationId: "getProject"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: "워크스페이스의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "워크스페이스의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      - name: "projectId"
        in: "path"
        description: "프로젝트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "프로젝트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AE"
      responses:
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      projectId: "0HZX2K7M9Q4AE"
                      workspaceId: "0HZX2K7M9Q4AD"
                      name: "모바일 앱"
                      description: "Heymoa 모바일 앱 프로젝트"
                      createdAt: "2026-07-14T01:02:03Z"
                      updatedAt: "2026-07-14T01:02:03Z"
                    error: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    put:
      tags:
      - "Projects"
      summary: "프로젝트 수정"
      description: "프로젝트 이름과 설명을 수정한다."
      operationId: "updateProject"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: "워크스페이스의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "워크스페이스의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      - name: "projectId"
        in: "path"
        description: "프로젝트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "프로젝트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AE"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProjectRequest"
            examples:
              updateProject_Success:
                value:
                  name: "모바일 앱"
                  description: "Heymoa 모바일 앱 프로젝트"
              updateProject_ProjectNotFoundException:
                value:
                  name: "모바일 앱"
                  description: "Heymoa 모바일 앱 프로젝트"
              updateProject_BadRequest:
                value:
                  name: ""
                  description: null
              updateProject_WorkspaceNotFoundException:
                value:
                  name: "모바일 앱"
                  description: "Heymoa 모바일 앱 프로젝트"
      responses:
        "200":
          description: "요청 성공"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectResponse"
              examples:
                SUCCESS:
                  value:
                    success: true
                    data:
                      projectId: "0HZX2K7M9Q4AE"
                      workspaceId: "0HZX2K7M9Q4AD"
                      name: "모바일 앱"
                      description: "Heymoa 모바일 앱 프로젝트"
                      createdAt: "2026-07-14T01:02:03Z"
                      updatedAt: "2026-07-14T01:02:03Z"
                    error: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
    delete:
      tags:
      - "Projects"
      summary: "프로젝트 삭제"
      description: "관리자 권한으로 노트가 없는 프로젝트를 삭제한다."
      operationId: "deleteProject"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: "워크스페이스의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "워크스페이스의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      - name: "projectId"
        in: "path"
        description: "프로젝트의 13자리 TSID"
        required: true
        schema:
          type: "string"
          description: "프로젝트의 13자리 TSID"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AE"
      responses:
        "204":
          description: "처리 성공"
        "409":
          description: "요청 상태 충돌"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                PROJECT_HAS_NOTES:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_HAS_NOTES"
                      message: "노트가 있는 프로젝트는 삭제할 수 없습니다."
                      details: null
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
                PROJECT_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "PROJECT_NOT_FOUND"
                      message: "프로젝트를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_ACCESS_DENIED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_ACCESS_DENIED"
                      message: "워크스페이스를 변경할 권한이 없습니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
  /v1/workspaces/{workspaceId}/integrations/{provider}/authorize:
    get:
      tags:
      - "WorkspaceIntegration"
      summary: "워크스페이스 연동 시작"
      description: "도구 연동 OAuth를 시작하고 provider authorize로 리다이렉트한다. ADMIN만 가능."
      operationId: "startWorkspaceIntegration"
      parameters:
      - name: "workspaceId"
        in: "path"
        description: "워크스페이스 식별자"
        required: true
        schema:
          type: "string"
          description: "워크스페이스 식별자"
          nullable: false
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        example: "0HZX2K7M9Q4AD"
      - name: "provider"
        in: "path"
        description: "연동 provider (LINEAR, GITHUB)"
        required: true
        schema:
          type: "string"
          description: "연동 provider (LINEAR, GITHUB)"
          nullable: false
          enum:
          - "LINEAR"
          - "GITHUB"
        example: "LINEAR"
      responses:
        "403":
          description: "권한 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_ACCESS_DENIED:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_ACCESS_DENIED"
                      message: "워크스페이스를 변경할 권한이 없습니다."
                      details: null
        "302":
          description: "302"
          headers:
            Location:
              description: "provider의 OAuth authorize URL"
              schema:
                type: "string"
            Set-Cookie:
              description: "CSRF 방지용 nonce 쿠키(HttpOnly)"
              schema:
                type: "string"
        "404":
          description: "요청한 리소스를 찾을 수 없음"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                WORKSPACE_NOT_FOUND:
                  value:
                    success: false
                    data: null
                    error:
                      code: "WORKSPACE_NOT_FOUND"
                      message: "워크스페이스를 찾을 수 없습니다."
                      details: null
        "400":
          description: "잘못된 요청"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AppErrorResponse"
              examples:
                BAD_REQUEST:
                  value:
                    success: false
                    data: null
                    error:
                      code: "BAD_REQUEST"
                      message: "잘못된 요청입니다."
                      details: null
        "401":
          $ref: "#/components/responses/Unauthorized"
      security:
      - accessCookie: []
components:
  schemas:
    StartTranscriptionSessionResponse:
      title: "StartTranscriptionSessionResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "endReason"
          - "endedAt"
          - "noteId"
          - "readyExpiresAt"
          - "sessionId"
          - "startedAt"
          - "status"
          type: "object"
          properties:
            readyExpiresAt:
              type: "string"
              description: "전사 연결 대기 만료 시각"
              nullable: false
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            noteId:
              type: "string"
              description: "노트의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AF"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            sessionId:
              type: "string"
              description: "전사 세션의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AG"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            status:
              type: "string"
              description: "전사 세션 상태"
              nullable: false
              example: "READY"
              enum:
              - "READY"
              - "ACTIVE"
              - "COMPLETED"
              - "INTERRUPTED"
            startedAt:
              type: "string"
              description: "전사 시작 시각 (READY면 null)"
              nullable: true
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            endedAt:
              type: "string"
              description: "전사 종료 시각 (READY면 null)"
              nullable: true
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            endReason:
              type: "string"
              description: "전사 종료 사유 (READY면 null)"
              nullable: true
              example: "CLIENT_DISCONNECTED"
              enum:
              - "READY_TIMEOUT"
              - "CLIENT_DISCONNECTED"
              - "CLIENT_PROTOCOL_ERROR"
              - "STT_PROVIDER_ERROR"
              - "INTERNAL_ERROR"
              - "MEETING_ENDED"
              - "MEETING_PAUSED"
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    AgentChatV2NullableResponse:
      title: "AgentChatV2NullableResponse"
      required:
      - "error"
      - "success"
      - "data"
      type: "object"
      properties:
        data:
          required:
          - "chatId"
          - "createdAt"
          - "scope"
          type: "object"
          properties:
            createdAt:
              type: "string"
              description: "생성 시각"
              nullable: false
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            chatId:
              type: "string"
              description: "채팅 세션의 13자리 TSID"
              nullable: false
              example: "0K9GVJT2C4Q2A"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            scope:
              type: "string"
              description: "컨텍스트 범위"
              nullable: false
              example: "workspace"
              enum:
              - "workspace"
              - "note"
            noteId:
              type: "string"
              description: "연결된 노트 (scope=workspace면 null)"
              nullable: true
              example: "0HZX2K7M9Q4AF"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            title:
              type: "string"
              description: "대화 제목"
              nullable: true
              example: "워크스페이스 질문"
            workspaceId:
              type: "string"
              description: "연결된 워크스페이스 (scope=note면 null)"
              nullable: true
              example: "0HZX2K7M9Q4AD"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
          description: "성공 응답 데이터"
          nullable: true
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    SendAgentChatMessageRequest:
      title: "SendAgentChatMessageRequest"
      required:
      - "message"
      type: "object"
      properties:
        message:
          type: "string"
          description: "유저 메시지 (공백만으로 구성 불가)"
          nullable: false
          example: "안녕"
          pattern: "[\\s\\S]*\\S[\\s\\S]*"
          minLength: 1
    RefreshTokensResponse:
      title: "RefreshTokensResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "message"
          type: "object"
          properties:
            message:
              type: "string"
              description: "처리 결과 메시지"
              nullable: false
              example: "토큰이 갱신되었습니다."
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    AnalysisResultResponse:
      title: "AnalysisResultResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "analysisId"
          - "noteId"
          - "sections"
          - "status"
          - "errorCode"
          - "errorMessage"
          type: "object"
          properties:
            errorMessage:
              type: "string"
              description: "에러 메시지 (FAILED일 때)"
              nullable: true
              example: "분석이 제한 시간을 초과했습니다."
            errorCode:
              type: "string"
              description: "에러 코드 (FAILED일 때)"
              nullable: true
              example: "ANALYSIS_TIMEOUT"
            analysisId:
              type: "string"
              description: "분석 잡의 13자리 TSID"
              nullable: false
              example: "0K9GVJT2C4Q1Z"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            noteId:
              type: "string"
              description: "노트의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AF"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            sections:
              type: "array"
              description: "SUCCEEDED면 OVERVIEW → ACTION_ITEM → DECISION 세 섹션, 그 밖\
                에는 빈 배열"
              items:
                required:
                - "items"
                - "kind"
                type: "object"
                properties:
                  kind:
                    type: "string"
                    description: "항목 종류"
                    nullable: false
                    example: "OVERVIEW"
                    enum:
                    - "OVERVIEW"
                    - "ACTION_ITEM"
                    - "DECISION"
                  items:
                    type: "array"
                    description: "그 섹션의 항목. 뽑을 것이 없었으면 빈 배열"
                    items:
                      required:
                      - "content"
                      - "evidence"
                      - "itemId"
                      type: "object"
                      properties:
                        itemId:
                          type: "string"
                          description: "항목의 13자리 TSID"
                          nullable: false
                          example: "0HZX2K7M9Q4AN"
                          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                          minLength: 13
                          maxLength: 13
                        evidence:
                          type: "array"
                          description: "이 항목이 나온 전사 줄. 최대 3개, 근거를 못 찾았으면 빈 배열"
                          items:
                            required:
                            - "segmentId"
                            - "startedAtMs"
                            - "text"
                            type: "object"
                            properties:
                              segmentId:
                                type: "string"
                                description: "전사 세그먼트의 13자리 TSID"
                                nullable: false
                                example: "0HZX2K7M9Q4AH"
                                pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                                minLength: 13
                                maxLength: 13
                              text:
                                type: "string"
                                description: "그 세그먼트의 발화. `transcript_segments` join\
                                  \ 결과이고 저장된 사본이 아니다"
                                nullable: false
                                example: "그럼 결제 쪽부터 보죠"
                              startedAtMs:
                                type: "number"
                                description: "전사 세션 시작 기준 offset (ms)"
                                nullable: false
                                example: "252000"
                          nullable: false
                        content:
                          type: "string"
                          description: "항목 본문 한 줄"
                          nullable: false
                          example: "결제 실패율이 3%로 올랐다"
                    nullable: false
              nullable: false
            status:
              type: "string"
              description: "분석 잡 상태"
              nullable: false
              example: "SUCCEEDED"
              enum:
              - "PENDING"
              - "RUNNING"
              - "SUCCEEDED"
              - "FAILED"
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    WorkspaceResponse:
      title: "WorkspaceResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "description"
          - "name"
          - "role"
          - "workspaceId"
          type: "object"
          properties:
            role:
              type: "string"
              description: "현재 사용자의 워크스페이스 역할"
              nullable: false
              example: "ADMIN"
              enum:
              - "ADMIN"
              - "MEMBER"
            name:
              type: "string"
              description: "워크스페이스 이름"
              nullable: false
              example: "제품 팀"
              maxLength: 100
            description:
              type: "string"
              description: "워크스페이스 설명"
              nullable: true
              example: "제품을 만드는 팀의 워크스페이스"
              maxLength: 1000
            workspaceId:
              type: "string"
              description: "워크스페이스의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AD"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    CreateAgentChatV2Request:
      title: "CreateAgentChatV2Request"
      required:
      - "scope"
      type: "object"
      properties:
        scope:
          type: "string"
          description: "컨텍스트 범위 (workspace: 워크스페이스 전체, note: 노트 단건)"
          nullable: false
          example: "note"
          enum:
          - "workspace"
          - "note"
        noteId:
          type: "string"
          description: "연결할 노트 TSID (scope=note일 때 필수)"
          nullable: true
          example: "0HZX2K7M9Q4AF"
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
        title:
          type: "string"
          description: "대화 제목 (없으면 기본값, 최대 200자)"
          nullable: true
          example: "회의 후속"
          maxLength: 200
        workspaceId:
          type: "string"
          description: "연결할 워크스페이스 TSID (scope=workspace일 때 필수)"
          nullable: true
          example: "0HZX2K7M9Q4AD"
          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
          minLength: 13
          maxLength: 13
    CurrentTranscriptionSessionNullableResponse:
      title: "CurrentTranscriptionSessionNullableResponse"
      required:
      - "error"
      - "success"
      - "data"
      type: "object"
      properties:
        data:
          required:
          - "noteId"
          - "readyExpiresAt"
          - "sessionId"
          - "startedAt"
          - "status"
          type: "object"
          properties:
            readyExpiresAt:
              type: "string"
              description: "전사 연결 대기 만료 시각"
              nullable: false
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            noteId:
              type: "string"
              description: "노트의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AF"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            sessionId:
              type: "string"
              description: "전사 세션의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AG"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            status:
              type: "string"
              description: "현재 전사 세션 상태"
              nullable: false
              example: "READY"
              enum:
              - "READY"
              - "ACTIVE"
            startedAt:
              type: "string"
              description: "전사 시작 시각 (READY면 null)"
              nullable: true
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
          description: "성공 응답 데이터"
          nullable: true
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    ResolveToolApprovalRequest:
      title: "ResolveToolApprovalRequest"
      required:
      - "decision"
      type: "object"
      properties:
        decision:
          type: "string"
          description: "승인 결정 (APPROVED: 도구 실행 재개, REJECTED: 도구 미실행)"
          nullable: false
          example: "APPROVED"
          enum:
          - "APPROVED"
          - "REJECTED"
    InternalWorkspaceNotesResponse:
      title: "InternalWorkspaceNotesResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          type: "object"
          properties:
            notes:
              type: "array"
              items:
                required:
                - "createdAt"
                - "hasAnalysis"
                - "meetingStatus"
                - "noteId"
                - "title"
                type: "object"
                properties:
                  createdAt:
                    type: "string"
                    description: "노트 생성 시각"
                    nullable: false
                    example: "2026-07-22T00:00:00Z"
                    format: "date-time"
                  hasAnalysis:
                    type: "boolean"
                    description: "성공한 분석(요약) 존재 여부 — 깊이 읽을 노트 선별용"
                    nullable: false
                    example: true
                  meetingStatus:
                    type: "string"
                    description: "회의 상태 (NOT_STARTED·IN_PROGRESS·PAUSED·ENDED)"
                    nullable: false
                    example: "ENDED"
                    enum:
                    - "NOT_STARTED"
                    - "IN_PROGRESS"
                    - "PAUSED"
                    - "ENDED"
                  noteId:
                    type: "string"
                    description: "노트 식별자"
                    nullable: false
                    example: "0HZX2K7M9Q4AF"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  title:
                    type: "string"
                    description: "노트 제목"
                    nullable: false
                    example: "7월 3주차 스프린트 회의"
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    CreateWorkspaceRequest:
      title: "CreateWorkspaceRequest"
      required:
      - "name"
      type: "object"
      properties:
        name:
          type: "string"
          description: "워크스페이스 이름"
          nullable: false
          example: "제품 팀"
          minLength: 1
          maxLength: 100
        description:
          type: "string"
          description: "워크스페이스 설명"
          nullable: true
          example: "제품을 만드는 팀의 워크스페이스"
          maxLength: 1000
    TranscriptionSessionResponse:
      title: "TranscriptionSessionResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "endReason"
          - "endedAt"
          - "noteId"
          - "readyExpiresAt"
          - "sessionId"
          - "startedAt"
          - "status"
          type: "object"
          properties:
            endReason:
              type: "string"
              description: "전사 종료 사유 (READY면 null)"
              nullable: true
              example: "CLIENT_DISCONNECTED"
              enum:
              - "READY_TIMEOUT"
              - "CLIENT_DISCONNECTED"
              - "CLIENT_PROTOCOL_ERROR"
              - "STT_PROVIDER_ERROR"
              - "INTERNAL_ERROR"
              - "MEETING_ENDED"
              - "MEETING_PAUSED"
            endedAt:
              type: "string"
              description: "전사 종료 시각 (READY면 null)"
              nullable: true
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            startedAt:
              type: "string"
              description: "전사 시작 시각 (READY면 null)"
              nullable: true
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            readyExpiresAt:
              type: "string"
              description: "전사 연결 대기 만료 시각"
              nullable: false
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            noteId:
              type: "string"
              description: "노트의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AF"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            sessionId:
              type: "string"
              description: "전사 세션의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AG"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            status:
              type: "string"
              description: "전사 세션 상태"
              nullable: false
              example: "COMPLETED"
              enum:
              - "READY"
              - "ACTIVE"
              - "COMPLETED"
              - "INTERRUPTED"
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    AppErrorResponse:
      title: "AppErrorResponse"
      type: "object"
      required:
      - "success"
      - "data"
      - "error"
      properties:
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: false
        data:
          nullable: true
          enum:
          - null
          description: "실패 시 null인 응답 데이터"
        error:
          $ref: "#/components/schemas/AppError"
    CreateWorkspaceResponse:
      title: "CreateWorkspaceResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "description"
          - "name"
          - "role"
          - "workspaceId"
          type: "object"
          properties:
            role:
              type: "string"
              description: "현재 사용자의 워크스페이스 역할"
              nullable: false
              example: "ADMIN"
              enum:
              - "ADMIN"
              - "MEMBER"
            name:
              type: "string"
              description: "워크스페이스 이름"
              nullable: false
              example: "제품 팀"
              maxLength: 100
            description:
              type: "string"
              description: "워크스페이스 설명"
              nullable: true
              example: "제품을 만드는 팀의 워크스페이스"
              maxLength: 1000
            workspaceId:
              type: "string"
              description: "워크스페이스의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AD"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    AnalysisResponse:
      title: "AnalysisResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "analysisId"
          - "noteId"
          - "status"
          type: "object"
          properties:
            analysisId:
              type: "string"
              description: "분석 잡의 13자리 TSID"
              nullable: false
              example: "0K9GVJT2C4Q1Z"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            noteId:
              type: "string"
              description: "노트의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AF"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            status:
              type: "string"
              description: "분석 잡 상태"
              nullable: false
              example: "PENDING"
              enum:
              - "PENDING"
              - "RUNNING"
              - "SUCCEEDED"
              - "FAILED"
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    SendNoteSharedChatMessageRequest:
      title: "SendNoteSharedChatMessageRequest"
      required:
      - "message"
      type: "object"
      properties:
        message:
          type: "string"
          description: "유저 메시지 (공백만으로 구성 불가)"
          nullable: false
          example: "방금 논의한 배포 이슈 요약해줘"
          pattern: "[\\s\\S]*\\S[\\s\\S]*"
          minLength: 1
    CreateWorkspaceInvitationRequest:
      title: "CreateWorkspaceInvitationRequest"
      required:
      - "email"
      - "role"
      type: "object"
      properties:
        role:
          type: "string"
          description: "수락 시 부여할 워크스페이스 역할"
          nullable: false
          example: "MEMBER"
          enum:
          - "ADMIN"
          - "MEMBER"
        email:
          type: "string"
          description: "초대할 사용자의 가입 이메일"
          nullable: false
          example: "kim@example.com"
          format: "email"
    LogoutResponse:
      title: "LogoutResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "message"
          type: "object"
          properties:
            message:
              type: "string"
              description: "처리 결과 메시지"
              nullable: false
              example: "성공적으로 로그아웃했습니다."
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    AgentChatV2Response:
      title: "AgentChatV2Response"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "chatId"
          - "createdAt"
          - "scope"
          type: "object"
          properties:
            createdAt:
              type: "string"
              description: "생성 시각"
              nullable: false
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            chatId:
              type: "string"
              description: "채팅 세션의 13자리 TSID"
              nullable: false
              example: "0K9GVJT2C4Q2A"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            scope:
              type: "string"
              description: "컨텍스트 범위"
              nullable: false
              example: "note"
              enum:
              - "workspace"
              - "note"
            noteId:
              type: "string"
              description: "연결된 노트 (scope=workspace면 null)"
              nullable: true
              example: "0HZX2K7M9Q4AF"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            title:
              type: "string"
              description: "대화 제목"
              nullable: true
              example: "회의 후속"
            workspaceId:
              type: "string"
              description: "연결된 워크스페이스 (scope=note면 null)"
              nullable: true
              example: "0HZX2K7M9Q4AD"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    NoteRequest:
      title: "NoteRequest"
      required:
      - "title"
      type: "object"
      properties:
        title:
          type: "string"
          description: "노트 제목"
          nullable: false
          example: "주간 회의"
          minLength: 1
          maxLength: 200
    WorkspaceListResponse:
      title: "WorkspaceListResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "workspaces"
          type: "object"
          properties:
            workspaces:
              type: "array"
              description: "워크스페이스 목록"
              items:
                required:
                - "description"
                - "name"
                - "role"
                - "workspaceId"
                type: "object"
                properties:
                  role:
                    type: "string"
                    description: "현재 사용자의 워크스페이스 역할"
                    nullable: false
                    example: "ADMIN"
                    enum:
                    - "ADMIN"
                    - "MEMBER"
                  name:
                    type: "string"
                    description: "워크스페이스 이름"
                    nullable: false
                    example: "제품 팀"
                    maxLength: 100
                  description:
                    type: "string"
                    description: "워크스페이스 설명"
                    nullable: true
                    example: "제품을 만드는 팀의 워크스페이스"
                    maxLength: 1000
                  workspaceId:
                    type: "string"
                    description: "워크스페이스의 13자리 TSID"
                    nullable: false
                    example: "0HZX2K7M9Q4AD"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
              nullable: false
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    UpdateWorkspaceRequest:
      title: "UpdateWorkspaceRequest"
      required:
      - "name"
      type: "object"
      properties:
        name:
          type: "string"
          description: "워크스페이스 이름"
          nullable: false
          example: "제품 팀"
          minLength: 1
          maxLength: 100
        description:
          type: "string"
          description: "워크스페이스 설명"
          nullable: true
          example: "제품을 만드는 팀의 워크스페이스"
          maxLength: 1000
    NoteListResponse:
      title: "NoteListResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "notes"
          type: "object"
          properties:
            notes:
              type: "array"
              description: "노트 목록"
              items:
                required:
                - "activeSessionStartedAt"
                - "createdAt"
                - "lastRecordedAt"
                - "meetingStartedAt"
                - "meetingStartedBy"
                - "meetingStatus"
                - "noteId"
                - "participants"
                - "projectId"
                - "recordedDurationMs"
                - "title"
                - "updatedAt"
                type: "object"
                properties:
                  meetingStartedAt:
                    type: "string"
                    description: "최초 ACTIVE 전사 세션의 시작 시각 (한 번도 ACTIVE가 아니면 null)"
                    nullable: true
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  createdAt:
                    type: "string"
                    description: "노트 생성 시각"
                    nullable: false
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  lastRecordedAt:
                    type: "string"
                    description: "가장 최근 종료된 전사 세션의 시작 시각"
                    nullable: true
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  meetingStartedBy:
                    type: "object"
                    properties:
                      image:
                        type: "string"
                        description: "회의 시작자 프로필 이미지 URL (없으면 null)"
                        nullable: true
                        example: "https://cdn.example.com/avatars/hong.png"
                      name:
                        type: "string"
                        description: "회의 시작자 이름"
                        nullable: false
                        example: "홍길동"
                      userId:
                        type: "string"
                        description: "회의 시작자의 13자리 TSID"
                        nullable: false
                        example: "0HZX2K7M9Q4AC"
                        pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                        minLength: 13
                        maxLength: 13
                      email:
                        type: "string"
                        description: "회의 시작자 이메일"
                        nullable: false
                        example: "hong@example.com"
                    description: "회의 시작자 (녹음을 최초로 시작한 유저, 녹음 전이면 null)"
                    nullable: true
                    required:
                    - "userId"
                    - "name"
                    - "email"
                    - "image"
                  recordedDurationMs:
                    type: "integer"
                    description: "ACTIVE였고 종료된 COMPLETED/INTERRUPTED 세션의 startedAt~endedAt\
                      \ 구간만 합산한 누적 녹음 시간(ms), READY 중단과 진행 중 구간은 0"
                    nullable: false
                    example: 6500
                    format: "int64"
                    minimum: 0
                  meetingStatus:
                    type: "string"
                    description: "회의 상태"
                    nullable: false
                    example: "IN_PROGRESS"
                    enum:
                    - "NOT_STARTED"
                    - "IN_PROGRESS"
                    - "PAUSED"
                    - "ENDED"
                  noteId:
                    type: "string"
                    description: "노트의 13자리 TSID"
                    nullable: false
                    example: "0HZX2K7M9Q4AF"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  title:
                    type: "string"
                    description: "노트 제목"
                    nullable: false
                    example: "주간 회의"
                    maxLength: 200
                  projectId:
                    type: "string"
                    description: "프로젝트의 13자리 TSID"
                    nullable: false
                    example: "0HZX2K7M9Q4AE"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  activeSessionStartedAt:
                    type: "string"
                    description: "현재 ACTIVE 전사 세션의 시작 시각 (READY 또는 활성 세션이 없으면 null)"
                    nullable: true
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  participants:
                    type: "array"
                    description: "회의 참여자 목록. 워크스페이스 멤버만 등록되며 이름 오름차순이다. 없으면 빈 배열"
                    items:
                      required:
                      - "email"
                      - "image"
                      - "name"
                      - "userId"
                      type: "object"
                      properties:
                        image:
                          type: "string"
                          description: "참여자 프로필 이미지 URL (없으면 null)"
                          nullable: true
                          example: "https://cdn.example.com/avatars/hong.png"
                        name:
                          type: "string"
                          description: "참여자 이름"
                          nullable: false
                          example: "홍길동"
                        userId:
                          type: "string"
                          description: "참여자의 13자리 TSID"
                          nullable: false
                          example: "0HZX2K7M9Q4AC"
                          pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                          minLength: 13
                          maxLength: 13
                        email:
                          type: "string"
                          description: "참여자 이메일"
                          nullable: false
                          example: "hong@example.com"
                    nullable: false
                  updatedAt:
                    type: "string"
                    description: "노트 수정 시각"
                    nullable: false
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
              nullable: false
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    AcceptInvitationByTokenRequest:
      title: "AcceptInvitationByTokenRequest"
      required:
      - "token"
      type: "object"
      properties:
        token:
          type: "string"
          description: "초대 이메일 링크에 실린 수락 토큰"
          nullable: false
          example: "3q2wF0rQvXzYbA1cD2eF3gH4iJ5kL6mN7oP8rS9tUvw"
          maxLength: 64
    WorkspaceMemberListResponse:
      title: "WorkspaceMemberListResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "members"
          type: "object"
          properties:
            members:
              type: "array"
              description: "멤버 목록. 가입순 정렬"
              items:
                required:
                - "email"
                - "image"
                - "joinedAt"
                - "name"
                - "role"
                - "userId"
                type: "object"
                properties:
                  image:
                    type: "string"
                    description: "멤버 프로필 이미지 URL. 등록되지 않은 경우 null"
                    nullable: true
                    example: "https://cdn.heymoa.com/profiles/member.png"
                    format: "uri"
                  role:
                    type: "string"
                    description: "워크스페이스 역할"
                    nullable: false
                    example: "ADMIN"
                    enum:
                    - "ADMIN"
                    - "MEMBER"
                  joinedAt:
                    type: "string"
                    description: "워크스페이스 가입 시각"
                    nullable: false
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  name:
                    type: "string"
                    description: "멤버 이름"
                    nullable: false
                    example: "홍길동"
                  userId:
                    type: "string"
                    description: "멤버 사용자 식별자"
                    nullable: false
                    example: "0HZX2K7M9Q4AC"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  email:
                    type: "string"
                    description: "멤버 이메일"
                    nullable: false
                    example: "hong@example.com"
                    format: "email"
              nullable: false
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    NoteResponse:
      title: "NoteResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "activeSessionStartedAt"
          - "createdAt"
          - "meetingStartedAt"
          - "meetingStartedBy"
          - "meetingStatus"
          - "noteId"
          - "participants"
          - "projectId"
          - "recordedDurationMs"
          - "title"
          - "updatedAt"
          type: "object"
          properties:
            meetingStartedAt:
              type: "string"
              description: "최초 ACTIVE 전사 세션의 시작 시각 (한 번도 ACTIVE가 아니면 null)"
              nullable: true
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            createdAt:
              type: "string"
              description: "노트 생성 시각"
              nullable: false
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            meetingStartedBy:
              type: "object"
              properties:
                image:
                  type: "string"
                  description: "회의 시작자 프로필 이미지 URL (없으면 null)"
                  nullable: true
                  example: "https://cdn.example.com/avatars/hong.png"
                name:
                  type: "string"
                  description: "회의 시작자 이름"
                  nullable: false
                  example: "홍길동"
                userId:
                  type: "string"
                  description: "회의 시작자의 13자리 TSID"
                  nullable: false
                  example: "0HZX2K7M9Q4AC"
                  pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                  minLength: 13
                  maxLength: 13
                email:
                  type: "string"
                  description: "회의 시작자 이메일"
                  nullable: false
                  example: "hong@example.com"
              description: "회의 시작자 (녹음을 최초로 시작한 유저, 녹음 전이면 null)"
              nullable: true
              required:
              - "userId"
              - "name"
              - "email"
              - "image"
            recordedDurationMs:
              type: "integer"
              description: "ACTIVE였고 종료된 COMPLETED/INTERRUPTED 세션의 startedAt~endedAt\
                \ 구간만 합산한 누적 녹음 시간(ms), READY 중단과 진행 중 구간은 0"
              nullable: false
              example: 0
              format: "int64"
              minimum: 0
            meetingStatus:
              type: "string"
              description: "회의 상태"
              nullable: false
              example: "NOT_STARTED"
              enum:
              - "NOT_STARTED"
              - "IN_PROGRESS"
              - "PAUSED"
              - "ENDED"
            noteId:
              type: "string"
              description: "노트의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AF"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            title:
              type: "string"
              description: "노트 제목"
              nullable: false
              example: "주간 회의"
              maxLength: 200
            projectId:
              type: "string"
              description: "프로젝트의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AE"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            activeSessionStartedAt:
              type: "string"
              description: "현재 ACTIVE 전사 세션의 시작 시각 (READY 또는 활성 세션이 없으면 null)"
              nullable: true
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            participants:
              type: "array"
              description: "회의 참여자 목록. 워크스페이스 멤버만 등록되며 이름 오름차순이다. 없으면 빈 배열"
              items:
                required:
                - "email"
                - "image"
                - "name"
                - "userId"
                type: "object"
                properties:
                  image:
                    type: "string"
                    description: "참여자 프로필 이미지 URL (없으면 null)"
                    nullable: true
                    example: "https://cdn.example.com/avatars/hong.png"
                  name:
                    type: "string"
                    description: "참여자 이름"
                    nullable: false
                    example: "홍길동"
                  userId:
                    type: "string"
                    description: "참여자의 13자리 TSID"
                    nullable: false
                    example: "0HZX2K7M9Q4AC"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  email:
                    type: "string"
                    description: "참여자 이메일"
                    nullable: false
                    example: "hong@example.com"
              nullable: false
            updatedAt:
              type: "string"
              description: "노트 수정 시각"
              nullable: false
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    TranscriptResponse:
      title: "TranscriptResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "segments"
          type: "object"
          properties:
            segments:
              type: "array"
              description: "시간순 확정 전사 구간 목록"
              items:
                required:
                - "endedAtMs"
                - "segmentId"
                - "sequence"
                - "startedAtMs"
                - "text"
                - "transcriptionSessionId"
                type: "object"
                properties:
                  sequence:
                    type: "integer"
                    description: "세션 내 구간 순서"
                    nullable: false
                    example: 1
                    format: "int64"
                    minimum: 0
                  segmentId:
                    type: "string"
                    description: "확정 전사 구간의 13자리 TSID"
                    nullable: false
                    example: "0HZX2K7M9Q4AH"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  endedAtMs:
                    type: "integer"
                    description: "세션 시작 기준 PCM 구간 종료 밀리초"
                    nullable: false
                    example: 1200
                    format: "int64"
                    minimum: 0
                  text:
                    type: "string"
                    description: "확정 전사 텍스트"
                    nullable: false
                    example: "안녕하세요."
                  transcriptionSessionId:
                    type: "string"
                    description: "전사 세션의 13자리 TSID"
                    nullable: false
                    example: "0HZX2K7M9Q4AG"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  startedAtMs:
                    type: "integer"
                    description: "세션 시작 기준 PCM 구간 시작 밀리초"
                    nullable: false
                    example: 0
                    format: "int64"
                    minimum: 0
              nullable: false
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    WorkspaceInvitationListResponse:
      title: "WorkspaceInvitationListResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "invitations"
          type: "object"
          properties:
            invitations:
              type: "array"
              description: "대기 중인 초대 목록. 최신순 정렬"
              items:
                required:
                - "createdAt"
                - "expiresAt"
                - "invitationId"
                - "inviteeEmail"
                - "inviteeImage"
                - "inviteeName"
                - "inviterName"
                - "role"
                type: "object"
                properties:
                  createdAt:
                    type: "string"
                    description: "초대 생성 시각"
                    nullable: false
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  role:
                    type: "string"
                    description: "수락 시 부여될 워크스페이스 역할"
                    nullable: false
                    example: "MEMBER"
                    enum:
                    - "ADMIN"
                    - "MEMBER"
                  inviteeImage:
                    type: "string"
                    description: "초대받은 사용자 프로필 이미지 URL. 등록되지 않은 경우 null"
                    nullable: true
                    example: "https://cdn.heymoa.com/profiles/member.png"
                    format: "uri"
                  inviteeEmail:
                    type: "string"
                    description: "초대받은 사용자 이메일"
                    nullable: false
                    example: "kim@example.com"
                    format: "email"
                  inviteeName:
                    type: "string"
                    description: "초대받은 사용자 이름. 미가입자 초대는 null"
                    nullable: true
                    example: "김멤버"
                  invitationId:
                    type: "string"
                    description: "초대 식별자"
                    nullable: false
                    example: "0HZX2K7M9Q4AK"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  expiresAt:
                    type: "string"
                    description: "초대 만료 시각. 지난 뒤에는 수락할 수 없다"
                    nullable: false
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  inviterName:
                    type: "string"
                    description: "초대를 보낸 사용자 이름"
                    nullable: false
                    example: "홍길동"
              nullable: false
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    WorkspaceInvitationActionResponse:
      title: "WorkspaceInvitationActionResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "invitationId"
          - "role"
          - "status"
          - "workspaceId"
          type: "object"
          properties:
            role:
              type: "string"
              description: "수락 시 부여될 워크스페이스 역할"
              nullable: false
              example: "MEMBER"
              enum:
              - "ADMIN"
              - "MEMBER"
            invitationId:
              type: "string"
              description: "초대 식별자"
              nullable: false
              example: "0HZX2K7M9Q4AK"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            status:
              type: "string"
              description: "처리 후 초대 상태"
              nullable: false
              example: "CANCELED"
              enum:
              - "PENDING"
              - "ACCEPTED"
              - "DECLINED"
              - "CANCELED"
              - "EXPIRED"
            workspaceId:
              type: "string"
              description: "초대가 속한 워크스페이스 식별자"
              nullable: false
              example: "0HZX2K7M9Q4AD"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    NotificationListResponse:
      title: "NotificationListResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "notifications"
          - "unreadCount"
          type: "object"
          properties:
            unreadCount:
              type: "integer"
              description: "읽지 않은 알림 개수"
              nullable: false
              example: 0
              format: "int64"
            notifications:
              type: "array"
              description: "알림 목록. 최신순 최대 50개"
              items:
                required:
                - "createdAt"
                - "notificationId"
                - "readAt"
                - "type"
                type: "object"
                properties:
                  createdAt:
                    type: "string"
                    description: "알림 생성 시각"
                    nullable: false
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  invitation:
                    required:
                    - "expiresAt"
                    - "invitationId"
                    - "inviterName"
                    - "role"
                    - "status"
                    - "workspaceId"
                    - "workspaceName"
                    type: "object"
                    properties:
                      role:
                        type: "string"
                        description: "수락 시 부여될 워크스페이스 역할"
                        nullable: false
                        example: "MEMBER"
                        enum:
                        - "ADMIN"
                        - "MEMBER"
                      workspaceName:
                        type: "string"
                        description: "초대된 워크스페이스 이름"
                        nullable: false
                        example: "제품 팀"
                      invitationId:
                        type: "string"
                        description: "알림이 가리키는 초대 식별자"
                        nullable: false
                        example: "0HZX2K7M9Q4AK"
                        pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                        minLength: 13
                        maxLength: 13
                      inviterName:
                        type: "string"
                        description: "초대를 보낸 사용자 이름"
                        nullable: false
                        example: "홍길동"
                      expiresAt:
                        type: "string"
                        description: "초대 만료 시각. 지난 뒤에는 수락할 수 없다"
                        nullable: false
                        example: "2026-07-14T01:02:03Z"
                        format: "date-time"
                      workspaceId:
                        type: "string"
                        description: "초대된 워크스페이스 식별자"
                        nullable: false
                        example: "0HZX2K7M9Q4AD"
                        pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                        minLength: 13
                        maxLength: 13
                      status:
                        type: "string"
                        description: "초대의 현재 상태"
                        nullable: false
                        example: "PENDING"
                        enum:
                        - "PENDING"
                        - "ACCEPTED"
                        - "DECLINED"
                        - "CANCELED"
                        - "EXPIRED"
                  notificationId:
                    type: "string"
                    description: "알림 식별자"
                    nullable: false
                    example: "0HZX2K7M9Q4AJ"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  readAt:
                    type: "string"
                    description: "알림을 읽은 시각. 읽지 않았다면 null"
                    nullable: true
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  type:
                    type: "string"
                    description: "알림 종류"
                    nullable: false
                    example: "WORKSPACE_INVITATION"
                    enum:
                    - "WORKSPACE_INVITATION"
              nullable: false
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    InternalNoteContextResponse:
      title: "InternalNoteContextResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "meetingStatus"
          - "noteId"
          - "title"
          - "analysis"
          type: "object"
          properties:
            transcript:
              type: "array"
              items:
                required:
                - "startMs"
                - "text"
                type: "object"
                properties:
                  startMs:
                    type: "integer"
                    description: "발화 시작 시각 (ms)"
                    nullable: false
                    example: 0
                    format: "int64"
                  text:
                    type: "string"
                    description: "확정(final) 전사 본문, 시간순"
                    nullable: false
                    example: "이번 주 배포 이슈부터 볼게요"
            meetingStatus:
              type: "string"
              description: "회의 상태 (NOT_STARTED·IN_PROGRESS·PAUSED·ENDED)"
              nullable: false
              example: "ENDED"
              enum:
              - "NOT_STARTED"
              - "IN_PROGRESS"
              - "PAUSED"
              - "ENDED"
            noteId:
              type: "string"
              description: "노트 식별자"
              nullable: false
              example: "0HZX2K7M9Q4AF"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            analysis:
              type: "object"
              properties:
                overview:
                  type: "string"
                  description: "회의 개요 (markdown)"
                  nullable: false
                  example: "배포 이슈 논의…"
                actionItems:
                  type: "string"
                  description: "action item 목록 (markdown)"
                  nullable: false
                  example: "- APP-123 수정…"
                decisions:
                  type: "string"
                  description: "결정 목록 (markdown)"
                  nullable: false
                  example: "- 다음 주 배포로 정했다"
              description: "요약 3종 — 성공한 분석이 없으면 null"
              nullable: true
              required:
              - "overview"
              - "actionItems"
              - "decisions"
            title:
              type: "string"
              description: "노트 제목"
              nullable: false
              example: "7월 3주차 스프린트 회의"
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    AgentChatMessagesResponse:
      title: "AgentChatMessagesResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          type: "object"
          properties:
            messages:
              type: "array"
              items:
                required:
                - "content"
                - "createdAt"
                - "role"
                - "toolEvent"
                type: "object"
                properties:
                  createdAt:
                    type: "string"
                    description: "메시지 시각"
                    nullable: false
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  role:
                    type: "string"
                    description: "메시지 역할"
                    nullable: false
                    example: "USER"
                    enum:
                    - "USER"
                    - "ASSISTANT"
                    - "TOOL"
                  toolEvent:
                    type: "object"
                    properties:
                      decision:
                        type: "string"
                        description: "승인 확정 기록이면 결정, 실행 결과 기록이면 null"
                        nullable: true
                        example: "APPROVED"
                        enum:
                        - "APPROVED"
                        - "REJECTED"
                      url:
                        type: "string"
                        description: "생성된 외부 리소스 링크 (없으면 null)"
                        nullable: true
                        example: "https://linear.app/heymoa/issue/APP-123"
                      tool:
                        type: "string"
                        description: "도구 이름"
                        nullable: false
                        example: "linear.create_issue"
                      status:
                        type: "string"
                        description: "실행 결과 기록이면 상태, 승인 확정 기록이면 null"
                        nullable: true
                        example: "success"
                        enum:
                        - "success"
                        - "error"
                    description: "role=TOOL일 때만 값이 있는 구조화된 승인/도구 실행 기록 (그 외 null)"
                    nullable: true
                    required:
                    - "tool"
                    - "decision"
                    - "status"
                    - "url"
                  content:
                    type: "string"
                    description: "메시지 내용 (TOOL이면 표시용 요약)"
                    nullable: false
                    example: "질문"
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    ToolConnectionsResponse:
      title: "ToolConnectionsResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          type: "object"
          properties:
            integrations:
              type: "array"
              items:
                required:
                - "connected"
                - "provider"
                - "connectedBy"
                - "connectedAt"
                type: "object"
                properties:
                  connected:
                    type: "boolean"
                    description: "연동 여부"
                    nullable: false
                    example: true
                  connectedAt:
                    type: "string"
                    description: "연동 시각 (미연동 시 null)"
                    nullable: true
                    example: "2026-07-22T00:00:00Z"
                    format: "date-time"
                  provider:
                    type: "string"
                    description: "연동 provider"
                    nullable: false
                    example: "LINEAR"
                    enum:
                    - "LINEAR"
                    - "GITHUB"
                  connectedBy:
                    type: "string"
                    description: "연동한 ADMIN 표시 이름 (미연동 시 null)"
                    nullable: true
                    example: "김민수"
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    CurrentUserResponse:
      title: "CurrentUserResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "email"
          - "image"
          - "name"
          - "userId"
          type: "object"
          properties:
            image:
              type: "string"
              description: "사용자 프로필 이미지 URL. 등록되지 않은 경우 null"
              nullable: true
              example: "https://cdn.heymoa.com/profiles/member.png"
              format: "uri"
            name:
              type: "string"
              description: "사용자 이름"
              nullable: false
              example: "홍길동"
            userId:
              type: "string"
              description: "사용자의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AC"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            email:
              type: "string"
              description: "사용자 이메일"
              nullable: false
              example: "hong@example.com"
              format: "email"
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    UpdateWorkspaceResponse:
      title: "UpdateWorkspaceResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "description"
          - "name"
          - "workspaceId"
          type: "object"
          properties:
            name:
              type: "string"
              description: "워크스페이스 이름"
              nullable: false
              example: "제품 팀"
            description:
              type: "string"
              description: "워크스페이스 설명"
              nullable: true
              example: "제품을 만드는 팀의 워크스페이스"
            workspaceId:
              type: "string"
              description: "워크스페이스의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AD"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    ProjectListResponse:
      title: "ProjectListResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "projects"
          type: "object"
          properties:
            projects:
              type: "array"
              description: "프로젝트 목록"
              items:
                required:
                - "createdAt"
                - "description"
                - "name"
                - "projectId"
                - "updatedAt"
                - "workspaceId"
                type: "object"
                properties:
                  createdAt:
                    type: "string"
                    description: "프로젝트 생성 시각"
                    nullable: false
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  name:
                    type: "string"
                    description: "프로젝트 이름"
                    nullable: false
                    example: "모바일 앱"
                    maxLength: 100
                  description:
                    type: "string"
                    description: "프로젝트 설명"
                    nullable: true
                    example: "Heymoa 모바일 앱 프로젝트"
                    maxLength: 1000
                  projectId:
                    type: "string"
                    description: "프로젝트의 13자리 TSID"
                    nullable: false
                    example: "0HZX2K7M9Q4AE"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  updatedAt:
                    type: "string"
                    description: "프로젝트 수정 시각"
                    nullable: false
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  workspaceId:
                    type: "string"
                    description: "워크스페이스의 13자리 TSID"
                    nullable: false
                    example: "0HZX2K7M9Q4AD"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
              nullable: false
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    NoteSharedChatResponse:
      title: "NoteSharedChatResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "chatId"
          - "lock"
          - "messages"
          type: "object"
          properties:
            chatId:
              type: "string"
              description: "공유 챗봇 세션의 13자리 TSID (승인 API 등에서 사용)"
              nullable: false
              example: "0K9GVJT2C4Q2A"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            messages:
              type: "array"
              description: "표시용 메시지 목록 (시간순, 없으면 빈 배열)"
              items:
                required:
                - "content"
                - "createdAt"
                - "messageId"
                - "role"
                - "authorName"
                - "toolEvent"
                type: "object"
                properties:
                  createdAt:
                    type: "string"
                    description: "메시지 시각"
                    nullable: false
                    example: "2026-07-14T01:02:03Z"
                    format: "date-time"
                  role:
                    type: "string"
                    description: "메시지 역할"
                    nullable: false
                    example: "USER"
                    enum:
                    - "USER"
                    - "ASSISTANT"
                    - "TOOL"
                  toolEvent:
                    type: "object"
                    properties:
                      decision:
                        type: "string"
                        description: "승인 확정 기록이면 결정, 실행 결과 기록이면 null"
                        nullable: true
                        example: "APPROVED"
                        enum:
                        - "APPROVED"
                        - "REJECTED"
                      url:
                        type: "string"
                        description: "생성된 외부 리소스 링크 (없으면 null)"
                        nullable: true
                        example: "https://linear.app/heymoa/issue/APP-123"
                      tool:
                        type: "string"
                        description: "도구 이름"
                        nullable: false
                        example: "linear.create_issue"
                      status:
                        type: "string"
                        description: "실행 결과 기록이면 상태, 승인 확정 기록이면 null"
                        nullable: true
                        example: "success"
                        enum:
                        - "success"
                        - "error"
                    description: "role=TOOL일 때만 값이 있는 구조화된 승인/도구 실행 기록 (그 외 null)"
                    nullable: true
                    required:
                    - "tool"
                    - "decision"
                    - "status"
                    - "url"
                  authorName:
                    type: "string"
                    description: "USER 메시지의 작성자 표시 이름 (ASSISTANT/TOOL은 null)"
                    nullable: true
                    example: "홍길동"
                  messageId:
                    type: "string"
                    description: "메시지의 13자리 TSID"
                    nullable: false
                    example: "0K9GVJT2C4Q3B"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  content:
                    type: "string"
                    description: "메시지 내용 (TOOL이면 표시용 요약)"
                    nullable: false
                    example: "방금 논의한 배포 이슈 요약해줘"
              nullable: false
            lock:
              required:
              - "locked"
              - "lockedBy"
              - "pendingApproval"
              type: "object"
              properties:
                lockedBy:
                  type: "string"
                  description: "입력 중인 멤버 표시 이름 (잠금이 없으면 null)"
                  nullable: true
                  example: "홍길동"
                locked:
                  type: "boolean"
                  description: "현재 다른 멤버가 입력 중인지 여부 (만료된 잠금은 false)"
                  nullable: false
                  example: true
                pendingApproval:
                  required:
                  - "approvalId"
                  - "tool"
                  - "summary"
                  type: "object"
                  properties:
                    summary:
                      type: "string"
                      description: "도구 실행 요약 (AI가 제공하지 않으면 null)"
                      nullable: true
                      example: "Linear 이슈 생성"
                    approvalId:
                      type: "string"
                      description: "승인 요청의 13자 TSID (heymoa-ai 발급) — 승인 API 경로에 사용"
                      nullable: false
                      example: "0K9GVJT2C4Q7F"
                      pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                      minLength: 13
                      maxLength: 13
                    tool:
                      type: "string"
                      description: "승인 대기 중인 도구 이름"
                      nullable: false
                      example: "linear.create_issue"
                  description: "승인 대기 중이면 관전자 표시용 정보 (대기가 없으면 null) — 관전자 폴링 대상"
                  nullable: true
              description: "입력 잠금 상태"
              nullable: false
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    MarkNotificationReadResponse:
      title: "MarkNotificationReadResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "notificationId"
          type: "object"
          properties:
            notificationId:
              type: "string"
              description: "읽음 처리된 알림 식별자"
              nullable: false
              example: "0HZX2K7M9Q4AJ"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    ProjectResponse:
      title: "ProjectResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "createdAt"
          - "description"
          - "name"
          - "projectId"
          - "updatedAt"
          - "workspaceId"
          type: "object"
          properties:
            createdAt:
              type: "string"
              description: "프로젝트 생성 시각"
              nullable: false
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            name:
              type: "string"
              description: "프로젝트 이름"
              nullable: false
              example: "모바일 앱"
              maxLength: 100
            description:
              type: "string"
              description: "프로젝트 설명"
              nullable: true
              example: "Heymoa 모바일 앱 프로젝트"
              maxLength: 1000
            projectId:
              type: "string"
              description: "프로젝트의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AE"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
            updatedAt:
              type: "string"
              description: "프로젝트 수정 시각"
              nullable: false
              example: "2026-07-14T01:02:03Z"
              format: "date-time"
            workspaceId:
              type: "string"
              description: "워크스페이스의 13자리 TSID"
              nullable: false
              example: "0HZX2K7M9Q4AD"
              pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
              minLength: 13
              maxLength: 13
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    ChangeWorkspaceMemberRoleRequest:
      title: "ChangeWorkspaceMemberRoleRequest"
      required:
      - "role"
      type: "object"
      properties:
        role:
          type: "string"
          description: "바꿀 역할"
          nullable: false
          example: "ADMIN"
          enum:
          - "ADMIN"
          - "MEMBER"
    NoteParticipantsRequest:
      title: "NoteParticipantsRequest"
      required:
      - "userIds"
      type: "object"
      properties:
        userIds:
          type: "array"
          description: "참여자로 남길 유저의 13자리 TSID 목록(최대 200개). 이 목록이 곧 최종 상태이며 여기 없는 참\
            여자는 지워진다"
          items:
            oneOf:
            - type: "object"
            - type: "boolean"
            - type: "string"
            - type: "number"
          nullable: false
    AnalysisResultCallback:
      title: "AnalysisResultCallback"
      oneOf:
      - $ref: "#/components/schemas/AnalysisSucceededCallback"
      - $ref: "#/components/schemas/AnalysisFailedCallback"
      discriminator:
        propertyName: "status"
        mapping:
          SUCCEEDED: "#/components/schemas/AnalysisSucceededCallback"
          FAILED: "#/components/schemas/AnalysisFailedCallback"
    ProjectRequest:
      title: "ProjectRequest"
      required:
      - "name"
      type: "object"
      properties:
        name:
          type: "string"
          description: "프로젝트 이름"
          nullable: false
          example: "모바일 앱"
          minLength: 1
          maxLength: 100
        description:
          type: "string"
          description: "프로젝트 설명"
          nullable: true
          example: "Heymoa 모바일 앱 프로젝트"
          maxLength: 1000
    NoteParticipantListResponse:
      title: "NoteParticipantListResponse"
      required:
      - "data"
      - "error"
      - "success"
      type: "object"
      properties:
        data:
          required:
          - "participants"
          type: "object"
          properties:
            participants:
              type: "array"
              description: "교체 후의 참여자 목록. 이름 오름차순이다"
              items:
                required:
                - "email"
                - "image"
                - "name"
                - "userId"
                type: "object"
                properties:
                  image:
                    type: "string"
                    description: "참여자 프로필 이미지 URL (없으면 null)"
                    nullable: true
                    example: "https://cdn.example.com/avatars/hong.png"
                  name:
                    type: "string"
                    description: "참여자 이름"
                    nullable: false
                    example: "홍길동"
                  userId:
                    type: "string"
                    description: "참여자의 13자리 TSID"
                    nullable: false
                    example: "0HZX2K7M9Q4AC"
                    pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
                    minLength: 13
                    maxLength: 13
                  email:
                    type: "string"
                    description: "참여자 이메일"
                    nullable: false
                    example: "hong@example.com"
              nullable: false
          description: "성공 응답 데이터"
          nullable: false
        success:
          type: "boolean"
          description: "요청 성공 여부"
          nullable: false
          example: true
        error:
          nullable: true
          enum:
          - null
          description: "성공 시 null인 오류 정보"
    AnalysisSucceededCallback:
      title: "AnalysisSucceededCallback"
      type: "object"
      required:
      - "status"
      - "sections"
      properties:
        status:
          type: "string"
          description: "SUCCEEDED 고정"
          enum:
          - "SUCCEEDED"
          example: "SUCCEEDED"
        sections:
          type: "array"
          description: "OVERVIEW · ACTION_ITEM · DECISION 세 섹션. 뽑을 것이 없어도 빠지지 않는다"
          items:
            $ref: "#/components/schemas/AnalysisSection"
    AnalysisSection:
      title: "AnalysisSection"
      type: "object"
      required:
      - "kind"
      - "items"
      properties:
        kind:
          type: "string"
          description: "항목 종류"
          enum:
          - "OVERVIEW"
          - "ACTION_ITEM"
          - "DECISION"
          example: "OVERVIEW"
        items:
          type: "array"
          description: "그 섹션의 항목. 뽑을 것이 없었으면 빈 배열"
          items:
            $ref: "#/components/schemas/MeetingItem"
    MeetingItem:
      title: "MeetingItem"
      type: "object"
      required:
      - "content"
      - "evidence"
      properties:
        content:
          type: "string"
          minLength: 1
          description: "항목 본문 한 줄. 빈 항목은 저장하지 않고 버린다"
          example: "결제 실패율이 3%로 올랐다"
        evidence:
          type: "array"
          description: "이 항목이 나온 전사 세그먼트 id. 못 찾았으면 빈 배열"
          maxItems: 3
          items:
            type: "string"
            pattern: "^[0-9A-HJKMNP-TV-Z]{13}$"
            example: "0HZX2K7M9Q4AH"
    AnalysisFailedCallback:
      title: "AnalysisFailedCallback"
      type: "object"
      required:
      - "status"
      - "error"
      properties:
        status:
          type: "string"
          description: "FAILED 고정"
          enum:
          - "FAILED"
          example: "FAILED"
        error:
          type: "object"
          required:
          - "code"
          - "message"
          properties:
            code:
              type: "string"
              description: "기계 판독용 에러 코드"
              example: "ANALYSIS_TIMEOUT"
            message:
              type: "string"
              description: "에러 메시지"
              example: "분석이 제한 시간을 초과했습니다."
    AppErrorDetail:
      type: "object"
      required:
      - "field"
      - "message"
      properties:
        field:
          type: "string"
          example: "name"
          description: "오류가 발생한 필드"
          nullable: false
        message:
          type: "string"
          example: "이름은 필수입니다."
          description: "필드 오류 메시지"
          nullable: false
      title: "AppErrorDetail"
    AppError:
      type: "object"
      required:
      - "code"
      - "message"
      - "details"
      properties:
        code:
          type: "string"
          example: "WORKSPACE_NOT_FOUND"
          description: "애플리케이션 오류 코드"
          nullable: false
        message:
          type: "string"
          example: "워크스페이스를 찾을 수 없습니다."
          description: "사용자에게 표시할 오류 메시지"
          nullable: false
        details:
          type: "array"
          nullable: true
          items:
            $ref: "#/components/schemas/AppErrorDetail"
          description: "필드별 오류 상세"
      description: "오류 정보"
      title: "AppError"
  securitySchemes:
    accessCookie:
      type: "apiKey"
      in: "cookie"
      name: "access_token"
      description: "로그인 후 발급되는 HttpOnly access token 쿠키"
    internalToken:
      type: "apiKey"
      in: "header"
      name: "X-Internal-Token"
      description: "heymoa-ai와 공유하는 내부 시크릿. /internal 하위 전 경로에 필요하다"
  responses:
    Unauthorized:
      description: "인증 필요"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/AppErrorResponse"
          examples:
            UNAUTHORIZED:
              value:
                success: false
                data: null
                error:
                  code: "UNAUTHORIZED"
                  message: "인증이 필요합니다."
                  details: null
