{"openapi":"3.1.0","info":{"title":"Framed! User API","version":"1.3.0","summary":"自分のアカウントとして、投稿・コメント・いいね・プロフィールを読み書きするための API","description":"Framed! に**自分として**投稿し、反応し、プロフィールを整えるための API です。扱えるのは 4 つです。\n\n- **投稿**: HTML・Markdown・リブログ（URL の共有）のどれかで書く。作る・本文を書き換える・消す（自分の投稿だけ）\n- **コメント**: 投稿へのプレーンテキスト。書く・書き換える・消す（自分のコメントだけ）\n- **いいね**: 投稿に付ける・外す。何度呼んでも同じ結果になります\n- **プロフィール**: 表示名・ハンドル・自己紹介・画像を書き換える（自分のものだけ）\n\n- 認証は**アカウントごとに発行するユーザーAPIトークン**で行います（`Authorization: Bearer <トークン>`）。\n  トークンは Framed! の `https://framed.dog/account/api-tokens` で自分で発行します。\n- **トークンで出来ることは、あなたが画面から出来ることの部分集合です。** 他人の投稿やコメントは書き換えられません。\n- 本文は JSON、エラーは RFC 9457（`application/problem+json`）で返します。\n- 呼び出しはトークンごとに 60 回/分までです。\n- **トークンには触れる範囲が付きます。** まず `GET /me` を呼んで、そのトークンで何ができるかを確かめてください。\n- **トークンは秘密情報で、あなたのアカウントそのものです。** サーバからだけ呼び出してください。ブラウザからの呼び出し（CORS）には対応していません。\n\nスペースのサイト（フリーページ・データストア・ストレージ）を扱う API は別にあります\n（スペースごとの API キーで認証します）: https://framed.dog/developers\n\n同じ操作を MCP サーバ（https://api.framed.dog/user-mcp）からも使えます。認証は同じトークンです。\n\n開発者向けドキュメント: https://framed.dog/developers/user-api","termsOfService":"https://framed.dog/terms","contact":{"name":"Eggplanet","email":"admin@eggplanet.com"}},"servers":[{"url":"https://api.framed.dog/v1","description":"Framed! User API v1"}],"security":[{"userApiToken":[]}],"tags":[{"name":"Account","description":"トークンの持ち主と、このトークンで出来ること。プロフィールの書き換え"},{"name":"Posts","description":"投稿。本文は HTML・Markdown・リブログのどれかで書く"},{"name":"Comments","description":"投稿へのコメント。プレーンテキスト"},{"name":"Likes","description":"投稿へのいいね"}],"paths":{"/me":{"parameters":[],"get":{"operationId":"getMe","tags":["Account"],"summary":"トークンの持ち主と、このトークンで出来ることを確かめる","description":"ハンドル・表示名・画像・自己紹介に加えて、**このトークンに付いている範囲（`scopes`）**と、\n投稿・コメント・プロフィールの入力の規則、呼び出し回数の上限を返します。\n\n**最初にこれを呼んでください。** 範囲の足りない操作は `403 insufficient_scope` になるので、\n何ができるトークンなのかを先に知る必要があります。この操作だけは**範囲を問わず**答えます。\n\n規則の値は実装と同じ定義から組み立てているので、上限を変えてもここがずれることはありません。","responses":{"200":{"description":"トークンの持ち主と、このトークンで出来ること。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"},"example":{"handle":"tanaka_yu_7k2mp","display_name":"田中結衣","icon_url":"https://framed.dog/account-icons/tanaka_yu_7k2mp?v=1789000000000","bio":"散歩と川の写真。\nHTML で遊んでいます。","scopes":["posts","comments","profile"],"rules":{"post":{"formats":["html","markdown","reblog"],"html_max_length":262144,"markdown_max_bytes":262144,"reblog_url_max_length":2048,"reblog_text_max_length":200,"tag_max_count":5,"tag_max_length":30},"comment":{"body_max_length":500,"page_size":100},"profile":{"handle_min_length":3,"handle_max_length":15,"handle_pattern":"^[A-Za-z0-9._]{3,15}$","display_name_max_length":30,"bio_max_length":1000,"icon_size":256,"icon_max_bytes":1048576}},"rate_limit":{"requests_per_minute":60}}}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}},"patch":{"operationId":"updateMe","tags":["Account"],"summary":"表示名・ハンドル・自己紹介を書き換える","description":"**送った項目だけ**を書き換えます。範囲 `profile` が要ります。応答は書き換えたあとの `GET /me` と同じです。\n規則はアカウントの画面と同じで、上限は `GET /me` の `rules.profile` にあります。\n\n- `display_name`: 表示名。日本語・空白・絵文字も使え、ほかの人と同じでもかまいません。\n  **`null` か空の文字列で外れ**、画面にはハンドルが出ます。\n- `bio`: 自己紹介。**プレーンテキスト**で、改行を使えます（HTML も Markdown も解釈しません）。`null` か空の文字列で外れます。\n- `handle`: ハンドル。英字・数字・`.`・`_` だけ。先頭の `@` は取り除きます。\n  **大文字・小文字を区別せずに一意**で、使われていれば `409 handle_taken` です。\n  **古いハンドルはすぐに空き、誰でも取れます。** 古いハンドルのプロフィールの URL は開けなくなり、転送もしません。\n\n規則を満たさない項目が 1 つでもあれば、**どの項目も書き換えません**（422 に全部の理由が並びます）。\nハンドルがぶつかったとき（409）も、表示名と自己紹介は書き換えません。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMeRequest"},"example":{"display_name":"田中結衣","bio":"散歩と川の写真。\nHTML で遊んでいます。"}}}},"responses":{"200":{"description":"書き換えました。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"},"example":{"handle":"tanaka_yu_7k2mp","display_name":"田中結衣","icon_url":"https://framed.dog/account-icons/tanaka_yu_7k2mp?v=1789000000000","bio":"散歩と川の写真。\nHTML で遊んでいます。","scopes":["posts","comments","profile"],"rules":{"post":{"formats":["html","markdown","reblog"],"html_max_length":262144,"markdown_max_bytes":262144,"reblog_url_max_length":2048,"reblog_text_max_length":200,"tag_max_count":5,"tag_max_length":30},"comment":{"body_max_length":500,"page_size":100},"profile":{"handle_min_length":3,"handle_max_length":15,"handle_pattern":"^[A-Za-z0-9._]{3,15}$","display_name_max_length":30,"bio_max_length":1000,"icon_size":256,"icon_max_bytes":1048576}},"rate_limit":{"requests_per_minute":60}}}}},"400":{"description":"リクエスト本文を JSON として読めません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-invalid_json","title":"リクエスト本文を JSON として読めません","status":400,"code":"invalid_json","detail":"Unexpected end of JSON input"}}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"409":{"description":"そのハンドルはすでに使われています","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-handle_taken","title":"そのハンドルはすでに使われています","status":409,"code":"handle_taken","detail":"ハンドル tanaka_yu_7k2mp はすでに使われています"}}}},"413":{"description":"リクエスト本文が大きすぎます","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-payload_too_large","title":"リクエスト本文が大きすぎます","status":413,"code":"payload_too_large","detail":"リクエスト本文は 1048576 バイトまでです"}}}},"415":{"description":"Content-Type は application/json にしてください","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unsupported_media_type","title":"Content-Type は application/json にしてください","status":415,"code":"unsupported_media_type","detail":"Content-Type: text/plain は受け付けません"}}}},"422":{"description":"入力が規則を満たしていません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-validation_failed","title":"入力が規則を満たしていません","status":422,"code":"validation_failed","detail":"入力を 1 件直してください","errors":[{"field":"handle","message":"ハンドルに使えるのは英字・数字・. _ の 3〜15 文字です"}]}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}}},"/me/icon":{"parameters":[],"put":{"operationId":"setIcon","tags":["Account"],"summary":"画像を設定する","description":"アカウントの画像を設定します（あれば置き換えます）。範囲 `profile` が要ります。\n\n- **256×256 の PNG だけ**を、base64 にして `png` に入れて送ります（1048576 バイトまで）。\n  切り抜きや縮小はしません。正方形に切り抜いて縮めてから送ってください。\n- サーバは中身を信用せず、画素だけを使って PNG を作り直して保存します（メタデータは残りません）。\n- 画像の URL は設定するたびに変わります。応答の `icon_url` を使ってください。\n- 本文の上限は、このエンドポイントだけ 1399128 バイトです。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetIconRequest"},"example":{"png":"iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAA…"}}}},"responses":{"200":{"description":"設定しました。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"},"example":{"handle":"tanaka_yu_7k2mp","display_name":"田中結衣","icon_url":"https://framed.dog/account-icons/tanaka_yu_7k2mp?v=1789000000000","bio":"散歩と川の写真。\nHTML で遊んでいます。","scopes":["posts","comments","profile"],"rules":{"post":{"formats":["html","markdown","reblog"],"html_max_length":262144,"markdown_max_bytes":262144,"reblog_url_max_length":2048,"reblog_text_max_length":200,"tag_max_count":5,"tag_max_length":30},"comment":{"body_max_length":500,"page_size":100},"profile":{"handle_min_length":3,"handle_max_length":15,"handle_pattern":"^[A-Za-z0-9._]{3,15}$","display_name_max_length":30,"bio_max_length":1000,"icon_size":256,"icon_max_bytes":1048576}},"rate_limit":{"requests_per_minute":60}}}}},"400":{"description":"リクエスト本文を JSON として読めません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-invalid_json","title":"リクエスト本文を JSON として読めません","status":400,"code":"invalid_json","detail":"Unexpected end of JSON input"}}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"413":{"description":"リクエスト本文が大きすぎます","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-payload_too_large","title":"リクエスト本文が大きすぎます","status":413,"code":"payload_too_large","detail":"リクエスト本文は 1399128 バイトまでです"}}}},"415":{"description":"Content-Type は application/json にしてください","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unsupported_media_type","title":"Content-Type は application/json にしてください","status":415,"code":"unsupported_media_type","detail":"Content-Type: text/plain は受け付けません"}}}},"422":{"description":"入力が規則を満たしていません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-validation_failed","title":"入力が規則を満たしていません","status":422,"code":"validation_failed","detail":"入力を 1 件直してください","errors":[{"field":"png","message":"画像の大きさが違います（256×256 の PNG、1048576 バイトまで）"}]}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}},"delete":{"operationId":"removeIcon","tags":["Account"],"summary":"画像を外す","description":"アカウントの画像を外します。範囲 `profile` が要ります。\n画面には名前の先頭の 1 文字が出ます。**設定していなくても成功します。**","responses":{"204":{"description":"外しました（本文なし）。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}}},"/posts":{"parameters":[],"get":{"operationId":"listMyPosts","tags":["Posts"],"summary":"自分の投稿の一覧","description":"**自分の**投稿を新しい順に返します。本文（HTML）は含みません。\n画面で作りかけの下書きも含み、`published` で見分けられます。\n\nほかの人の投稿の一覧（タイムライン）は、この API では読めません。","responses":{"200":{"description":"自分の投稿の一覧。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostList"},"example":{"data":[{"id":"v7n3k9q2m5x8b4p6","url":"https://v7n3k9q2m5x8b4p6.usercontents.frmd.spot","author_handle":"tanaka_yu_7k2mp","format":"html","tags":["demo"],"published":true,"published_at":"2026-09-20T09:00:00.000Z","created_at":"2026-09-20T09:00:00.000Z","updated_at":"2026-09-20T09:00:00.000Z","like_count":3,"comment_count":1}]}}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}},"post":{"operationId":"createPost","tags":["Posts"],"summary":"投稿する","description":"本文を送ると投稿になり、**すぐタイムラインに出ます。** 書き方は `format` で選びます。\n\n- `html`（省略したときもこれ）: HTML をそのまま。**サーバは書き換えません**（サニタイズしません）。\n  CSS と JavaScript も、配信オリジンのフレームの中で動きます。\n- `markdown`: Markdown。画面の「文章」と同じ形に移して保存します。スクリプトは動きません。\n- `reblog`: 共有する URL と、添える短い文。画面にはリンクカードが出ます。\n\n- `tags` でタグを付けられます（あとから付け外しはできません）。\n- 応答の `url` が配信オリジンです。\n- **画面の下書きには触りません。** 画面で書きかけのものは、この呼び出しで消えません。\n- 宛先は本体のタイムラインだけです（スペース宛ての投稿はまだ API から作れません）。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostRequest"},"example":{"format":"markdown","markdown":"## 今日の散歩\n\n川沿いを **3km** 歩いた。\n\n- 桜はまだ\n- 鴨がいた","tags":["散歩"]}}}},"responses":{"201":{"description":"投稿しました。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Post"},"example":{"id":"v7n3k9q2m5x8b4p6","url":"https://v7n3k9q2m5x8b4p6.usercontents.frmd.spot","author_handle":"tanaka_yu_7k2mp","format":"html","tags":["demo"],"published":true,"published_at":"2026-09-20T09:00:00.000Z","created_at":"2026-09-20T09:00:00.000Z","updated_at":"2026-09-20T09:00:00.000Z","like_count":3,"comment_count":1}}}},"400":{"description":"リクエスト本文を JSON として読めません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-invalid_json","title":"リクエスト本文を JSON として読めません","status":400,"code":"invalid_json","detail":"Unexpected end of JSON input"}}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"413":{"description":"リクエスト本文が大きすぎます","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-payload_too_large","title":"リクエスト本文が大きすぎます","status":413,"code":"payload_too_large","detail":"リクエスト本文は 1048576 バイトまでです"}}}},"415":{"description":"Content-Type は application/json にしてください","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unsupported_media_type","title":"Content-Type は application/json にしてください","status":415,"code":"unsupported_media_type","detail":"Content-Type: text/plain は受け付けません"}}}},"422":{"description":"入力が規則を満たしていません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-validation_failed","title":"入力が規則を満たしていません","status":422,"code":"validation_failed","detail":"入力を 1 件直してください","errors":[{"field":"html","message":"中身が空です"}]}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}}},"/posts/{post_id}":{"parameters":[{"$ref":"#/components/parameters/PostId"}],"get":{"operationId":"getPost","tags":["Posts"],"summary":"投稿を本文ごと取得する","description":"投稿を 1 件、本文（配信している HTML）といいね・コメントの数まで返します。\nリブログなら `reblog` に URL と文が入ります。\n**ほかの人の公開済みの投稿も読めます**（タイムラインに並ぶものなので）。\nただし**下書きは本人だけ**です。ほかの人の下書きを指すと 404 になります。","responses":{"200":{"description":"投稿の本文と数。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostDetail"},"example":{"id":"v7n3k9q2m5x8b4p6","url":"https://v7n3k9q2m5x8b4p6.usercontents.frmd.spot","author_handle":"tanaka_yu_7k2mp","format":"html","tags":["demo"],"published":true,"published_at":"2026-09-20T09:00:00.000Z","created_at":"2026-09-20T09:00:00.000Z","updated_at":"2026-09-20T09:00:00.000Z","like_count":3,"comment_count":1,"html":"<h1>こんにちは</h1>\n<style>h1 { font-family: system-ui; }</style>\n<script>document.querySelector(\"h1\").onclick = (e) => { e.target.textContent = \"やあ\" }</script>","reblog":null}}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"404":{"description":"その投稿がない、ほかの人の下書きである、または**書き換え・削除でほかの人の投稿を指した**（`post_not_found`）。ID の存在を他人に伝えないため、権限が無い場合も 404 です。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-post_not_found","title":"その投稿はありません","status":404,"code":"post_not_found","detail":"投稿 v7n3k9q2m5x8b4p6 はありません"}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}},"patch":{"operationId":"updatePost","tags":["Posts"],"summary":"投稿の本文を書き換える","description":"**自分の投稿の本文だけ**を、**丸ごと**書き換えます。ほかの人の投稿を指すと 404 です。\n本文の送り方は作成と同じ（`format` と、その書き方の項目）です。\n\n- **ID と配信オリジン（`url`）は変わりません。** 書き換えは次に開いた人にすぐ届きます。\n- **書き方は元と違ってもかまいません**（Markdown で書いた投稿を HTML で書き換える、など）。\n- タグと公開の状態は変わりません（下書きは下書きのまま）。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePostRequest"},"example":{"format":"reblog","url":"https://example.com/articles/42","text":"これよかった"}}}},"responses":{"200":{"description":"書き換えました。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Post"},"example":{"id":"v7n3k9q2m5x8b4p6","url":"https://v7n3k9q2m5x8b4p6.usercontents.frmd.spot","author_handle":"tanaka_yu_7k2mp","format":"html","tags":["demo"],"published":true,"published_at":"2026-09-20T09:00:00.000Z","created_at":"2026-09-20T09:00:00.000Z","updated_at":"2026-09-20T09:00:00.000Z","like_count":3,"comment_count":1}}}},"400":{"description":"リクエスト本文を JSON として読めません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-invalid_json","title":"リクエスト本文を JSON として読めません","status":400,"code":"invalid_json","detail":"Unexpected end of JSON input"}}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"404":{"description":"その投稿がない、ほかの人の下書きである、または**書き換え・削除でほかの人の投稿を指した**（`post_not_found`）。ID の存在を他人に伝えないため、権限が無い場合も 404 です。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-post_not_found","title":"その投稿はありません","status":404,"code":"post_not_found","detail":"投稿 v7n3k9q2m5x8b4p6 はありません"}}}},"413":{"description":"リクエスト本文が大きすぎます","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-payload_too_large","title":"リクエスト本文が大きすぎます","status":413,"code":"payload_too_large","detail":"リクエスト本文は 1048576 バイトまでです"}}}},"415":{"description":"Content-Type は application/json にしてください","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unsupported_media_type","title":"Content-Type は application/json にしてください","status":415,"code":"unsupported_media_type","detail":"Content-Type: text/plain は受け付けません"}}}},"422":{"description":"入力が規則を満たしていません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-validation_failed","title":"入力が規則を満たしていません","status":422,"code":"validation_failed","detail":"入力を 1 件直してください","errors":[{"field":"html","message":"中身が空です"}]}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}},"delete":{"operationId":"deletePost","tags":["Posts"],"summary":"投稿を消す","description":"**自分の投稿だけ**を消します。ほかの人の投稿を指すと 404 です。\n\n**元に戻せません。** 配信オリジンはすぐ 404 になり、その投稿へのいいね・コメント・\n通報も一緒に消えます。","responses":{"204":{"description":"削除しました（本文なし）。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"404":{"description":"その投稿がない、ほかの人の下書きである、または**書き換え・削除でほかの人の投稿を指した**（`post_not_found`）。ID の存在を他人に伝えないため、権限が無い場合も 404 です。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-post_not_found","title":"その投稿はありません","status":404,"code":"post_not_found","detail":"投稿 v7n3k9q2m5x8b4p6 はありません"}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}}},"/posts/{post_id}/comments":{"parameters":[{"$ref":"#/components/parameters/PostId"}],"get":{"operationId":"listPostComments","tags":["Comments"],"summary":"投稿へのコメントの一覧","description":"その投稿のコメントを**古い順**に返します（会話として読む順なので、新しい順にはしません）。\n一度に 100 件までです。","responses":{"200":{"description":"コメントの一覧。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentList"},"example":{"data":[{"id":"q2m5x8b4p6v7n3k9","post_id":"v7n3k9q2m5x8b4p6","author_handle":"tanaka_yu_7k2mp","body":"これ、クリックすると変わるんですね","created_at":"2026-09-20T09:30:00.000Z","updated_at":"2026-09-20T09:30:00.000Z"}]}}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"404":{"description":"その投稿がない、ほかの人の下書きである、または**書き換え・削除でほかの人の投稿を指した**（`post_not_found`）。ID の存在を他人に伝えないため、権限が無い場合も 404 です。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-post_not_found","title":"その投稿はありません","status":404,"code":"post_not_found","detail":"投稿 v7n3k9q2m5x8b4p6 はありません"}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}},"post":{"operationId":"createComment","tags":["Comments"],"summary":"コメントする","description":"その投稿にコメントします。**本文はプレーンテキスト**で、HTML として解釈されません。\n同じ投稿に何件でも書けます（1 人 1 件の制限はありません）。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentRequest"},"example":{"body":"これ、クリックすると変わるんですね"}}}},"responses":{"201":{"description":"コメントしました。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Comment"},"example":{"id":"q2m5x8b4p6v7n3k9","post_id":"v7n3k9q2m5x8b4p6","author_handle":"tanaka_yu_7k2mp","body":"これ、クリックすると変わるんですね","created_at":"2026-09-20T09:30:00.000Z","updated_at":"2026-09-20T09:30:00.000Z"}}}},"400":{"description":"リクエスト本文を JSON として読めません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-invalid_json","title":"リクエスト本文を JSON として読めません","status":400,"code":"invalid_json","detail":"Unexpected end of JSON input"}}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"404":{"description":"その投稿がない、ほかの人の下書きである、または**書き換え・削除でほかの人の投稿を指した**（`post_not_found`）。ID の存在を他人に伝えないため、権限が無い場合も 404 です。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-post_not_found","title":"その投稿はありません","status":404,"code":"post_not_found","detail":"投稿 v7n3k9q2m5x8b4p6 はありません"}}}},"413":{"description":"リクエスト本文が大きすぎます","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-payload_too_large","title":"リクエスト本文が大きすぎます","status":413,"code":"payload_too_large","detail":"リクエスト本文は 1048576 バイトまでです"}}}},"415":{"description":"Content-Type は application/json にしてください","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unsupported_media_type","title":"Content-Type は application/json にしてください","status":415,"code":"unsupported_media_type","detail":"Content-Type: text/plain は受け付けません"}}}},"422":{"description":"入力が規則を満たしていません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-validation_failed","title":"入力が規則を満たしていません","status":422,"code":"validation_failed","detail":"入力を 1 件直してください","errors":[{"field":"body","message":"コメントは 500 文字までです"}]}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}}},"/posts/{post_id}/like":{"parameters":[{"$ref":"#/components/parameters/PostId"}],"put":{"operationId":"addLike","tags":["Likes"],"summary":"いいねを付ける","description":"その投稿にいいねを付けます。**何度呼んでも同じ結果**で、すでに付いていても数は増えません。\n呼び出しが失敗して再試行しても、数が狂うことはありません。\n\n本文は要りません（送っても無視します）。","responses":{"200":{"description":"いまのいいねの状態。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Like"},"example":{"post_id":"v7n3k9q2m5x8b4p6","liked":true,"like_count":4}}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"404":{"description":"その投稿がない、ほかの人の下書きである、または**書き換え・削除でほかの人の投稿を指した**（`post_not_found`）。ID の存在を他人に伝えないため、権限が無い場合も 404 です。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-post_not_found","title":"その投稿はありません","status":404,"code":"post_not_found","detail":"投稿 v7n3k9q2m5x8b4p6 はありません"}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}},"delete":{"operationId":"removeLike","tags":["Likes"],"summary":"いいねを外す","description":"その投稿のいいねを外します。**付いていなくても成功します**（`PUT` と同じく冪等です）。","responses":{"204":{"description":"外しました（本文なし）。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"404":{"description":"その投稿がない、ほかの人の下書きである、または**書き換え・削除でほかの人の投稿を指した**（`post_not_found`）。ID の存在を他人に伝えないため、権限が無い場合も 404 です。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-post_not_found","title":"その投稿はありません","status":404,"code":"post_not_found","detail":"投稿 v7n3k9q2m5x8b4p6 はありません"}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}}},"/comments/{comment_id}":{"parameters":[{"$ref":"#/components/parameters/CommentId"}],"patch":{"operationId":"updateComment","tags":["Comments"],"summary":"コメントを書き換える","description":"**自分のコメントだけ**を書き換えます。ほかの人のコメントを指すと 404 です。投稿は変えられません。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentRequest"},"example":{"body":"これ、クリックすると変わるんですね（追記: すごい）"}}}},"responses":{"200":{"description":"書き換えました。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Comment"},"example":{"id":"q2m5x8b4p6v7n3k9","post_id":"v7n3k9q2m5x8b4p6","author_handle":"tanaka_yu_7k2mp","body":"これ、クリックすると変わるんですね","created_at":"2026-09-20T09:30:00.000Z","updated_at":"2026-09-20T09:30:00.000Z"}}}},"400":{"description":"リクエスト本文を JSON として読めません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-invalid_json","title":"リクエスト本文を JSON として読めません","status":400,"code":"invalid_json","detail":"Unexpected end of JSON input"}}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"404":{"description":"そのコメントがない、または**ほかの人のコメントを指した**（`comment_not_found`）。投稿と同じ理由で 404 です。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-comment_not_found","title":"そのコメントはありません","status":404,"code":"comment_not_found","detail":"コメント q2m5x8b4p6v7n3k9 はありません"}}}},"413":{"description":"リクエスト本文が大きすぎます","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-payload_too_large","title":"リクエスト本文が大きすぎます","status":413,"code":"payload_too_large","detail":"リクエスト本文は 1048576 バイトまでです"}}}},"415":{"description":"Content-Type は application/json にしてください","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unsupported_media_type","title":"Content-Type は application/json にしてください","status":415,"code":"unsupported_media_type","detail":"Content-Type: text/plain は受け付けません"}}}},"422":{"description":"入力が規則を満たしていません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-validation_failed","title":"入力が規則を満たしていません","status":422,"code":"validation_failed","detail":"入力を 1 件直してください","errors":[{"field":"body","message":"コメントは 500 文字までです"}]}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}},"delete":{"operationId":"deleteComment","tags":["Comments"],"summary":"コメントを消す","description":"**自分のコメントだけ**を消します。ほかの人のコメントを指すと 404 です。元に戻せません。","responses":{"204":{"description":"削除しました（本文なし）。","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"API キーが無いか、有効ではありません","headers":{"WWW-Authenticate":{"description":"認証の方式。常に `Bearer`。","schema":{"type":"string"}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-unauthorized","title":"API キーが無いか、有効ではありません","status":401,"code":"unauthorized","detail":"Authorization ヘッダに Bearer 形式でユーザーAPIトークンを入れてください"}}}},"403":{"description":"トークンにその操作の範囲が付いていない（`insufficient_scope`）。範囲は発行するときに決まり、あとから増やせません。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-insufficient_scope","title":"この資格情報には、その操作の範囲が付いていません","status":403,"code":"insufficient_scope","detail":"この操作には posts（投稿）の範囲が要ります。その範囲を付けたトークンを発行し直してください"}}}},"404":{"description":"そのコメントがない、または**ほかの人のコメントを指した**（`comment_not_found`）。投稿と同じ理由で 404 です。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-comment_not_found","title":"そのコメントはありません","status":404,"code":"comment_not_found","detail":"コメント q2m5x8b4p6v7n3k9 はありません"}}}},"429":{"description":"呼び出しの回数が上限を超えました","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-rate_limited","title":"呼び出しの回数が上限を超えました","status":429,"code":"rate_limited","detail":"このトークンでの呼び出しは 60 回/分までです。23 秒後にもう一度呼び出してください"}}}},"500":{"description":"サーバの内部で問題が起きました","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-internal_error","title":"サーバの内部で問題が起きました","status":500,"code":"internal_error","detail":"問い合わせるときは、この番号を添えてください: 3f9a1c2b7d4e"}}}}}}}},"components":{"securitySchemes":{"userApiToken":{"type":"http","scheme":"bearer","bearerFormat":"framed_ut_…","description":"アカウントごとに発行するユーザーAPIトークン。Framed! の `/account/api-tokens` で自分で発行します。\nトークンは発行したときに一度だけ表示されます。**あなたのアカウントそのもの**として扱ってください。\n\n**トークンには触れる範囲が付きます**（発行時に選び、あとから変えられません）。\n- `posts`: 自分の投稿を読み、投稿を作り、自分の投稿の本文を書き換え、削除します。ほかの人の投稿は読めますが書き換えられません。\n- `comments`: 投稿へのコメントを読み、コメントを書き、自分のコメントを書き換え、削除します。ほかの人のコメントは書き換えられません。\n- `likes`: 投稿にいいねを付け、外します。\n- `profile`: 自分の表示名・ハンドル・自己紹介・画像を書き換えます。ハンドルを変えると、古いハンドルのプロフィールの URL は開けなくなります。\n範囲の足りない操作を呼ぶと `403 insufficient_scope` を返します。\n\nスペースごとの API キー（`framed_sk_…`）とは別物で、こちらでは使えません（401 になります）。"}},"parameters":{"PostId":{"name":"post_id","in":"path","required":true,"description":"投稿の ID（作成・一覧の応答の `id`）。本文を書き換えても変わりません。","schema":{"type":"string","pattern":"^[2-9a-z]{16}$"},"example":"v7n3k9q2m5x8b4p6"},"CommentId":{"name":"comment_id","in":"path","required":true,"description":"コメントの ID（作成・一覧の応答の `id`）。","schema":{"type":"string","pattern":"^[2-9a-z]{16}$"},"example":"q2m5x8b4p6v7n3k9"}},"headers":{"RateLimitLimit":{"description":"このトークンで 1 分間に呼び出せる回数（60）。","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"いまの 1 分間に、あと何回呼び出せるか。","schema":{"type":"integer"}},"RateLimitReset":{"description":"回数が数え直されるまでの秒数。","schema":{"type":"integer"}},"RetryAfter":{"description":"もう一度呼び出してよくなるまでの秒数。","schema":{"type":"integer"}}},"schemas":{"Me":{"type":"object","additionalProperties":false,"required":["handle","display_name","icon_url","bio","scopes","rules","rate_limit"],"properties":{"handle":{"type":"string","description":"あなたのハンドル（`user.name`）。投稿やコメントの表示に使われます。","examples":["tanaka_yu_7k2mp"]},"display_name":{"type":["string","null"],"description":"表示名。設定していなければ `null`（画面にはハンドルが出ます）。"},"icon_url":{"type":["string","null"],"format":"uri","description":"アカウントの画像の URL。設定していなければ `null`。"},"bio":{"type":["string","null"],"description":"自己紹介（プレーンテキスト。改行は `\\n`）。設定していなければ `null`。"},"scopes":{"type":"array","description":"**このトークンに付いている範囲。** 足りない操作は 403 になります。","items":{"type":"string","enum":["posts","comments","likes","profile"]},"examples":[["posts","comments"]]},"rules":{"type":"object","description":"入力の規則。画面と同じ定義から組み立てています。","additionalProperties":false,"required":["post","comment","profile"],"properties":{"post":{"type":"object","additionalProperties":false,"required":["formats","html_max_length","markdown_max_bytes","reblog_url_max_length","reblog_text_max_length","tag_max_count","tag_max_length"],"properties":{"formats":{"type":"array","description":"投稿の `format` に使える書き方。","items":{"type":"string","enum":["html","markdown","reblog"]}},"html_max_length":{"type":"integer","description":"HTML の本文の上限（文字数）。Markdown から組み立てた HTML にもかかります。"},"markdown_max_bytes":{"type":"integer","description":"Markdown の本文の上限（UTF-8 のバイト数）。"},"reblog_url_max_length":{"type":"integer","description":"リブログの URL の上限（文字数）。"},"reblog_text_max_length":{"type":"integer","description":"リブログに添える文の上限（コードポイントの数）。"},"tag_max_count":{"type":"integer","description":"1 投稿に付けられるタグの数。"},"tag_max_length":{"type":"integer","description":"タグ 1 つの上限（文字数）。"}}},"comment":{"type":"object","additionalProperties":false,"required":["body_max_length","page_size"],"properties":{"body_max_length":{"type":"integer","description":"コメント 1 件の上限（文字数）。"},"page_size":{"type":"integer","description":"コメントの一覧が一度に返す件数の上限。"}}},"profile":{"type":"object","additionalProperties":false,"required":["handle_min_length","handle_max_length","handle_pattern","display_name_max_length","bio_max_length","icon_size","icon_max_bytes"],"properties":{"handle_min_length":{"type":"integer","description":"ハンドルの下限（文字数）。"},"handle_max_length":{"type":"integer","description":"ハンドルの上限（文字数）。"},"handle_pattern":{"type":"string","description":"ハンドルが満たす正規表現（先頭の `@` と前後の空白を取り除いたあと）。"},"display_name_max_length":{"type":"integer","description":"表示名の上限（コードポイントの数）。"},"bio_max_length":{"type":"integer","description":"自己紹介の上限（コードポイントの数。改行も 1 文字）。"},"icon_size":{"type":"integer","description":"画像の一辺（px）。正方形だけ。"},"icon_max_bytes":{"type":"integer","description":"画像の PNG の上限（バイト）。"}}}}},"rate_limit":{"type":"object","additionalProperties":false,"required":["requests_per_minute"],"properties":{"requests_per_minute":{"type":"integer","description":"このトークンで 1 分間に呼び出せる回数。"}}}}},"UpdateMeRequest":{"type":"object","description":"書き換える項目だけを送ります。1 つは要ります。","additionalProperties":false,"minProperties":1,"properties":{"handle":{"type":"string","description":"ハンドル。英字・数字・`.`・`_` の 3〜15 文字。\n前後の空白と先頭の `@` は取り除きます。大文字・小文字を区別せずに一意です。外すことはできません。","examples":["tanaka_yu"]},"display_name":{"type":["string","null"],"description":"表示名。前後の空白を落として 30 文字まで（コードポイントで数えます）。\n改行・制御文字・文字の向きを変える制御は使えません。`null` か空の文字列で外れます。"},"bio":{"type":["string","null"],"description":"自己紹介（プレーンテキスト）。前後の空白を落として 1000 文字まで（コードポイントで数え、改行も 1 文字）。\n改行は使えますが、タブなどほかの制御文字と、文字の向きを変える制御は使えません。`null` か空の文字列で外れます。"}}},"SetIconRequest":{"type":"object","additionalProperties":false,"required":["png"],"properties":{"png":{"type":"string","description":"256×256 の PNG を base64 にしたもの（改行は無視します）。","minLength":1}}},"Post":{"type":"object","additionalProperties":false,"required":["id","url","author_handle","format","tags","published","published_at","created_at","updated_at","like_count","comment_count"],"properties":{"id":{"type":"string","description":"投稿の ID。本文を書き換えても変わりません。","pattern":"^[2-9a-z]{16}$","examples":["v7n3k9q2m5x8b4p6"]},"url":{"type":"string","format":"uri","description":"投稿の HTML が配信される URL。**投稿ごとに 1 つのオリジン**です。\nタイムラインはこの URL をフレームで読み込みます。","examples":["https://v7n3k9q2m5x8b4p6.usercontents.frmd.spot"]},"author_handle":{"type":"string","description":"投稿した人のハンドル。","examples":["tanaka_yu_7k2mp"]},"format":{"type":"string","enum":["html","rich","reblog"],"description":"いまの本文の書き方。\n- `html`: HTML で書いた投稿。配信オリジンのフレームの中で、スクリプトも含めて動く\n- `rich`: 画面の「文章」か、Markdown で書いた投稿（どちらだったかは残りません）。画面にはフレームを使わずに出る\n- `reblog`: URL を共有した投稿（`reblog` に URL と文）。画面には文とリンクカードが出る"},"tags":{"type":"array","description":"付いているタグ（名前の順）。付いていなければ空の配列。","items":{"type":"string"}},"published":{"type":"boolean","description":"公開されているか。`false` は画面で作りかけの下書きです（→ `published_at`）。"},"published_at":{"type":["string","null"],"format":"date-time","description":"公開した日時（UTC、ISO 8601）。下書きなら `null`。"},"created_at":{"type":"string","format":"date-time","description":"作った日時（UTC、ISO 8601）。"},"updated_at":{"type":"string","format":"date-time","description":"最後に書き換えた日時（UTC、ISO 8601）。作っただけなら作った日時。"},"like_count":{"type":"integer","description":"いいねの数。"},"comment_count":{"type":"integer","description":"コメントの数。"}}},"PostDetail":{"type":"object","additionalProperties":false,"required":["id","url","author_handle","format","tags","published","published_at","created_at","updated_at","like_count","comment_count","html","reblog"],"properties":{"id":{"type":"string","description":"投稿の ID。本文を書き換えても変わりません。","pattern":"^[2-9a-z]{16}$","examples":["v7n3k9q2m5x8b4p6"]},"url":{"type":"string","format":"uri","description":"投稿の HTML が配信される URL。**投稿ごとに 1 つのオリジン**です。\nタイムラインはこの URL をフレームで読み込みます。","examples":["https://v7n3k9q2m5x8b4p6.usercontents.frmd.spot"]},"author_handle":{"type":"string","description":"投稿した人のハンドル。","examples":["tanaka_yu_7k2mp"]},"format":{"type":"string","enum":["html","rich","reblog"],"description":"いまの本文の書き方。\n- `html`: HTML で書いた投稿。配信オリジンのフレームの中で、スクリプトも含めて動く\n- `rich`: 画面の「文章」か、Markdown で書いた投稿（どちらだったかは残りません）。画面にはフレームを使わずに出る\n- `reblog`: URL を共有した投稿（`reblog` に URL と文）。画面には文とリンクカードが出る"},"tags":{"type":"array","description":"付いているタグ（名前の順）。付いていなければ空の配列。","items":{"type":"string"}},"published":{"type":"boolean","description":"公開されているか。`false` は画面で作りかけの下書きです（→ `published_at`）。"},"published_at":{"type":["string","null"],"format":"date-time","description":"公開した日時（UTC、ISO 8601）。下書きなら `null`。"},"created_at":{"type":"string","format":"date-time","description":"作った日時（UTC、ISO 8601）。"},"updated_at":{"type":"string","format":"date-time","description":"最後に書き換えた日時（UTC、ISO 8601）。作っただけなら作った日時。"},"like_count":{"type":"integer","description":"いいねの数。"},"comment_count":{"type":"integer","description":"コメントの数。"},"html":{"type":"string","description":"**配信している HTML 文書そのもの**（`url` が返すもの）。\n`format` が `html` なら送った HTML のまま。`rich` と `reblog` は、サーバが組み立てた文書です。"},"reblog":{"type":["object","null"],"description":"リブログの URL と文。`format` が `reblog` でなければ `null`。","additionalProperties":false,"required":["url","text"],"properties":{"url":{"type":"string","format":"uri","description":"共有した URL。"},"text":{"type":"string","description":"添えた文（プレーンテキスト）。空のこともあります。"}}}}},"PostList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"自分の投稿。新しい順。","items":{"$ref":"#/components/schemas/Post"}}}},"CreatePostRequest":{"description":"`format` で書き方を選び、その書き方の項目を送ります。`format` を省くと `html`。","oneOf":[{"title":"HTML","type":"object","additionalProperties":false,"required":["html"],"properties":{"format":{"const":"html","description":"HTML で書く。**省略したときもこれ**（1.0.0 の呼び出しはそのまま通ります）。"},"html":{"type":"string","description":"投稿の中身。**HTML そのままで、サーバは書き換えません**（サニタイズしません）。\nCSS と JavaScript を含められます。Framed! 本体とは別のドメイン（`<post_id>.usercontents.frmd.spot`）の\nフレームの中で動くので、閲覧者のサインイン状態やほかの投稿には触れられません。\n前後の空白を取り除いたうえで、空でなく 262144 文字以内であること。","minLength":1,"maxLength":262144},"tags":{"type":"array","description":"投稿に付けるタグ。5 個まで。同じタグのフィードに自動で並びます。\n1 つ 30 文字までで、空白・「#」・「,」を含められません（先頭の「#」は取り除きます）。\n保存するときに小文字・NFKC にそろえ、重複はまとめます。**成り立たないタグがあると 422 です**（黙って捨てません）。\n**あとから付け外しはできません。**","maxItems":5,"items":{"type":"string"},"examples":[["散歩","diary"]]}}},{"title":"Markdown","type":"object","additionalProperties":false,"required":["format","markdown"],"properties":{"format":{"const":"markdown","description":"Markdown で書く。"},"markdown":{"type":"string","description":"投稿の中身を Markdown（GFM）で。**保存のときに、画面の「文章」と同じ形に移します**\n（見出し・太字・斜体・打ち消し・コード・リンク・箇条書き・番号付き・引用・コードブロック・区切り線・画像）。\n\n- **Markdown の中の HTML はタグにならず、書いたとおりの文字として出ます。** スクリプトは動きません\n- 表は等幅の文字として、チェックボックスは `☐` / `☑` として残ります\n- **元の Markdown は保存しません。** あとで読むと `format` は `rich` で、`html` は組み立てた文書です\n\nUTF-8 で 262144 バイトまで。文字も画像も無い本文は受け付けません。","minLength":1},"tags":{"type":"array","description":"投稿に付けるタグ。5 個まで。同じタグのフィードに自動で並びます。\n1 つ 30 文字までで、空白・「#」・「,」を含められません（先頭の「#」は取り除きます）。\n保存するときに小文字・NFKC にそろえ、重複はまとめます。**成り立たないタグがあると 422 です**（黙って捨てません）。\n**あとから付け外しはできません。**","maxItems":5,"items":{"type":"string"},"examples":[["散歩","diary"]]}}},{"title":"リブログ","type":"object","additionalProperties":false,"required":["format","url"],"properties":{"format":{"const":"reblog","description":"URL を共有する（リブログ）。"},"url":{"type":"string","description":"共有する URL。`http:` か `https:` だけで、ユーザー名やパスワードを含むものは受け付けません。\n2048 文字まで。画面ではこの URL の Open Graph からリンクカードを作って出します。","minLength":1,"maxLength":2048,"examples":["https://example.com/articles/42"]},"text":{"type":"string","description":"URL に添える文。**プレーンテキスト**（HTML として解釈されません）で、200 文字まで\n（コードポイントで数えます）。改行は残し、前後の空白は落とします。省略すると空（URL だけを共有する）。","maxLength":200},"tags":{"type":"array","description":"投稿に付けるタグ。5 個まで。同じタグのフィードに自動で並びます。\n1 つ 30 文字までで、空白・「#」・「,」を含められません（先頭の「#」は取り除きます）。\n保存するときに小文字・NFKC にそろえ、重複はまとめます。**成り立たないタグがあると 422 です**（黙って捨てません）。\n**あとから付け外しはできません。**","maxItems":5,"items":{"type":"string"},"examples":[["散歩","diary"]]}}}]},"UpdatePostRequest":{"description":"本文を丸ごと置き換えます。送り方は作成と同じで、書き方は元と違ってもかまいません。\nタグは変えられません（`tags` を送ると 422）。","oneOf":[{"title":"HTML","type":"object","additionalProperties":false,"required":["html"],"properties":{"format":{"const":"html","description":"HTML で書く。**省略したときもこれ**（1.0.0 の呼び出しはそのまま通ります）。"},"html":{"type":"string","description":"投稿の中身。**HTML そのままで、サーバは書き換えません**（サニタイズしません）。\nCSS と JavaScript を含められます。Framed! 本体とは別のドメイン（`<post_id>.usercontents.frmd.spot`）の\nフレームの中で動くので、閲覧者のサインイン状態やほかの投稿には触れられません。\n前後の空白を取り除いたうえで、空でなく 262144 文字以内であること。","minLength":1,"maxLength":262144}}},{"title":"Markdown","type":"object","additionalProperties":false,"required":["format","markdown"],"properties":{"format":{"const":"markdown","description":"Markdown で書く。"},"markdown":{"type":"string","description":"投稿の中身を Markdown（GFM）で。**保存のときに、画面の「文章」と同じ形に移します**\n（見出し・太字・斜体・打ち消し・コード・リンク・箇条書き・番号付き・引用・コードブロック・区切り線・画像）。\n\n- **Markdown の中の HTML はタグにならず、書いたとおりの文字として出ます。** スクリプトは動きません\n- 表は等幅の文字として、チェックボックスは `☐` / `☑` として残ります\n- **元の Markdown は保存しません。** あとで読むと `format` は `rich` で、`html` は組み立てた文書です\n\nUTF-8 で 262144 バイトまで。文字も画像も無い本文は受け付けません。","minLength":1}}},{"title":"リブログ","type":"object","additionalProperties":false,"required":["format","url"],"properties":{"format":{"const":"reblog","description":"URL を共有する（リブログ）。"},"url":{"type":"string","description":"共有する URL。`http:` か `https:` だけで、ユーザー名やパスワードを含むものは受け付けません。\n2048 文字まで。画面ではこの URL の Open Graph からリンクカードを作って出します。","minLength":1,"maxLength":2048,"examples":["https://example.com/articles/42"]},"text":{"type":"string","description":"URL に添える文。**プレーンテキスト**（HTML として解釈されません）で、200 文字まで\n（コードポイントで数えます）。改行は残し、前後の空白は落とします。省略すると空（URL だけを共有する）。","maxLength":200}}}]},"Comment":{"type":"object","additionalProperties":false,"required":["id","post_id","author_handle","body","created_at","updated_at"],"properties":{"id":{"type":"string","description":"コメントの ID。","pattern":"^[2-9a-z]{16}$","examples":["q2m5x8b4p6v7n3k9"]},"post_id":{"type":"string","description":"コメントした先の投稿の ID。","pattern":"^[2-9a-z]{16}$","examples":["v7n3k9q2m5x8b4p6"]},"author_handle":{"type":"string","description":"コメントした人のハンドル。","examples":["tanaka_yu_7k2mp"]},"body":{"type":"string","description":"コメントの本文（プレーンテキスト）。"},"created_at":{"type":"string","format":"date-time","description":"書いた日時（UTC、ISO 8601）。"},"updated_at":{"type":"string","format":"date-time","description":"最後に書き換えた日時（UTC、ISO 8601）。書いただけなら書いた日時。"}}},"CommentList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"コメント。古い順。","items":{"$ref":"#/components/schemas/Comment"}}}},"CommentRequest":{"type":"object","additionalProperties":false,"required":["body"],"properties":{"body":{"type":"string","description":"コメントの本文。**プレーンテキストです**（HTML として解釈されません）。\n前後の空白を取り除いたうえで、空でなく 500 文字以内であること。\n改行は `\\n` にそろえ、改行とタブ以外の制御文字と双方向制御文字は受け付けません。","minLength":1,"maxLength":500}}},"Like":{"type":"object","additionalProperties":false,"required":["post_id","liked","like_count"],"properties":{"post_id":{"type":"string","description":"いいねを付けた投稿の ID。","pattern":"^[2-9a-z]{16}$","examples":["v7n3k9q2m5x8b4p6"]},"liked":{"type":"boolean","description":"いいねが付いているか。`PUT` の応答では常に真。"},"like_count":{"type":"integer","description":"その投稿のいいねの数。"}}},"Problem":{"type":"object","description":"エラー（RFC 9457 Problem Details）。機械的な分岐には `code` を使ってください。","required":["type","title","status","code"],"properties":{"type":{"type":"string","format":"uri","description":"エラーの種類を説明するドキュメントの URI。"},"title":{"type":"string","description":"エラーの種類ごとに固定の説明。"},"status":{"type":"integer","description":"HTTP ステータスコード。"},"detail":{"type":"string","description":"このリクエストに固有の説明。"},"code":{"type":"string","enum":["invalid_json","unauthorized","space_mismatch","insufficient_scope","not_found","page_not_found","part_not_found","file_not_found","data_not_found","post_not_found","comment_not_found","method_not_allowed","slug_taken","version_conflict","handle_taken","part_name_taken","parts_quota_exceeded","data_quota_exceeded","storage_quota_exceeded","payload_too_large","unsupported_media_type","validation_failed","rate_limited","internal_error"],"description":"エラーの種類を表す機械向けのコード。"},"errors":{"type":"array","description":"`validation_failed` のときだけ、通らなかった項目を並べます。","items":{"$ref":"#/components/schemas/ValidationError"}}}},"ValidationError":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string","description":"通らなかった項目。本文そのものが問題のときは `(body)`。","examples":["slug"]},"message":{"type":"string","description":"通らなかった理由。"}}}}}}