{"openapi":"3.0.0","info":{"title":"HowToSay API","version":"1.0.0","description":"Unified Hono API for HowToSay clients and Spanish News frontend."},"servers":[{"url":"https://api.langtangs.com","description":"Production"}],"tags":[{"name":"Auth","description":"Authentication and session lifecycle."},{"name":"Profile","description":"Current user profile and runtime config."},{"name":"Expressions","description":"Expression generation and favorites."},{"name":"AskMe","description":"Spoken English practice question endpoints."},{"name":"Beta","description":"Beta personality restoration endpoints."},{"name":"Spanish News","description":"Article, translation, and TTS endpoints."}],"components":{"schemas":{"AppConfigResponse":{"type":"object","properties":{"targetLanguages":{"type":"array","items":{"$ref":"#/components/schemas/TargetLanguage"}},"scenes":{"type":"array","items":{"$ref":"#/components/schemas/Scene"}},"levels":{"type":"array","items":{"$ref":"#/components/schemas/UserLevel"}},"defaults":{"$ref":"#/components/schemas/AppDefaults"},"askmeDefaults":{"$ref":"#/components/schemas/AskMeDefaults"}},"required":["targetLanguages","scenes","levels","defaults"]},"TargetLanguage":{"type":"string","enum":["en"]},"Scene":{"type":"string","enum":["general","chat","work","travel","study"]},"UserLevel":{"type":"string","enum":["A1","A2","B1","B2"]},"AppDefaults":{"type":"object","properties":{"targetLanguage":{"$ref":"#/components/schemas/TargetLanguage"},"scene":{"$ref":"#/components/schemas/Scene"},"userLevel":{"$ref":"#/components/schemas/UserLevel"}},"required":["targetLanguage","scene","userLevel"]},"AskMeDefaults":{"type":"object","properties":{"scene":{"$ref":"#/components/schemas/Scene"},"userLevel":{"$ref":"#/components/schemas/UserLevel"}},"required":["scene","userLevel"]},"ErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"invalid_request"},"message":{"type":"string","example":"Request body must be a JSON object."}},"required":["code","message"]}},"required":["error"]},"AuthSessionResponse":{"type":"object","properties":{"sessionToken":{"type":"string"},"me":{"$ref":"#/components/schemas/MeResponse"}},"required":["sessionToken","me"]},"MeResponse":{"type":"object","properties":{"id":{"type":"string"},"displayName":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"currentLevel":{"$ref":"#/components/schemas/UserLevel"},"defaultTargetLanguage":{"$ref":"#/components/schemas/TargetLanguage"},"remainingQuota":{"type":"integer"},"isGuest":{"type":"boolean"},"authProvider":{"type":"string","nullable":true,"enum":["apple",null]}},"required":["id","displayName","email","currentLevel","defaultTargetLanguage","remainingQuota","isGuest","authProvider"]},"AppleSignInRequest":{"type":"object","properties":{"identityToken":{"type":"string","minLength":1},"authorizationCode":{"type":"string","nullable":true,"minLength":1},"email":{"type":"string","nullable":true,"minLength":1},"displayName":{"type":"string","nullable":true,"minLength":1}},"required":["identityToken"]},"GenerateExpressionResult":{"type":"object","properties":{"requestId":{"type":"string"},"intent":{"type":"string"},"tone":{"type":"string"},"sceneUsed":{"$ref":"#/components/schemas/Scene"},"literalTranslation":{"type":"string"},"naturalExpression":{"type":"string"},"shortExplanation":{"type":"string"}},"required":["requestId","intent","tone","sceneUsed","literalTranslation","naturalExpression","shortExplanation"]},"GenerateExpressionRequest":{"type":"object","properties":{"sourceText":{"type":"string","minLength":1},"sourceLanguage":{"$ref":"#/components/schemas/SourceLanguage"},"targetLanguage":{"$ref":"#/components/schemas/TargetLanguage"},"scene":{"$ref":"#/components/schemas/Scene"},"userLevel":{"$ref":"#/components/schemas/UserLevel"},"mode":{"$ref":"#/components/schemas/GenerateMode"}},"required":["sourceText","sourceLanguage","targetLanguage","scene","userLevel","mode"]},"SourceLanguage":{"type":"string","enum":["zh"]},"GenerateMode":{"type":"string","enum":["auto"]},"FavoriteMutationResponse":{"type":"object","properties":{"favoriteId":{"type":"string"},"requestId":{"type":"string"},"createdAt":{"type":"string"}},"required":["favoriteId","requestId","createdAt"]},"FavoriteExpressionRequest":{"type":"object","properties":{"requestId":{"type":"string","minLength":1}},"required":["requestId"]},"FavoritesListResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/FavoriteExpressionResponse"}}},"required":["items"]},"FavoriteExpressionResponse":{"type":"object","properties":{"favoriteId":{"type":"string"},"requestId":{"type":"string"},"sourceText":{"type":"string"},"scene":{"$ref":"#/components/schemas/Scene"},"intent":{"type":"string"},"tone":{"type":"string"},"literalTranslation":{"type":"string"},"naturalExpression":{"type":"string"},"shortExplanation":{"type":"string"},"createdAt":{"type":"string"}},"required":["favoriteId","requestId","sourceText","scene","intent","tone","literalTranslation","naturalExpression","shortExplanation","createdAt"]},"AskMeTextResponse":{"type":"object","properties":{"sceneUsed":{"$ref":"#/components/schemas/Scene"},"userLevelUsed":{"$ref":"#/components/schemas/UserLevel"},"questionText":{"type":"string"}},"required":["sceneUsed","userLevelUsed","questionText"]},"AskMeAudioResponse":{"type":"object","properties":{"sceneUsed":{"$ref":"#/components/schemas/Scene"},"userLevelUsed":{"$ref":"#/components/schemas/UserLevel"},"questionText":{"type":"string"},"audioBase64":{"type":"string"},"mimeType":{"type":"string","enum":["audio/pcm;rate=24000"]}},"required":["sceneUsed","userLevelUsed","questionText","audioBase64","mimeType"]},"SpanishNewsArticleResponse":{"type":"object","properties":{"article":{"$ref":"#/components/schemas/SpanishNewsArticle"}},"required":["article"]},"SpanishNewsArticle":{"type":"object","properties":{"slug":{"type":"string"},"level":{"$ref":"#/components/schemas/SpanishNewsLevel"},"cefr":{"type":"string"},"title":{"type":"string"},"paragraphs":{"type":"array","items":{"type":"string"}},"readingTimeText":{"type":"string"},"wordCount":{"type":"integer"},"publishedAt":{"type":"string"},"heroImageUrl":{"type":"string"},"quiz":{"$ref":"#/components/schemas/SpanishNewsQuizQuestion"}},"required":["slug","level","cefr","title","paragraphs","readingTimeText","wordCount","publishedAt","heroImageUrl","quiz"]},"SpanishNewsLevel":{"type":"string","enum":["1","2","3"]},"SpanishNewsQuizQuestion":{"type":"object","properties":{"eyebrow":{"type":"string"},"title":{"type":"string"},"prompt":{"type":"string"},"options":{"type":"array","items":{"type":"string"}},"correctIndex":{"type":"integer"},"currentQuestion":{"type":"integer"},"totalQuestions":{"type":"integer"},"ctaLabel":{"type":"string"}},"required":["eyebrow","title","prompt","options","correctIndex","currentQuestion","totalQuestions","ctaLabel"]},"SpanishNewsTranslateWordResponse":{"type":"object","properties":{"translation":{"type":"string"},"phonetic":{"type":"string"}},"required":["translation","phonetic"]},"SpanishNewsTranslateWordRequest":{"type":"object","properties":{"word":{"type":"string","minLength":1},"context":{"type":"string","minLength":1}},"required":["word","context"]},"SpanishNewsTranslateParagraphResponse":{"type":"object","properties":{"translation":{"type":"string"}},"required":["translation"]},"SpanishNewsTranslateParagraphRequest":{"type":"object","properties":{"paragraph":{"type":"string","minLength":1}},"required":["paragraph"]},"SpanishNewsTtsResponse":{"type":"object","properties":{"audioBase64":{"type":"string"},"mimeType":{"type":"string","enum":["audio/pcm;rate=24000"]}},"required":["audioBase64","mimeType"]},"SpanishNewsTtsRequest":{"type":"object","properties":{"text":{"type":"string","minLength":1}},"required":["text"]},"BetaRestoreExpressionResponse":{"type":"object","properties":{"requestId":{"type":"string"},"sceneUsed":{"$ref":"#/components/schemas/Scene"},"literalTranslation":{"type":"string"},"primary":{"$ref":"#/components/schemas/BetaPrimaryExpression"},"alternatives":{"type":"array","items":{"$ref":"#/components/schemas/BetaAlternativeExpression"},"example":[{"toneKey":"breezy","toneLabel":"Breezy","intent":"轻松提出疑问","personalitySignal":"松弛感","restoredExpression":"I think something's a little off here. Mind walking me through these extra charges?","coachNote":"更像轻松但不失分寸地提出质疑。"},{"toneKey":"sharp","toneLabel":"Sharp","intent":"据理说明问题","personalitySignal":"清晰边界","restoredExpression":"I need to push back on that. These extra charges don't line up with the original quote.","coachNote":"更适合需要明确立场的场景。"},{"toneKey":"gentle","toneLabel":"Gentle","intent":"委婉澄清情况","personalitySignal":"体贴分寸","restoredExpression":"I was hoping you could help me understand these extra charges, because they don't seem to match what I expected.","coachNote":"更适合先争取对方配合解释。"}]},"naturalExpression":{"type":"string"},"shortExplanation":{"type":"string"}},"required":["requestId","sceneUsed","literalTranslation","primary","alternatives","naturalExpression","shortExplanation"]},"BetaPrimaryExpression":{"type":"object","properties":{"toneKey":{"type":"string","example":"professional"},"toneLabel":{"type":"string","example":"Sharp"},"intent":{"type":"string","example":"据理说明问题"},"personalitySignal":{"type":"string","example":"清晰边界"},"restoredExpression":{"type":"string","example":"I need to push back on that. From where I'm standing, those extra charges don't line up with the original quote."},"coachNote":{"type":"string","example":"这个版本更像在冷静陈述立场，而不是情绪化抱怨。"}},"required":["toneKey","toneLabel","intent","personalitySignal","restoredExpression","coachNote"]},"BetaAlternativeExpression":{"type":"object","properties":{"toneKey":{"$ref":"#/components/schemas/BetaToneKey"},"toneLabel":{"type":"string","example":"Gentle"},"intent":{"type":"string","example":"委婉澄清情况"},"personalitySignal":{"type":"string","example":"体贴分寸"},"restoredExpression":{"type":"string","example":"I was hoping you could help me understand these extra charges, because they don't seem to match what I expected."},"coachNote":{"type":"string","example":"这个版本更柔和，适合先争取对方配合。"}},"required":["toneKey","toneLabel","intent","personalitySignal","restoredExpression","coachNote"]},"BetaToneKey":{"type":"string","enum":["breezy","sharp","gentle"]},"BetaRestoreExpressionRequest":{"allOf":[{"$ref":"#/components/schemas/GenerateExpressionRequest"},{"type":"object","properties":{}}]}},"parameters":{}},"paths":{"/v1/config":{"get":{"tags":["Profile"],"summary":"Get runtime configuration","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"App configuration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppConfigResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/auth/anonymous":{"post":{"tags":["Auth"],"summary":"Create an anonymous iOS session","security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Anonymous session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/auth/apple":{"post":{"tags":["Auth"],"summary":"Authenticate the iOS app with Apple Sign In","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppleSignInRequest"}}}},"responses":{"201":{"description":"Authenticated session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/auth/logout":{"post":{"tags":["Auth"],"summary":"Revoke the current iOS session and mint a guest session","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Logged out and replaced with guest session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/me":{"get":{"tags":["Profile"],"summary":"Get the current user profile","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Current user profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/expressions/generate":{"post":{"tags":["Expressions"],"summary":"Generate a natural English expression from Chinese input","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateExpressionRequest"}}}},"responses":{"201":{"description":"Generated expression result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateExpressionResult"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/expressions/favorites":{"post":{"tags":["Expressions"],"summary":"Save an expression as a favorite","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FavoriteExpressionRequest"}}}},"responses":{"201":{"description":"Favorite saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FavoriteMutationResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Request not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"tags":["Expressions"],"summary":"List favorites for the current caller","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Favorite expressions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FavoritesListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/askme/text":{"get":{"tags":["AskMe"],"summary":"Generate one English practice question based on askme defaults","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"One English practice question","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskMeTextResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/askme/audio":{"get":{"tags":["AskMe"],"summary":"Generate one English practice question and its Gemini TTS audio","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"One English practice question with audio","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskMeAudioResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/spanish-news/article":{"get":{"tags":["Spanish News"],"summary":"Get a Spanish News article by slug and level","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","default":"elena-vane-k2"},"required":false,"name":"slug","in":"query"},{"schema":{"$ref":"#/components/schemas/SpanishNewsLevel"},"required":false,"name":"level","in":"query"}],"responses":{"200":{"description":"Article payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpanishNewsArticleResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Article not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/spanish-news/translate-word":{"post":{"tags":["Spanish News"],"summary":"Translate one word using its sentence context","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpanishNewsTranslateWordRequest"}}}},"responses":{"200":{"description":"Word translation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpanishNewsTranslateWordResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/spanish-news/translate-paragraph":{"post":{"tags":["Spanish News"],"summary":"Translate a full article paragraph to Spanish","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpanishNewsTranslateParagraphRequest"}}}},"responses":{"200":{"description":"Paragraph translation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpanishNewsTranslateParagraphResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/spanish-news/tts":{"post":{"tags":["Spanish News"],"summary":"Generate article narration TTS","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpanishNewsTtsRequest"}}}},"responses":{"200":{"description":"PCM audio payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpanishNewsTtsResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/beta/expressions/restore":{"post":{"tags":["Beta"],"summary":"Restore personality-rich English expressions from Chinese input","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BetaRestoreExpressionRequest"}}}},"responses":{"201":{"description":"Restored personality expression result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BetaRestoreExpressionResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}