{"openapi":"3.1.0","info":{"title":"Framed! API","version":"1.6.0","summary":"スペースのサイトのフリーページ・パーツ・データ・ファイルを扱うための外部 API","description":"Framed! のスペースのサイトを、外部のプログラムやエージェントから作るための API です。扱えるのは 4 つです。\n\n- **フリーページ**: サイトの 1 枚。URL を持ちます。本文は Liquid のテンプレートとして評価されます\n- **パーツ**: ページから組み込む共通のテンプレート。単独では公開されません\n- **データストア**: ページから読める共有の JSON。見た目と中身を分けるためのものです\n- **ストレージ**: 画像などのファイル。URL を持ち、ページの HTML から参照します\n\n見た目は HTML、繰り返す枠はパーツ、変わる中身はデータストアに置き、ページからはテンプレートで読むと、あとで中身だけを直せます。\nテンプレートの書き方（引ける値・エスケープ・パーツの組み込み）は開発者向けドキュメントの「テンプレート」にあります。\n\n- 認証はスペースごとに発行する API キーで行います（`Authorization: Bearer <API キー>`）。\n- 本文は JSON、エラーは RFC 9457（`application/problem+json`）で返します。ファイルの中身も JSON の文字列（base64 か、文字のファイルならそのまま）で送ります。\n- 呼び出しは API キーごとに 60 回/分までです。\n- **キーには触れる範囲が付きます。** まず `GET /spaces/{subdomain}` を呼んで、そのキーで何ができるかを確かめてください。\n- **API キーは秘密情報です。** サーバからだけ呼び出してください。ブラウザからの呼び出し（CORS）には対応していません。\n\n開発者向けドキュメント: https://framed.dog/developers","termsOfService":"https://framed.dog/terms","contact":{"name":"Eggplanet","email":"admin@eggplanet.com"}},"servers":[{"url":"https://api.framed.dog/v1","description":"Framed! API v1"}],"security":[{"spaceApiKey":[]}],"tags":[{"name":"Space","description":"スペース自身の情報と、入力の規則"},{"name":"Pages","description":"スペースのサイトのフリーページ"},{"name":"Parts","description":"ページから組み込む共通のテンプレート。単独では公開されない"},{"name":"Data","description":"スペースのデータストア。フリーページのテンプレートから読める、共有の構造化データ"},{"name":"Files","description":"スペースのストレージのファイル。画像・フォント・CSS などを置き、ページから URL で参照する"}],"paths":{"/spaces/{subdomain}":{"parameters":[{"$ref":"#/components/parameters/Subdomain"}],"get":{"operationId":"getSpace","tags":["Space"],"summary":"スペースと、このキーでできることを確かめる","description":"スペースの名前・URL に加えて、**この API キーに付いている範囲（`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/Space"},"example":{"subdomain":"our-club","name":"わたしたちの部室","url":"https://our-club.framed.dog","icon_url":"https://our-club.framed.dog/space-icon.png?v=1789000000000","scopes":["pages","data"],"rules":{"page":{"slug":{"min_length":1,"max_length":60,"pattern":"^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$"},"kinds":["scripted","static"],"default_kind":"scripted","html_max_length":262144},"part":{"name":{"min_length":1,"max_length":60,"pattern":"^[a-z0-9](?:[a-z0-9_-]{0,58}[a-z0-9])?$"},"html_max_length":262144,"total_max_length":1048576},"data":{"key_pattern":"^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$","max_records":100,"value_max_bytes":65536,"total_max_bytes":1048576,"max_depth":32},"file":{"max_bytes":10485760,"quota_bytes":104857600,"name_max_length":255,"encodings":["base64","text"]}},"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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"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":"この API キーでの呼び出しは 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"}}}}}}},"/spaces/{subdomain}/pages":{"parameters":[{"$ref":"#/components/parameters/Subdomain"}],"get":{"operationId":"listSpacePages","tags":["Pages"],"summary":"フリーページの一覧","description":"スペースのフリーページを、スラグの順にすべて返します。本文（HTML）は含みません。","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/PageList"},"example":{"data":[{"id":"k3m9q2x7v4b8n6p5","slug":"about","kind":"scripted","url":"https://our-club.framed.dog/about","created_at":"2026-09-13T12:34:56.000Z","updated_at":"2026-09-13T12:34:56.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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"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":"この API キーでの呼び出しは 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":"createSpacePage","tags":["Pages"],"summary":"フリーページを作る","description":"スラグと HTML を指定して、スペースのサイトにフリーページを作ります。\n作ったページはすぐに `https://<subdomain>.framed.dog/<slug>` で公開されます。\n\nHTML はサーバで書き換えずにそのまま保存し、Framed! 本体とは別のドメイン（`<id>.pages.frmd.spot`）から配信します。\n同じスラグのページがすでにあると `409` を返します（既存のページは変わりません）。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePageRequest"},"example":{"slug":"about","kind":"scripted","html":"<h1>わたしたちについて</h1>\n<style>h1 { font-family: system-ui; }</style>"}}}},"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/Page"},"example":{"id":"k3m9q2x7v4b8n6p5","slug":"about","kind":"scripted","url":"https://our-club.framed.dog/about","created_at":"2026-09-13T12:34:56.000Z","updated_at":"2026-09-13T12:34:56.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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"409":{"description":"そのスラグのページはすでにあります","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-slug_taken","title":"そのスラグのページはすでにあります","status":409,"code":"slug_taken","detail":"our-club にはスラグ about のページがすでにあります"}}}},"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":"slug","message":"スラグは英小文字・数字・ハイフンの 1〜60 文字で、先頭と末尾はハイフンにできません"}]}}}},"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":"この API キーでの呼び出しは 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"}}}}}}},"/spaces/{subdomain}/pages/{page_id}":{"parameters":[{"$ref":"#/components/parameters/Subdomain"},{"$ref":"#/components/parameters/PageId"}],"get":{"operationId":"getSpacePage","tags":["Pages"],"summary":"フリーページを取得する","description":"ページを本文（HTML）ごと返します。","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/PageDetail"},"example":{"id":"k3m9q2x7v4b8n6p5","slug":"about","kind":"scripted","url":"https://our-club.framed.dog/about","created_at":"2026-09-13T12:34:56.000Z","updated_at":"2026-09-13T12:34:56.000Z","html":"<h1>わたしたちについて</h1>\n<style>h1 { font-family: system-ui; }</style>"}}}},"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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのページはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-page_not_found","title":"そのページはありません","status":404,"code":"page_not_found","detail":"ページ k3m9q2x7v4b8n6p5 はありません"}}}},"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":"この API キーでの呼び出しは 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":"updateSpacePage","tags":["Pages"],"summary":"フリーページを書き換える","description":"`slug`・`kind`・`html` のうち、送ったものだけを書き換えます。どれも無いと `422` です。\n**スラグを変えると公開 URL が変わり、古い URL は開けなくなります**（転送はしません）。ページ ID は変わりません。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePageRequest"},"example":{"html":"<h1>わたしたちについて（更新）</h1>"}}}},"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/Page"},"example":{"id":"k3m9q2x7v4b8n6p5","slug":"about","kind":"scripted","url":"https://our-club.framed.dog/about","created_at":"2026-09-13T12:34:56.000Z","updated_at":"2026-09-14T09:00: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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのページはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-page_not_found","title":"そのページはありません","status":404,"code":"page_not_found","detail":"ページ k3m9q2x7v4b8n6p5 はありません"}}}},"409":{"description":"そのスラグのページはすでにあります","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-slug_taken","title":"そのスラグのページはすでにあります","status":409,"code":"slug_taken","detail":"our-club にはスラグ about のページがすでにあります"}}}},"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":"slug","message":"スラグは英小文字・数字・ハイフンの 1〜60 文字で、先頭と末尾はハイフンにできません"}]}}}},"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":"この API キーでの呼び出しは 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":"deleteSpacePage","tags":["Pages"],"summary":"フリーページを削除する","description":"ページを削除します。**公開中のページは開けなくなり、元に戻せません。**","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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのページはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-page_not_found","title":"そのページはありません","status":404,"code":"page_not_found","detail":"ページ k3m9q2x7v4b8n6p5 はありません"}}}},"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":"この API キーでの呼び出しは 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"}}}}}}},"/spaces/{subdomain}/parts":{"parameters":[{"$ref":"#/components/parameters/Subdomain"}],"get":{"operationId":"listSpaceParts","tags":["Parts"],"summary":"パーツの一覧","description":"スペースのパーツを、識別名の順にすべて返します。中身（HTML）は含みません。","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/PartList"},"example":{"data":[{"id":"q8v2n5k3m7x9b4p6","name":"site-header","created_at":"2026-09-15T10:00:00.000Z","updated_at":"2026-09-15T10:00: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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"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":"この API キーでの呼び出しは 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":"createSpacePart","tags":["Parts"],"summary":"パーツを作る","description":"識別名とテンプレート HTML でパーツを作ります。**パーツ単独では公開されません**（URL を持ちません）。\nページやほかのパーツから、Liquid の標準のタグで組み込んで使います。\n\n- `{% render 'site-header', title: 'お知らせ' %}` — 値を明示して渡す（ページの変数は見えない）\n- `{% include 'site-header' %}` — ページの変数をそのまま使う\n- `{% layout 'base' %}{% block content %}…{% endblock %}` — 共通の枠に本文をはめる\n\n組み込みをたどって元に戻る形（循環）は `422` で断ります。評価が終わらなくなるためです。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePartRequest"},"example":{"name":"site-header","html":"<header>\n  <h1>{{ space.name }}</h1>\n  <nav>{% for p in pages %}<a href=\"{{ p.url }}\">{{ p.slug }}</a>{% endfor %}</nav>\n</header>"}}}},"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/Part"},"example":{"id":"q8v2n5k3m7x9b4p6","name":"site-header","created_at":"2026-09-15T10:00:00.000Z","updated_at":"2026-09-15T10:00: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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"409":{"description":"識別名が使われている（`part_name_taken`）か、パーツの合計の大きさが上限に達している（`parts_quota_exceeded`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-part_name_taken","title":"その識別名のパーツはすでにあります","status":409,"code":"part_name_taken","detail":"our-club には同じ識別名のパーツがすでにあります"}}}},"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":"slug","message":"スラグは英小文字・数字・ハイフンの 1〜60 文字で、先頭と末尾はハイフンにできません"}]}}}},"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":"この API キーでの呼び出しは 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"}}}}}}},"/spaces/{subdomain}/parts/{part_id}":{"parameters":[{"$ref":"#/components/parameters/Subdomain"},{"$ref":"#/components/parameters/PartId"}],"get":{"operationId":"getSpacePart","tags":["Parts"],"summary":"パーツを取得する","description":"パーツを中身（HTML）ごと返します。\n**そのパーツを組み込んでいるページとパーツ（`used_by`）も返します。**\n識別名を変える前・消す前に、どこが壊れるかを確かめてください。\n\n`used_by` に出るのは、識別名を引用符でそのまま書いた組み込みだけです。\n変数で名前を渡した組み込み（`{% render part_name %}`）は探せません。","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/PartDetail"},"example":{"id":"q8v2n5k3m7x9b4p6","name":"site-header","created_at":"2026-09-15T10:00:00.000Z","updated_at":"2026-09-15T10:00:00.000Z","html":"<header>\n  <h1>{{ space.name }}</h1>\n  <nav>{% for p in pages %}<a href=\"{{ p.url }}\">{{ p.slug }}</a>{% endfor %}</nav>\n</header>","used_by":{"pages":[{"id":"k3m9q2x7v4b8n6p5","slug":"about"}],"parts":[]}}}}},"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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのパーツはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-part_not_found","title":"そのパーツはありません","status":404,"code":"part_not_found","detail":"パーツ q8v2n5k3m7x9b4p6 はありません"}}}},"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":"この API キーでの呼び出しは 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":"updateSpacePart","tags":["Parts"],"summary":"パーツを書き換える","description":"`name` と `html` のうち、送ったものだけを書き換えます。どちらも無いと `422` です。\n**組み込んでいる公開中のページは、直ちに変わります。**\n\n**識別名を変えると、古い名前で組み込んでいるページはエラーのページになります。**\n組み込み側の記述は書き換わらないので、先に `GET` の `used_by` を見て、\nページやほかのパーツの方も直してください。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePartRequest"},"example":{"html":"<header><h1>{{ space.name }}</h1></header>"}}}},"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/Part"},"example":{"id":"q8v2n5k3m7x9b4p6","name":"site-header","created_at":"2026-09-15T10:00:00.000Z","updated_at":"2026-09-16T09:00: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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのパーツはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-part_not_found","title":"そのパーツはありません","status":404,"code":"part_not_found","detail":"パーツ q8v2n5k3m7x9b4p6 はありません"}}}},"409":{"description":"識別名が使われている（`part_name_taken`）か、パーツの合計の大きさが上限に達している（`parts_quota_exceeded`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-part_name_taken","title":"その識別名のパーツはすでにあります","status":409,"code":"part_name_taken","detail":"our-club には同じ識別名のパーツがすでにあります"}}}},"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":"slug","message":"スラグは英小文字・数字・ハイフンの 1〜60 文字で、先頭と末尾はハイフンにできません"}]}}}},"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":"この API キーでの呼び出しは 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":"deleteSpacePart","tags":["Parts"],"summary":"パーツを削除する","description":"パーツを削除します。**組み込んでいたページはエラーのページになります。元に戻せません。** 先に `GET` の `used_by` で影響を確かめてください。","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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのパーツはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-part_not_found","title":"そのパーツはありません","status":404,"code":"part_not_found","detail":"パーツ q8v2n5k3m7x9b4p6 はありません"}}}},"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":"この API キーでの呼び出しは 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"}}}}}}},"/spaces/{subdomain}/data":{"parameters":[{"$ref":"#/components/parameters/Subdomain"}],"get":{"operationId":"listSpaceData","tags":["Data"],"summary":"データストアのレコードの一覧","description":"スペースのデータストアのレコードを、キーの順にすべて返します。値も含みます。\nレコードは 100 件・合計 1048576 バイトまでなので、ページ分割はありません。\n\nレコードを作るのは `PUT /spaces/{subdomain}/data/{key}` です。キーは呼び出し側が決めるので、ここに `POST` はありません。","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/DataRecordList"},"example":{"data":[{"key":"events","value":{"items":[{"name":"春の展示","at":"2026-04-11"},{"name":"夏の合宿","at":"2026-08-02"}]},"description":"今年のイベント","version":3,"updated_at":"2026-09-14T09:00:00.000Z","schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"at":{"type":"string"}}}}}},"warnings":[]}]}}}},"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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"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":"この API キーでの呼び出しは 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"}}}}}}},"/spaces/{subdomain}/data/{key}":{"parameters":[{"$ref":"#/components/parameters/Subdomain"},{"$ref":"#/components/parameters/DataKey"}],"get":{"operationId":"getSpaceDataRecord","tags":["Data"],"summary":"データストアのレコードを取得する","description":"キーを指定してレコードを 1 件返します。\n書き換えるときは、返ってきた `version` をそのまま `PUT` に渡すと、他の人の更新を上書きしません。","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/DataRecord"},"example":{"key":"events","value":{"items":[{"name":"春の展示","at":"2026-04-11"},{"name":"夏の合宿","at":"2026-08-02"}]},"description":"今年のイベント","version":3,"updated_at":"2026-09-14T09:00:00.000Z","schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"at":{"type":"string"}}}}}},"warnings":[]}}}},"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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのレコードはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-data_not_found","title":"そのレコードはありません","status":404,"code":"data_not_found","detail":"キー events のレコードはありません"}}}},"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":"この API キーでの呼び出しは 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"}}}}}},"put":{"operationId":"putSpaceDataRecord","tags":["Data"],"summary":"データストアのレコードを保存する","description":"キーを指定してレコードを作る、または**丸ごと置き換え**ます。一部だけの更新はできません。\n既存のレコードを直すときは、`GET` で読んだ値を元に、変えたところだけ直した `value` 全体を送ってください。\n\n`version` を送ると、その版のままのときだけ書き換えます（楽観ロック）。合わないときは `409 version_conflict` です。\n`description` を省くと、既存の説明はそのままです。\n\n**形の食い違いでは断りません。** 配列の要素どうしでキーや型が揃っていないときは、\n保存したうえで応答の `warnings` に載せます。スキーマはユーザーが書くのではなく、入れた値から推論します。\n\n**ここに置いたものは公開されます。** フリーページから読める＝誰でも読めるということです。秘密は置かないでください。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutDataRequest"},"example":{"value":{"items":[{"name":"春の展示","at":"2026-04-11"},{"name":"夏の合宿","at":"2026-08-02"}]},"description":"今年のイベント","version":2}}}},"responses":{"200":{"description":"保存しました。`warnings` があっても保存は通っています。","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/DataRecord"},"example":{"key":"events","value":{"items":[{"name":"春の展示","at":"2026-04-11"},{"name":"夏の合宿","at":"2026-08-02"}]},"description":"今年のイベント","version":3,"updated_at":"2026-09-14T09:00:00.000Z","schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"at":{"type":"string"}}}}}},"warnings":[]}}}},"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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのレコードはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-data_not_found","title":"そのレコードはありません","status":404,"code":"data_not_found","detail":"キー events のレコードはありません"}}}},"409":{"description":"版が合わない（`version_conflict`）か、データストアの上限に達している（`data_quota_exceeded`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-version_conflict","title":"他の人が先に更新しました","status":409,"code":"version_conflict","detail":"いまの版は 3 です。読み直してから、もう一度送ってください"}}}},"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":"slug","message":"スラグは英小文字・数字・ハイフンの 1〜60 文字で、先頭と末尾はハイフンにできません"}]}}}},"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":"この API キーでの呼び出しは 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":"deleteSpaceDataRecord","tags":["Data"],"summary":"データストアのレコードを削除する","description":"レコードを削除します。**このレコードを読んでいるフリーページは、その部分が空になります。元に戻せません。**","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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのレコードはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-data_not_found","title":"そのレコードはありません","status":404,"code":"data_not_found","detail":"キー events のレコードはありません"}}}},"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":"この API キーでの呼び出しは 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"}}}}}}},"/spaces/{subdomain}/files":{"parameters":[{"$ref":"#/components/parameters/Subdomain"}],"get":{"operationId":"listSpaceFiles","tags":["Files"],"summary":"ストレージのファイルの一覧","description":"スペースのストレージに上がっているファイルを、新しい順にすべて返します。使っている容量も返します。\n\n**ページの HTML から画像やフォントを参照するための `url` を知る手段は、これだけです**\n（上げたときの応答を除く。ID は推測できません）。テンプレートの中からは `{{ files }}` でも引けます。","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/FileList"},"example":{"data":[{"id":"w4t7h2n8k5m3q9x6","name":"logo.png","content_type":"image/png","size":20480,"url":"https://w4t7h2n8k5m3q9x6.files.frmd.spot","uploaded_by":"qr7mz2xk9dfa","created_at":"2026-09-14T08:00:00.000Z","updated_at":"2026-09-14T08:00:00.000Z"}],"storage":{"used":20480,"quota":104857600,"file_max_bytes":10485760}}}}},"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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"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":"この API キーでの呼び出しは 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":"uploadSpaceFile","tags":["Files"],"summary":"ストレージにファイルを上げる","description":"ファイル名と中身でファイルを上げ、`url` を返します。**範囲 `files_write` が要ります。**\n\n**中身は JSON の文字列で送ります。**\n- `encoding: \"base64\"`: 画像など、どんなファイルでも。改行や空白は無視します\n- `encoding: \"text\"`: SVG・CSS・JSON のような文字のファイルを、そのまま（UTF-8 で保存します）\n\n1 ファイル 10485760 バイト（元の大きさ）・1 スペース合計 104857600 バイトまでです。\nbase64 にすると大きくなるので、このエンドポイントだけ本文の上限を 14680064 バイトにしています。\n\n**配信する種類はファイル名の拡張子で決まります**（送った `Content-Type` や中身は見ません）。\n画像・動画・音声・PDF・テキスト・CSS・JSON・フォントはブラウザでそのまま開き、\nそれ以外（`.html`・`.js`・知らない拡張子）はダウンロードとして返します。どれも配信元でスクリプトは動きません。\n\n同じ名前のファイルがあっても、別のファイルとして増えます（URL は ID で決まります）。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadFileRequest"},"example":{"name":"logo.png","content":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==","encoding":"base64"}}}},"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/File"},"example":{"id":"w4t7h2n8k5m3q9x6","name":"logo.png","content_type":"image/png","size":70,"url":"https://w4t7h2n8k5m3q9x6.files.frmd.spot","uploaded_by":"qr7mz2xk9dfa","created_at":"2026-09-14T08:00:00.000Z","updated_at":"2026-09-14T08:00: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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"409":{"description":"ストレージの容量が足りません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-storage_quota_exceeded","title":"ストレージの容量が足りません","status":409,"code":"storage_quota_exceeded","detail":"ストレージは 1 スペース 104857600 バイトまでです。要らないファイルを消してから送ってください"}}}},"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":"リクエスト本文は 14680064 バイトまでです"}}}},"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":"content","message":"1 つのファイルは 10485760 バイトまでです"}]}}}},"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":"この API キーでの呼び出しは 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"}}}}}}},"/spaces/{subdomain}/files/{file_id}":{"parameters":[{"$ref":"#/components/parameters/Subdomain"},{"$ref":"#/components/parameters/FileId"}],"get":{"operationId":"getSpaceFile","tags":["Files"],"summary":"ストレージのファイルを 1 つ取得する","description":"ファイルの名前・種類・大きさ・URL を返します。**中身は返しません**（`url` をそのまま取りに行けば取り出せます）。","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/File"},"example":{"id":"w4t7h2n8k5m3q9x6","name":"logo.png","content_type":"image/png","size":20480,"url":"https://w4t7h2n8k5m3q9x6.files.frmd.spot","uploaded_by":"qr7mz2xk9dfa","created_at":"2026-09-14T08:00:00.000Z","updated_at":"2026-09-14T08:00: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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのファイルはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-file_not_found","title":"そのファイルはありません","status":404,"code":"file_not_found","detail":"ファイル w4t7h2n8k5m3q9x6 はありません"}}}},"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":"この API キーでの呼び出しは 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":"updateSpaceFile","tags":["Files"],"summary":"ストレージのファイルを書き換える","description":"`name` と中身（`content` と `encoding`）のうち、送ったものだけを書き換えます。**範囲 `files_write` が要ります。**\n**ID と `url` は変わりません。** 参照しているページの HTML を直さずに、画像や CSS を差し替えられます。\n\n- `content` と `encoding` は一緒に送ります。中身の書き方は `POST` と同じです\n- 名前を変えると、配信する種類も新しい拡張子で決め直します\n- **参照している公開中のページは、直ちに変わります。** 前の中身には戻せません\n- 配信はブラウザに 5 分キャッシュさせるので、見る人によっては古い中身がしばらく見えます\n- 同時に書き換えられたときは、あとから届いた方が残ります\n\n人が管理画面から上げたファイルも書き換えられます。","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFileRequest"},"example":{"name":"theme.css","content":"body { font-family: system-ui; }","encoding":"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/File"},"example":{"id":"w4t7h2n8k5m3q9x6","name":"theme.css","content_type":"text/css","size":32,"url":"https://w4t7h2n8k5m3q9x6.files.frmd.spot","uploaded_by":"qr7mz2xk9dfa","created_at":"2026-09-14T08:00:00.000Z","updated_at":"2026-09-16T09:00: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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのファイルはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-file_not_found","title":"そのファイルはありません","status":404,"code":"file_not_found","detail":"ファイル w4t7h2n8k5m3q9x6 はありません"}}}},"409":{"description":"ストレージの容量が足りません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-storage_quota_exceeded","title":"ストレージの容量が足りません","status":409,"code":"storage_quota_exceeded","detail":"ストレージは 1 スペース 104857600 バイトまでです。要らないファイルを消してから送ってください"}}}},"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":"リクエスト本文は 14680064 バイトまでです"}}}},"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":"content","message":"1 つのファイルは 10485760 バイトまでです"}]}}}},"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":"この API キーでの呼び出しは 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":"deleteSpaceFile","tags":["Files"],"summary":"ストレージのファイルを削除する","description":"ファイルを削除します。**範囲 `files_write` が要ります。**\n**`url` はすぐ 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":"キーのスペースがパスと違う（`space_mismatch`）か、キーにその操作の範囲が付いていない（`insufficient_scope`）。`code` で見分けてください。","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-space_mismatch","title":"この API キーでは、そのスペースを操作できません","status":403,"code":"space_mismatch","detail":"この API キーは our-club のためのものではありません"}}}},"404":{"description":"そのファイルはありません","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://framed.dog/developers#error-file_not_found","title":"そのファイルはありません","status":404,"code":"file_not_found","detail":"ファイル w4t7h2n8k5m3q9x6 はありません"}}}},"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":"この API キーでの呼び出しは 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":{"spaceApiKey":{"type":"http","scheme":"bearer","bearerFormat":"framed_sk_…","description":"スペースごとに発行する API キー。Framed! のスペースの管理画面（`/spaces/<subdomain>`）で参加者が発行します。\nキーは発行したときに一度だけ表示されます。発行した人がスペースから外れると、そのキーは失効します。\n\n**キーには触れる範囲が付きます**（発行時に選び、あとから変えられません）。\n- `pages`: スペースのサイトのフリーページとパーツ（共通のテンプレート）を読み書きします。サイトの見た目を作る範囲です。\n- `data`: スペースのデータストアのレコードを読み書きします。\n- `files`: スペースのストレージに上がっているファイルの一覧と URL を読みます。上げる・消すはできません。\n- `files_write`: スペースのストレージにファイルを上げ、中身の差し替え・名前の変更・削除をします。一覧と URL の読み取り（files）も含みます。\n範囲の足りない操作を呼ぶと `403 insufficient_scope` を返します。"}},"parameters":{"Subdomain":{"name":"subdomain","in":"path","required":true,"description":"スペースのサブドメイン（`https://<subdomain>.framed.dog` の部分）。","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$"},"example":"our-club"},"PartId":{"name":"part_id","in":"path","required":true,"description":"パーツの ID（作成・一覧の応答の `id`）。識別名とは別物で、変わりません。","schema":{"type":"string","pattern":"^[2-9a-z]{16}$"},"example":"q8v2n5k3m7x9b4p6"},"FileId":{"name":"file_id","in":"path","required":true,"description":"ファイルの ID（一覧・上げたときの応答の `id`）。名前や中身を変えても変わりません。","schema":{"type":"string","pattern":"^[2-9a-z]{16}$"},"example":"w4t7h2n8k5m3q9x6"},"DataKey":{"name":"key","in":"path","required":true,"description":"レコードのキー。呼び出し側が決めます。フリーページからは `{{ data[\"<キー>\"] }}` で読めます。","schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$"},"example":"events"},"PageId":{"name":"page_id","in":"path","required":true,"description":"ページの ID（作成・一覧の応答の `id`）。","schema":{"type":"string","pattern":"^[2-9a-z]{16}$"},"example":"k3m9q2x7v4b8n6p5"}},"headers":{"RateLimitLimit":{"description":"この API キーで 1 分間に呼び出せる回数（60）。","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"いまの 1 分間に、あと何回呼び出せるか。","schema":{"type":"integer"}},"RateLimitReset":{"description":"回数が数え直されるまでの秒数。","schema":{"type":"integer"}},"RetryAfter":{"description":"もう一度呼び出してよくなるまでの秒数。","schema":{"type":"integer"}}},"schemas":{"CreatePageRequest":{"type":"object","additionalProperties":false,"required":["slug","html"],"properties":{"slug":{"type":"string","description":"ページの URL（`https://<subdomain>.framed.dog/<slug>`）になる文字列。スペースの中で一意です。\n英小文字・数字・ハイフンの 1〜60 文字で、先頭と末尾はハイフンにできません。前後の空白は取り除きます。\n\n**サイトが自分で使うパスは指定できません**（`articles`）。\nその名前のページは作れても開けなくなるためです。","minLength":1,"maxLength":60,"pattern":"^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$","not":{"enum":["articles"]},"examples":["about","events-2026"]},"kind":{"type":"string","enum":["scripted","static"],"description":"ページの種類。\n- `scripted`（スクリプトあり）: HTML・CSS・JavaScript をそのまま動かします。Framed! 本体とは別のドメインのフレームの中で表示するため、閲覧者のサインイン状態には触れられません。\n- `static`（スクリプトなし）: スクリプトを動かさず、スペースのサイトに直接表示します。`<script>`・イベント属性・`javascript:` の URL・フォーム・`<iframe>` などの埋め込み・SVG は、表示するときに取り除きます（保存した本文は書き換えません）。\n省略すると `scripted`。","examples":["scripted","static"],"default":"scripted"},"html":{"type":"string","description":"ページの中身。保存するときには書き換えません。\n前後の空白を取り除いたうえで、空でなく 262144 文字以内であること。\n`kind` が `static` のページは、表示するときにスクリプトなどを取り除きます（`kind` を参照）。","minLength":1,"maxLength":262144}}},"UpdatePageRequest":{"type":"object","description":"`slug`・`kind`・`html` のうち 1 つ以上。","additionalProperties":false,"minProperties":1,"properties":{"slug":{"type":"string","description":"ページの URL（`https://<subdomain>.framed.dog/<slug>`）になる文字列。スペースの中で一意です。\n英小文字・数字・ハイフンの 1〜60 文字で、先頭と末尾はハイフンにできません。前後の空白は取り除きます。\n\n**サイトが自分で使うパスは指定できません**（`articles`）。\nその名前のページは作れても開けなくなるためです。","minLength":1,"maxLength":60,"pattern":"^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$","not":{"enum":["articles"]},"examples":["about","events-2026"]},"kind":{"type":"string","enum":["scripted","static"],"description":"ページの種類。\n- `scripted`（スクリプトあり）: HTML・CSS・JavaScript をそのまま動かします。Framed! 本体とは別のドメインのフレームの中で表示するため、閲覧者のサインイン状態には触れられません。\n- `static`（スクリプトなし）: スクリプトを動かさず、スペースのサイトに直接表示します。`<script>`・イベント属性・`javascript:` の URL・フォーム・`<iframe>` などの埋め込み・SVG は、表示するときに取り除きます（保存した本文は書き換えません）。","examples":["scripted","static"]},"html":{"type":"string","description":"ページの中身。保存するときには書き換えません。\n前後の空白を取り除いたうえで、空でなく 262144 文字以内であること。\n`kind` が `static` のページは、表示するときにスクリプトなどを取り除きます（`kind` を参照）。","minLength":1,"maxLength":262144}}},"Page":{"type":"object","additionalProperties":false,"required":["id","slug","kind","url","created_at","updated_at"],"properties":{"id":{"type":"string","description":"ページの ID。スラグを変えても変わりません。","pattern":"^[2-9a-z]{16}$","examples":["k3m9q2x7v4b8n6p5"]},"slug":{"type":"string","description":"ページのスラグ。","examples":["about"]},"kind":{"type":"string","enum":["scripted","static"],"description":"ページの種類。"},"url":{"type":"string","format":"uri","description":"ページが公開されている URL。","examples":["https://our-club.framed.dog/about"]},"created_at":{"type":"string","format":"date-time","description":"作った日時（UTC、ISO 8601）。"},"updated_at":{"type":"string","format":"date-time","description":"最後に書き換えた日時（UTC、ISO 8601）。作っただけなら作った日時。"}}},"PageDetail":{"type":"object","additionalProperties":false,"required":["id","slug","kind","url","created_at","updated_at","html"],"properties":{"id":{"type":"string","description":"ページの ID。スラグを変えても変わりません。","pattern":"^[2-9a-z]{16}$","examples":["k3m9q2x7v4b8n6p5"]},"slug":{"type":"string","description":"ページのスラグ。","examples":["about"]},"kind":{"type":"string","enum":["scripted","static"],"description":"ページの種類。"},"url":{"type":"string","format":"uri","description":"ページが公開されている URL。","examples":["https://our-club.framed.dog/about"]},"created_at":{"type":"string","format":"date-time","description":"作った日時（UTC、ISO 8601）。"},"updated_at":{"type":"string","format":"date-time","description":"最後に書き換えた日時（UTC、ISO 8601）。作っただけなら作った日時。"},"html":{"type":"string","description":"ページの中身（保存したとおりの HTML）。"}}},"PageList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"ページ。スラグの順。","items":{"$ref":"#/components/schemas/Page"}}}},"Space":{"type":"object","additionalProperties":false,"required":["subdomain","name","url","icon_url","scopes","rules","rate_limit"],"properties":{"subdomain":{"type":"string","description":"スペースのサブドメイン。"},"name":{"type":"string","description":"スペースの表示名。自由な文字列です。"},"url":{"type":"string","format":"uri","description":"スペースのサイトの URL。"},"icon_url":{"type":["string","null"],"description":"スペースの画像（256×256 の PNG）の URL。設定されていなければ `null`。画像を替えると URL が変わります。"},"scopes":{"type":"array","description":"**この API キーに付いている範囲。** 発行するときに決まり、あとから変わりません。\n無い範囲の操作は `403 insufficient_scope` になります。","items":{"type":"string","enum":["pages","data","files","files_write"]}},"rules":{"type":"object","description":"入力の規則。実装と同じ定義から組み立てているので、上限を変えてもここがずれることはありません。","additionalProperties":false,"required":["page","part","data","file"],"properties":{"page":{"type":"object","additionalProperties":false,"required":["slug","kinds","default_kind","html_max_length"],"properties":{"slug":{"$ref":"#/components/schemas/LengthPatternRule"},"kinds":{"type":"array","description":"ページの種類。`scripted` はスクリプトが動き、`static` は動きません。","items":{"type":"string","enum":["scripted","static"]}},"default_kind":{"type":"string","enum":["scripted","static"]},"html_max_length":{"type":"integer"}}},"part":{"type":"object","additionalProperties":false,"required":["name","html_max_length","total_max_length"],"properties":{"name":{"$ref":"#/components/schemas/LengthPatternRule"},"html_max_length":{"type":"integer"},"total_max_length":{"type":"integer","description":"1 スペースのパーツの中身の合計の上限（文字数）。"}}},"data":{"type":"object","additionalProperties":false,"required":["key_pattern","max_records","value_max_bytes","total_max_bytes","max_depth"],"properties":{"key_pattern":{"type":"string"},"max_records":{"type":"integer"},"value_max_bytes":{"type":"integer"},"total_max_bytes":{"type":"integer"},"max_depth":{"type":"integer"}}},"file":{"type":"object","additionalProperties":false,"required":["max_bytes","quota_bytes","name_max_length","encodings"],"properties":{"max_bytes":{"type":"integer","description":"1 ファイルの上限（バイト）。"},"quota_bytes":{"type":"integer","description":"1 スペースの合計の上限（バイト）。"},"name_max_length":{"type":"integer","description":"ファイル名の上限（文字数）。"},"encodings":{"type":"array","description":"上げるときの中身の書き方（`encoding`）。","items":{"type":"string","enum":["base64","text"]}}}}}},"rate_limit":{"type":"object","additionalProperties":false,"required":["requests_per_minute"],"properties":{"requests_per_minute":{"type":"integer"}}}}},"LengthPatternRule":{"type":"object","description":"長さと形の規則。`pattern` は正規表現です。","additionalProperties":false,"required":["min_length","max_length","pattern"],"properties":{"min_length":{"type":"integer"},"max_length":{"type":"integer"},"pattern":{"type":"string"}}},"CreatePartRequest":{"type":"object","additionalProperties":false,"required":["name","html"],"properties":{"name":{"type":"string","description":"パーツの識別名。テンプレートからは `{% render '<識別名>' %}` のように引用符で囲んで引きます。\n英小文字・数字・ハイフン・アンダースコアの 1〜60 文字で、先頭と末尾は英小文字か数字です。スペースの中で一意です。\n`/` と `.` は使えないので、パスのように読める名前は作れません。","minLength":1,"maxLength":60,"pattern":"^[a-z0-9](?:[a-z0-9_-]{0,58}[a-z0-9])?$","examples":["site-header","base"]},"html":{"type":"string","description":"パーツの中身。Liquid のテンプレートです。\n前後の空白を取り除いたうえで、空でなく 262144 文字以内であること。\n1 スペースのパーツの中身の合計は 1048576 文字までです。\nほかのパーツを組み込めますが、たどって元に戻る形（循環）は保存できません。","minLength":1,"maxLength":262144}}},"UpdatePartRequest":{"type":"object","description":"`name` と `html` のどちらか、または両方。","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":"string","description":"パーツの識別名。テンプレートからは `{% render '<識別名>' %}` のように引用符で囲んで引きます。\n英小文字・数字・ハイフン・アンダースコアの 1〜60 文字で、先頭と末尾は英小文字か数字です。スペースの中で一意です。\n`/` と `.` は使えないので、パスのように読める名前は作れません。","minLength":1,"maxLength":60,"pattern":"^[a-z0-9](?:[a-z0-9_-]{0,58}[a-z0-9])?$","examples":["site-header","base"]},"html":{"type":"string","description":"パーツの中身。Liquid のテンプレートです。\n前後の空白を取り除いたうえで、空でなく 262144 文字以内であること。\n1 スペースのパーツの中身の合計は 1048576 文字までです。\nほかのパーツを組み込めますが、たどって元に戻る形（循環）は保存できません。","minLength":1,"maxLength":262144}}},"Part":{"type":"object","additionalProperties":false,"required":["id","name","created_at","updated_at"],"properties":{"id":{"type":"string","description":"パーツの ID。識別名を変えても変わりません。","pattern":"^[2-9a-z]{16}$","examples":["q8v2n5k3m7x9b4p6"]},"name":{"type":"string","description":"パーツの識別名。","examples":["site-header"]},"created_at":{"type":"string","format":"date-time","description":"作った日時。"},"updated_at":{"type":"string","format":"date-time","description":"最後に書き換えた日時。"}}},"PartDetail":{"type":"object","additionalProperties":false,"required":["id","name","created_at","updated_at","html","used_by"],"properties":{"id":{"type":"string","pattern":"^[2-9a-z]{16}$"},"name":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"html":{"type":"string","description":"パーツの中身（保存したとおりのテンプレート）。"},"used_by":{"type":"object","description":"このパーツを組み込んでいるページとパーツ。\n識別名を引用符でそのまま書いた組み込みだけが見つかります（変数で渡したものは探せません）。","additionalProperties":false,"required":["pages","parts"],"properties":{"pages":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["id","slug"],"properties":{"id":{"type":"string"},"slug":{"type":"string"}}}},"parts":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}}}}}}},"PartList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"パーツ。識別名の順。","items":{"$ref":"#/components/schemas/Part"}}}},"File":{"type":"object","additionalProperties":false,"required":["id","name","content_type","size","url","uploaded_by","created_at","updated_at"],"properties":{"id":{"type":"string","description":"ファイルの ID。名前や中身を変えても変わりません。","pattern":"^[2-9a-z]{16}$","examples":["w4t7h2n8k5m3q9x6"]},"name":{"type":"string","description":"ファイル名。同じ名前のファイルを複数置けます。"},"content_type":{"type":"string","description":"配信するときの種類。**ファイル名の拡張子から決めます**（送られてきた種類や中身は使いません）。","examples":["image/png"]},"size":{"type":"integer","description":"大きさ（バイト）。"},"url":{"type":"string","format":"uri","description":"ファイルの URL。1 ファイル 1 オリジンで、パスは見ません。\n**URL を知っていれば誰でも取り出せます**（スペースのサイトは公開のため）。","examples":["https://w4t7h2n8k5m3q9x6.files.frmd.spot"]},"uploaded_by":{"type":["string","null"],"description":"上げた人のハンドル。アカウントが消えていれば `null`。"},"created_at":{"type":"string","format":"date-time","description":"上げた日時。"},"updated_at":{"type":"string","format":"date-time","description":"最後に名前か中身を変えた日時。上げただけなら上げた日時。"}}},"UploadFileRequest":{"type":"object","additionalProperties":false,"required":["name","content","encoding"],"properties":{"name":{"type":"string","description":"ファイル名。255 文字までです。**拡張子で配信する種類が決まります。**\nパスの部分（`/` や `\\` より前）は捨て、制御文字を取り除きます。`.` や `..` だけの名前にはできません。","minLength":1,"maxLength":255,"examples":["logo.png","theme.css"]},"content":{"type":"string","description":"ファイルの中身。`encoding` が `base64` なら base64 にした文字列、`text` なら中身の文字列そのままです。\n1 ファイル 10485760 バイトまで（元の大きさで数えます）。空のファイルは上げられません。","minLength":1},"encoding":{"type":"string","enum":["base64","text"],"description":"`content` の書き方。\n- `base64`: 画像などどんなファイルでも。改行や空白は無視します\n- `text`: SVG・CSS・JSON のような文字のファイル。UTF-8 で保存します"}}},"UpdateFileRequest":{"type":"object","description":"`name` と中身（`content` と `encoding`）のどちらか、または両方。","additionalProperties":false,"anyOf":[{"required":["name"]},{"required":["content"]}],"dependentRequired":{"content":["encoding"],"encoding":["content"]},"properties":{"name":{"type":"string","description":"ファイル名。255 文字までです。**拡張子で配信する種類が決まります。**\nパスの部分（`/` や `\\` より前）は捨て、制御文字を取り除きます。`.` や `..` だけの名前にはできません。","minLength":1,"maxLength":255,"examples":["logo.png","theme.css"]},"content":{"type":"string","description":"ファイルの中身。`encoding` が `base64` なら base64 にした文字列、`text` なら中身の文字列そのままです。\n1 ファイル 10485760 バイトまで（元の大きさで数えます）。空のファイルは上げられません。","minLength":1},"encoding":{"type":"string","enum":["base64","text"],"description":"`content` の書き方。\n- `base64`: 画像などどんなファイルでも。改行や空白は無視します\n- `text`: SVG・CSS・JSON のような文字のファイル。UTF-8 で保存します"}}},"FileList":{"type":"object","additionalProperties":false,"required":["data","storage"],"properties":{"data":{"type":"array","description":"ファイル。新しい順。","items":{"$ref":"#/components/schemas/File"}},"storage":{"type":"object","description":"スペースのストレージの状況。","additionalProperties":false,"required":["used","quota","file_max_bytes"],"properties":{"used":{"type":"integer","description":"いま使っている合計（バイト）。"},"quota":{"type":"integer","description":"1 スペースの上限（バイト）。"},"file_max_bytes":{"type":"integer","description":"1 ファイルの上限（バイト）。"}}}}},"DataRecord":{"type":"object","additionalProperties":false,"required":["key","value","description","version","updated_at","schema","warnings"],"properties":{"key":{"type":"string","description":"レコードのキー。","pattern":"^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$","examples":["events"]},"value":{"description":"レコードの中身。JSON として表せるものなら何でも入ります。\n1 件 65536 バイト・入れ子 32 段までです。"},"description":{"type":["string","null"],"description":"このレコードが何かの短い説明。スペースの管理画面に出ます。","maxLength":200},"version":{"type":"integer","minimum":1,"description":"書き換えるたびに 1 増えます。`PUT` に渡すと楽観ロックになります。"},"updated_at":{"type":"string","format":"date-time","description":"最後に書き換えた日時（UTC、ISO 8601）。"},"schema":{"type":"object","description":"`value` から**その場で導いた**形（JSON Schema）。保存していないので、値とずれません。\n型と、その値に在るキーだけの緩いものです（`enum` / `format` / `pattern` は作りません）。"},"warnings":{"type":"array","description":"形の食い違い。**あっても保存は通っています。**\n同じ配列の要素どうしで、キーの集合・型が揃っているかだけを見ます。","items":{"$ref":"#/components/schemas/ShapeWarning"}}}},"ShapeWarning":{"type":"object","additionalProperties":false,"required":["path","message"],"properties":{"path":{"type":"string","description":"食い違っている場所。`items[1].at` のような書き方。","examples":["items[1]"]},"message":{"type":"string","description":"何が食い違っているか。"}}},"DataRecordList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"レコード。キーの順。","items":{"$ref":"#/components/schemas/DataRecord"}}}},"PutDataRequest":{"type":"object","additionalProperties":false,"required":["value"],"properties":{"value":{"description":"レコードの中身。**丸ごと置き換わります。**\n1 件 65536 バイト・入れ子 32 段までです。"},"description":{"type":["string","null"],"description":"短い説明。省くと、既存の説明はそのままです。","maxLength":200},"version":{"type":"integer","minimum":1,"description":"いま持っている版。送ると、その版のままのときだけ書き換えます（楽観ロック）。\n省くと、あるかどうかに関わらず置き換えます。"}}},"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":"通らなかった理由。"}}}}}}