openapi: 3.0.3 info: title: YuXinGu API version: 0.2.0 description: | 愈心谷 P1 API 契约(冻结草案)。 用户可见文案遵循 .ai/product/lexicon.md。 统一信封:{ code, message, data };code=0 成功。 servers: - url: http://127.0.0.1:8080 tags: - name: system - name: profile - name: portrait - name: scale - name: relation - name: report - name: ask - name: companion - name: commerce - name: admin paths: /api/v1/healthz: get: tags: [system] summary: Liveness responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/Envelope' /api/v1/ping: get: tags: [system] summary: Ping responses: '200': description: OK /api/v1/admin/auth/login: post: tags: [admin] summary: Admin login requestBody: required: true content: application/json: schema: type: object required: [username, password] properties: username: { type: string } password: { type: string } responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/Envelope' /api/v1/admin/auth/logout: post: tags: [admin] summary: Admin logout responses: '200': description: OK /api/v1/admin/me: get: tags: [admin] summary: Current admin (role + permissions[]) responses: '200': description: OK content: application/json: schema: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: $ref: '#/components/schemas/AdminMe' /api/v1/admin/roles: get: tags: [admin] summary: List admin roles description: Requires admin.roles.read responses: '200': description: OK '403': description: Forbidden /api/v1/admin/roles/{id}: get: tags: [admin] summary: Role detail with permissions description: Requires admin.roles.read parameters: - $ref: '#/components/parameters/Id' responses: '200': description: OK '403': description: Forbidden '404': description: Not found /api/v1/admin/roles/{id}/permissions: put: tags: [admin] summary: Replace role permission set description: Requires admin.roles.write; writes AuditLog parameters: - $ref: '#/components/parameters/Id' requestBody: required: true content: application/json: schema: type: object required: [permissions] properties: permissions: type: array items: { type: string } responses: '200': description: OK '400': description: Invalid permission code '403': description: Forbidden '404': description: Not found /api/v1/admin/users: get: tags: [admin] summary: List users parameters: - in: query name: q schema: { type: string } - in: query name: limit schema: { type: integer } - in: query name: offset schema: { type: integer } responses: '200': description: OK /api/v1/admin/users/{id}: get: tags: [admin] summary: User detail parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK /api/v1/admin/users/{id}/membership/grant: post: tags: [admin] summary: Grant or extend membership parameters: - in: path name: id required: true schema: { type: string, format: uuid } requestBody: required: true content: application/json: schema: type: object required: [plan] properties: plan: { type: string, enum: [month, quarter, year] } responses: '200': description: OK /api/v1/admin/users/{id}/status: post: tags: [admin] summary: Transition UserStatus description: Requires admin.users.status.write; writes transition + AuditLog parameters: - in: path name: id required: true schema: { type: string, format: uuid } requestBody: required: true content: application/json: schema: type: object required: [status, reason] properties: status: { type: string, enum: [active, disabled, banned, suspended] } reason: { type: string } responses: '200': description: OK '400': description: Invalid edge or missing reason '403': description: Forbidden /api/v1/admin/users/{id}/status-transitions: get: tags: [admin] summary: List UserStatus transitions description: Requires admin.users.read parameters: - in: path name: id required: true schema: { type: string, format: uuid } - in: query name: limit schema: { type: integer } responses: '200': description: OK '401': description: Unauthorized /api/v1/admin/users/{id}/insight: get: tags: [admin] summary: UserIntelligence insight (read-only aggregate) description: Requires admin.users.read; no schema write parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden '404': description: User not found /api/v1/admin/users/{id}/entitlements: get: tags: [admin] summary: User Entitlement aggregate (read-only) description: Requires admin.users.read; Membership ∪ DeepAccess ∪ ask quotas parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '401': description: Unauthorized '404': description: User not found /api/v1/admin/membership-plans: get: tags: [admin] summary: List membership plans description: Requires admin.membership.plans.read responses: '200': description: OK '403': description: Forbidden /api/v1/admin/membership-plans/{code}: get: tags: [admin] summary: Get membership plan parameters: - in: path name: code required: true schema: { type: string } responses: '200': description: OK '404': description: Not found put: tags: [admin] summary: Update membership plan description: Requires admin.membership.plans.write parameters: - in: path name: code required: true schema: { type: string } requestBody: required: true content: application/json: schema: type: object required: [title, duration_days, amount_cents] properties: title: { type: string } duration_days: { type: integer } amount_cents: { type: integer } active: { type: boolean } responses: '200': description: OK '400': description: Invalid '403': description: Forbidden /api/v1/admin/redemption-batches: get: tags: [admin] summary: List redemption batches responses: { '200': { description: OK } } post: tags: [admin] summary: Create redemption batch requestBody: required: true content: application/json: schema: type: object required: [label, plan_code, quantity] properties: label: { type: string } plan_code: { type: string } quantity: { type: integer, minimum: 1, maximum: 100 } responses: { '200': { description: OK } } /api/v1/admin/redemption-batches/{id}/codes: get: tags: [admin] summary: List codes in batch parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: { '200': { description: OK } } /api/v1/admin/redemption-codes/{id}/disable: post: tags: [admin] summary: Disable unused code parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: { '200': { description: OK } } /api/v1/admin/ask/threads: get: tags: [admin] summary: List AskSessionView (read-only) description: Requires admin.ask.read parameters: - in: query name: user_id schema: { type: string, format: uuid } - in: query name: limit schema: { type: integer } - in: query name: offset schema: { type: integer } responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/ask/threads/{id}: get: tags: [admin] summary: Ask session detail with messages description: Requires admin.ask.read; no message mutation parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/content-safety/filter-rules: get: tags: [admin] summary: List FilterRule description: Requires admin.content_safety.read responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/content-safety/filter-rules/{id}: get: tags: [admin] summary: Get FilterRule parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/content-safety/evaluate: post: tags: [admin] summary: Preview FilterRule matches (no ModerationCase write) requestBody: required: true content: application/json: schema: type: object required: [text] properties: text: { type: string } responses: '200': description: OK /api/v1/admin/ask/feedback: get: tags: [admin] summary: List QualityFeedback description: Requires admin.ask.read responses: '200': description: OK '401': description: Unauthorized /api/v1/admin/ask/threads/{id}/feedback: post: tags: [admin] summary: Create QualityFeedback (ops) description: Requires admin.ask.feedback.write; writes AuditLog parameters: - in: path name: id required: true schema: { type: string, format: uuid } requestBody: required: true content: application/json: schema: type: object required: [rating] properties: rating: { type: integer, minimum: 1, maximum: 5 } tag: { type: string, enum: [helpful, off_topic, unsafe, other] } note: { type: string } message_id: { type: string, format: uuid } responses: '200': description: OK '400': description: Invalid '403': description: Forbidden /api/v1/ask/threads/{id}/feedback: post: tags: [ask] summary: User QualityFeedback on owned thread parameters: - in: path name: id required: true schema: { type: string, format: uuid } requestBody: required: true content: application/json: schema: type: object required: [rating] properties: rating: { type: integer, minimum: 1, maximum: 5 } tag: { type: string } note: { type: string } message_id: { type: string, format: uuid } responses: '200': description: OK /api/v1/admin/ai/system-prompts: get: tags: [admin] summary: List SystemPrompt catalog description: Requires admin.ai_config.read responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/ai/system-prompts/{id}: get: tags: [admin] summary: Get SystemPrompt parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/ai/knowledge-sources: get: tags: [admin] summary: List KnowledgeSource catalog description: Requires admin.ai_config.read responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/ai/knowledge-sources/{id}: get: tags: [admin] summary: Get KnowledgeSource parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/cms/banners: get: tags: [admin] summary: List OpsCMS Banner catalog description: Requires admin.cms.read responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/cms/banners/{id}: get: tags: [admin] summary: Get Banner parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/cms/feed-slots: get: tags: [admin] summary: List OpsCMS FeedSlot catalog description: Requires admin.cms.read responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/cms/feed-slots/{id}: get: tags: [admin] summary: Get FeedSlot parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/cms/publications: get: tags: [admin] summary: List ScheduledPublication catalog description: Requires admin.cms.read responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/cms/publications/{id}: get: tags: [admin] summary: Get ScheduledPublication parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/ai/knowledge-chunks: get: tags: [admin] summary: List KnowledgeChunk catalog description: Requires admin.ai_config.read responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/ai/knowledge-chunks/{id}: get: tags: [admin] summary: Get KnowledgeChunk parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/ai/tools: get: tags: [admin] summary: List ToolDefinition catalog description: Requires admin.ai_config.read responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/ai/tools/{id}: get: tags: [admin] summary: Get ToolDefinition parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/content-safety/block-policies: get: tags: [admin] summary: List BlockPolicy catalog description: Requires admin.content_safety.read responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/content-safety/block-policies/{id}: get: tags: [admin] summary: Get BlockPolicy parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/content-safety/cases: get: tags: [admin] summary: List ModerationCase catalog description: Requires admin.content_safety.read responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/content-safety/cases/{id}: get: tags: [admin] summary: Get ModerationCase parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/crisis/policies: get: tags: [admin] summary: List CrisisPolicy description: Requires admin.crisis.read responses: '200': description: OK '401': description: Unauthorized '403': description: Forbidden /api/v1/admin/crisis/policies/{id}: get: tags: [admin] summary: Get CrisisPolicy parameters: - in: path name: id required: true schema: { type: string, format: uuid } responses: '200': description: OK '404': description: Not found /api/v1/admin/crisis/evaluate: post: tags: [admin] summary: Preview CrisisPolicy matches (no CrisisEvent write) requestBody: required: true content: application/json: schema: type: object required: [text] properties: text: { type: string } responses: '200': description: OK /api/v1/admin/orders: get: tags: [admin] summary: List orders responses: '200': description: OK /api/v1/admin/audit-logs: get: tags: [admin] summary: List audit logs responses: '200': description: OK /api/v1/admin/analytics/overview: get: tags: [admin] summary: Behavior analytics overview (DAU / sessions) parameters: - in: query name: from schema: { type: string, format: date } - in: query name: to schema: { type: string, format: date } responses: '200': description: OK /api/v1/admin/analytics/pages: get: tags: [admin] summary: Page PV/UV/dwell/exit aggregates parameters: - in: query name: from schema: { type: string, format: date } - in: query name: to schema: { type: string, format: date } responses: '200': description: OK /api/v1/admin/analytics/exits: get: tags: [admin] summary: Exit page ranking parameters: - in: query name: from schema: { type: string, format: date } - in: query name: to schema: { type: string, format: date } responses: '200': description: OK /api/v1/admin/analytics/clicks: get: tags: [admin] summary: UI click ranking parameters: - in: query name: from schema: { type: string, format: date } - in: query name: to schema: { type: string, format: date } responses: '200': description: OK /api/v1/admin/analytics/funnel: get: tags: [admin] summary: Core funnel step counts parameters: - in: query name: from schema: { type: string, format: date } - in: query name: to schema: { type: string, format: date } responses: '200': description: OK /api/v1/analytics/events: post: tags: [system] summary: Batch ingest behavior events (DeviceAuth) requestBody: required: true content: application/json: schema: type: object required: [items] properties: items: type: array maxItems: 100 items: type: object required: [name, session_id, client_ts] properties: name: { type: string } session_id: { type: string } page_path: { type: string } client_ts: {} props: { type: object } responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/Envelope' /api/v1/home/tools: get: tags: [system] summary: Homepage grid tools (enabled only) responses: '200': description: OK /api/v1/admin/home/tools: get: tags: [admin] summary: List all homepage tools responses: '200': description: OK put: tags: [admin] summary: Replace homepage tools requestBody: required: true content: application/json: schema: type: object required: [items] properties: items: { type: array } responses: '200': description: OK /api/v1/admin/scales: get: tags: [admin] summary: List all scales (incl. draft) responses: '200': description: OK /api/v1/admin/scales/{id}: patch: tags: [admin] summary: Set scale status published|draft parameters: - in: path name: id required: true schema: { type: string, format: uuid } requestBody: required: true content: application/json: schema: type: object required: [status] properties: status: { type: string, enum: [published, draft] } responses: '200': description: OK /api/v1/profiles: get: tags: [profile] summary: 列出个人档案 responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/EnvelopeProfileList' post: tags: [profile] summary: 创建个人档案 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateProfileRequest' responses: '200': description: OK content: application/json: schema: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: { $ref: '#/components/schemas/Profile' } /api/v1/profiles/{id}: patch: tags: [profile] summary: 更新个人档案 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: OK content: application/json: schema: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: { $ref: '#/components/schemas/Profile' } delete: tags: [profile] summary: 删除个人档案 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: OK /api/v1/reports/portrait: post: tags: [portrait] summary: 生成个人画像成长报告 requestBody: required: true content: application/json: schema: type: object required: [profile_id] properties: profile_id: { type: string, format: uuid } responses: '200': description: 返回 GrowthReport(detail 按权益裁剪) content: application/json: schema: $ref: '#/components/schemas/EnvelopeGrowthReport' /api/v1/explore/catalog: get: tags: [explore] summary: 探索三级目录(L1 分类 → L3 工具) responses: '200': description: categories[] /api/v1/explore/catalog/{key}: get: tags: [explore] summary: 单个探索分类 parameters: - name: key in: path required: true schema: { type: string } responses: '200': description: Category '404': description: not found /api/v1/growth/plans: get: tags: [growth] summary: 成长计划列表 responses: '200': description: OK post: tags: [growth] summary: 创建成长计划 responses: '200': description: OK /api/v1/growth/plans/{id}/checkin: post: tags: [growth] summary: 今日打卡 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: OK /api/v1/growth/plans/{id}/checkins: get: tags: [growth] summary: 打卡记录 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: OK /api/v1/moods/recent: get: tags: [companion] summary: 近七日心情轨迹 responses: '200': description: OK /api/v1/reports/star: post: tags: [star] summary: 生成本命排盘 / 运势成长报告 requestBody: required: true content: application/json: schema: type: object required: [profile_id] properties: profile_id: { type: string, format: uuid } responses: '200': description: GrowthReport type=star;summary 含 chart/planets/aspects_preview/outlook(日周月年一生)/transits;无 fortune/lucky 键;detail 按权益裁剪 content: application/json: schema: $ref: '#/components/schemas/EnvelopeGrowthReport' /api/v1/reports/synastry: post: tags: [star] summary: 生成双人合盘报告(五主盘 + 次限推运) requestBody: required: true content: application/json: schema: type: object required: [profile_id_a, profile_id_b] properties: profile_id_a: { type: string, format: uuid } profile_id_b: { type: string, format: uuid } as_of: { type: string, format: date, description: 次限推运日期 YYYY-MM-DD } responses: '200': description: GrowthReport type=synastry(三指数 + charts 五主盘/推运) /api/v1/synastry/nearby: get: tags: [star] summary: 附近可合盘档案 parameters: - in: query name: lat required: true schema: { type: number } - in: query name: lng required: true schema: { type: number } - in: query name: radius_km schema: { type: number, default: 50 } responses: '200': description: items[{profile, distance_km}] /api/v1/synastry/invites: post: tags: [star] summary: 创建合盘邀请 requestBody: required: true content: application/json: schema: type: object required: [profile_id] properties: profile_id: { type: string, format: uuid } responses: '200': description: token / path / expires_at /api/v1/synastry/invites/{token}: get: tags: [star] summary: 邀请元数据 parameters: - in: path name: token required: true schema: { type: string } responses: '200': description: host_name / expires_at / already_accepted /api/v1/synastry/invites/{token}/accept: post: tags: [star] summary: 接受合盘邀请并生成报告 parameters: - in: path name: token required: true schema: { type: string } requestBody: required: true content: application/json: schema: type: object required: [birth_date] properties: display_name: { type: string } birth_date: { type: string, format: date } birth_time: { type: string } birth_place: { type: string } responses: '200': description: GrowthReport type=synastry content: application/json: schema: $ref: '#/components/schemas/EnvelopeGrowthReport' /api/v1/reports/rhythm: post: tags: [rhythm] summary: 生成身心节律成长报告 requestBody: required: true content: application/json: schema: type: object required: [profile_id] properties: profile_id: { type: string, format: uuid } responses: '200': description: GrowthReport type=rhythm content: application/json: schema: $ref: '#/components/schemas/EnvelopeGrowthReport' /api/v1/image-cards/scenes: get: tags: [image-card] summary: 意象卡片场景列表 responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/EnvelopeImageCardScenes' /api/v1/image-cards/quota: get: tags: [image-card] summary: 今日抽卡剩余次数 responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/EnvelopeImageCardQuota' /api/v1/image-cards/draw: post: tags: [image-card] summary: 抽意象卡片(沉淀为 GrowthReport type=image_card) requestBody: required: true content: application/json: schema: type: object required: [profile_id] properties: profile_id: { type: string, format: uuid } scene: { type: string } depth: { type: boolean, description: 请求三卡组合(无权益则 detail 剥离) } responses: '200': description: report + cards + quota_left '402': description: 免费次数用尽 /api/v1/relation/insight: post: tags: [relation] summary: 生成关系理解 requestBody: required: true content: application/json: schema: type: object required: [profile_a_id, profile_b_id] properties: profile_a_id: { type: string, format: uuid } profile_b_id: { type: string, format: uuid } responses: '200': description: RelationInsight + 可选 report_id /api/v1/reports: get: tags: [report] summary: 列出当前用户成长报告 responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/EnvelopeReportList' /api/v1/reports/{id}: get: tags: [report] summary: 获取成长报告(权益裁剪) parameters: - $ref: '#/components/parameters/Id' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/EnvelopeGrowthReport' /api/v1/scales: get: tags: [scale] summary: 探索测试列表 responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/EnvelopeScaleList' /api/v1/scales/{slug}: get: tags: [scale] summary: 探索测试题目 parameters: - name: slug in: path required: true schema: { type: string } responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/EnvelopeScaleDetail' /api/v1/scales/{slug}/result: post: tags: [scale] summary: 提交探索测试并计分 parameters: - name: slug in: path required: true schema: { type: string } requestBody: required: true content: application/json: schema: type: object required: [profile_id, answers] properties: profile_id: { type: string, format: uuid } answers: { type: object } responses: '200': description: 探索结果 /api/v1/ask/quota: get: tags: [ask] summary: 当前问答剩余次数(免费额度或成长会员) responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/EnvelopeAskQuota' /api/v1/ask/threads: post: tags: [ask] summary: 创建问答线程(须绑定 profile) requestBody: required: true content: application/json: schema: type: object required: [profile_id] properties: profile_id: { type: string, format: uuid } scene: { type: string, description: self|relation|career|emotion|life } responses: '200': description: OK /api/v1/ask/threads/{id}/messages: get: tags: [ask] summary: 拉取线程消息历史 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: OK post: tags: [ask] summary: 发送消息(扣额度;P1 规则引擎回复) parameters: - $ref: '#/components/parameters/Id' requestBody: required: true content: application/json: schema: type: object required: [content] properties: content: { type: string } responses: '200': description: user + assistant + quota '402': description: 额度用尽 /api/v1/solar-terms/today: get: tags: [companion] summary: 今日节气生活建议 responses: '200': description: OK /api/v1/moods: post: tags: [companion] summary: 记录/更新当日心情 requestBody: required: true content: application/json: schema: type: object properties: score: { type: integer, minimum: 1, maximum: 5 } note: { type: string, maxLength: 200 } day: { type: string, format: date } responses: '200': description: Mood /api/v1/moods/today: get: tags: [companion] summary: 今日心情 responses: '200': description: '{ mood: Mood | null }' /api/v1/membership/me: get: tags: [commerce] summary: 当前成长会员权益 responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/EnvelopeMembershipMe' /api/v1/membership/redeem: post: tags: [commerce] summary: 兑换会员码 requestBody: required: true content: application/json: schema: type: object required: [code] properties: code: { type: string } responses: '200': description: OK '400': description: Invalid or used code '401': description: Unauthorized /api/v1/orders: post: tags: [commerce] summary: 创建订单(membership | deep_access | ask_pack) requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateOrderRequest' responses: '200': description: OK /api/v1/orders/{id}/pay-mock: post: tags: [commerce] summary: 模拟支付成功 parameters: - $ref: '#/components/parameters/Id' responses: '200': description: OK components: parameters: Id: name: id in: path required: true schema: { type: string, format: uuid } schemas: Envelope: type: object required: [code, message] properties: code: { type: integer, example: 0 } message: { type: string, example: success } data: { type: object } AdminMe: type: object required: [id, username, permissions] properties: id: { type: string, format: uuid } username: { type: string } role: { type: string } permissions: type: array items: { type: string } CreateProfileRequest: type: object required: [relation, birth_date] properties: relation: { type: string, enum: [self, other] } display_name: { type: string } birth_date: { type: string, format: date } birth_time: { type: string, nullable: true } birth_place: { type: string, nullable: true } gender: { type: string, nullable: true } relation_type: { type: string, nullable: true } CreateOrderRequest: type: object required: [kind] properties: kind: { type: string, enum: [membership, deep_access, ask_pack] } plan: { type: string, description: month|quarter|year when membership; pack10|pack30|pack100 when ask_pack } report_id: { type: string, format: uuid, description: when deep_access } # --- Aligned with packages/types (ECR-004) --- Profile: type: object required: [id, user_id, relation, display_name, birth_date, created_at] properties: id: { type: string, format: uuid } user_id: { type: string, format: uuid } relation: { type: string, enum: [self, other] } display_name: { type: string } birth_date: { type: string, format: date } birth_time: { type: string, nullable: true } birth_place: { type: string, nullable: true } relation_type: { type: string, nullable: true } geo_lat: { type: number, nullable: true } geo_lng: { type: number, nullable: true } geo_visible: { type: boolean } created_at: { type: string, format: date-time } GrowthReport: type: object required: [id, user_id, profile_id, type, summary, has_deep_access, created_at] properties: id: { type: string, format: uuid } user_id: { type: string, format: uuid } profile_id: { type: string, format: uuid } type: { type: string } summary: { type: object, additionalProperties: true } detail: { type: object, nullable: true, additionalProperties: true } has_deep_access: { type: boolean } created_at: { type: string, format: date-time } MembershipMe: type: object required: [active, status] properties: active: { type: boolean } plan: { type: string } status: { type: string } expires_at: { type: string, format: date-time, nullable: true } ask_quota_left: { type: integer } ScaleSummary: type: object required: [slug, title, description] properties: slug: { type: string } title: { type: string } description: { type: string } ScaleQuestionOption: type: object required: [key, text] properties: key: { type: string } text: { type: string } ScaleQuestion: type: object required: [id, sort, body] properties: id: { type: string, format: uuid } sort: { type: integer } body: type: object required: [prompt, options] properties: prompt: { type: string } options: type: array items: { $ref: '#/components/schemas/ScaleQuestionOption' } ScaleDetail: type: object required: [slug, title, description, questions] properties: slug: { type: string } title: { type: string } description: { type: string } questions: type: array items: { $ref: '#/components/schemas/ScaleQuestion' } ScaleSubmitResult: type: object required: [id, result] properties: id: { type: string, format: uuid } result: { type: object, additionalProperties: true } AskQuota: type: object required: [active_membership, remaining, free_limit, source] properties: active_membership: { type: boolean } remaining: { type: integer } free_limit: { type: integer } source: { type: string } AskThread: type: object required: [id, user_id, profile_id, created_at] properties: id: { type: string, format: uuid } user_id: { type: string, format: uuid } profile_id: { type: string, format: uuid } scene: { type: string, nullable: true } created_at: { type: string, format: date-time } AskMessage: type: object required: [id, thread_id, role, content, created_at] properties: id: { type: string, format: uuid } thread_id: { type: string, format: uuid } role: { type: string, enum: [user, assistant] } content: { type: string } created_at: { type: string, format: date-time } ImageCardScene: type: object required: [key, label] properties: key: { type: string } label: { type: string } ImageCardQuota: type: object required: [remaining, daily_free, unlimited] properties: remaining: { type: integer } daily_free: { type: integer } unlimited: { type: boolean } EnvelopeProfileList: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: type: object properties: items: type: array items: { $ref: '#/components/schemas/Profile' } EnvelopeGrowthReport: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: { $ref: '#/components/schemas/GrowthReport' } EnvelopeMembershipMe: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: { $ref: '#/components/schemas/MembershipMe' } EnvelopeScaleList: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: type: object properties: items: type: array items: { $ref: '#/components/schemas/ScaleSummary' } EnvelopeAskQuota: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: { $ref: '#/components/schemas/AskQuota' } EnvelopeReportList: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: type: object properties: items: type: array items: { $ref: '#/components/schemas/GrowthReport' } EnvelopeScaleDetail: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: { $ref: '#/components/schemas/ScaleDetail' } EnvelopeImageCardScenes: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: type: object properties: items: type: array items: { $ref: '#/components/schemas/ImageCardScene' } EnvelopeImageCardQuota: allOf: - $ref: '#/components/schemas/Envelope' - type: object properties: data: { $ref: '#/components/schemas/ImageCardQuota' }