Framed! API · v1.6.0

開発者向けドキュメント

Framed! のスペースのサイトを、外部のプログラムやエージェントから作るための API です。扱えるのは 4 つです。

  • フリーページ: サイトの 1 枚。URL を持ちます。本文は Liquid のテンプレートとして評価されます
  • パーツ: ページから組み込む共通のテンプレート。単独では公開されません
  • データストア: ページから読める共有の JSON。見た目と中身を分けるためのものです
  • ストレージ: 画像などのファイル。URL を持ち、ページの HTML から参照します

見た目は HTML、繰り返す枠はパーツ、変わる中身はデータストアに置き、ページからはテンプレートで読むと、あとで中身だけを直せます。

テンプレートの書き方(引ける値・エスケープ・パーツの組み込み)は開発者向けドキュメントの「テンプレート」にあります。

  • 認証はスペースごとに発行する API キーで行います(Authorization: Bearer <API キー>)。
  • 本文は JSON、エラーは RFC 9457(application/problem+json)で返します。ファイルの中身も JSON の文字列(base64 か、文字のファイルならそのまま)で送ります。
  • 呼び出しは API キーごとに 60 回/分までです。
  • キーには触れる範囲が付きます。 まず GET /spaces/{subdomain} を呼んで、そのキーで何ができるかを確かめてください。
  • API キーは秘密情報です。 サーバからだけ呼び出してください。ブラウザからの呼び出し(CORS)には対応していません。

自分として投稿・コメント・いいねをしたい場合は、こちらではありません。この API はスペースのサイトを扱うもので、認証はスペースごとの API キーです。人として 投稿するには ユーザーAPI を使ってください(アカウントごとのトークンで認証します)。

はじめる

  1. Framed! にサインインし、スペースの「外部連携(API)」の画面(/spaces/<subdomain>/integrations)でキーを発行します。キーは発行したときに一度だけ表示されます。
  2. キーを環境変数などの秘密の置き場所に保存します(例: FRAMED_API_KEY)。
  3. サーバから次のように呼び出すと、フリーページがすぐに公開されます。その前に GET /v1/spaces/<subdomain> を呼ぶと、そのキーで何ができるか(範囲)と入力の規則が分かります。
curl
curl -X POST https://api.framed.dog/v1/spaces/our-club/pages \
-H "Authorization: Bearer $FRAMED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"slug":"about","kind":"scripted","html":"<h1>わたしたちについて</h1>\n<style>h1 { font-family: system-ui; }</style>"}'
JavaScript(Node.js などのサーバで)
const response = await fetch('https://api.framed.dog/v1/spaces/our-club/pages', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.FRAMED_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"slug": "about",

AI エージェントから使う場合は、MCP サーバをつなぐのが簡単です。

認証

スペースごとに発行する API キー。Framed! のスペースの管理画面(/spaces/<subdomain>)で参加者が発行します。

キーは発行したときに一度だけ表示されます。発行した人がスペースから外れると、そのキーは失効します。

キーには触れる範囲が付きます(発行時に選び、あとから変えられません)。

  • pages: スペースのサイトのフリーページとパーツ(共通のテンプレート)を読み書きします。サイトの見た目を作る範囲です。
  • data: スペースのデータストアのレコードを読み書きします。
  • files: スペースのストレージに上がっているファイルの一覧と URL を読みます。上げる・消すはできません。
  • files_write: スペースのストレージにファイルを上げ、中身の差し替え・名前の変更・削除をします。一覧と URL の読み取り(files)も含みます。

範囲の足りない操作を呼ぶと 403 insufficient_scope を返します。

ヘッダ
Authorization: Bearer framed_sk_…

エンドポイント

ベース URL は https://api.framed.dog/v1 です。

スペースと、このキーでできることを確かめる

GEThttps://api.framed.dog/v1/spaces/{subdomain}

スペースの名前・URL に加えて、この API キーに付いている範囲(`scopes`)と、

ページ・パーツ・データ・ファイルそれぞれの入力の規則、呼び出し回数の上限を返します。

最初にこれを呼んでください。 範囲の足りない操作は 403 insufficient_scope になるので、

何ができるキーなのかを先に知る必要があります。この操作だけは範囲を問わず答えます。

規則の値は実装と同じ定義から組み立てているので、上限を変えてもここがずれることはありません。

パスパラメータ

スペースと、このキーでできることを確かめるのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$

応答(200)スペースと、このキーでできること。

スペースと、このキーでできることを確かめるの応答
項目説明
subdomain必須string

スペースのサブドメイン。

name必須string

スペースの表示名。自由な文字列です。

url必須string (uri)

スペースのサイトの URL。

icon_url必須stringnull

スペースの画像(256×256 の PNG)の URL。設定されていなければ null。画像を替えると URL が変わります。

scopes必須string[]

この API キーに付いている範囲。 発行するときに決まり、あとから変わりません。

無い範囲の操作は 403 insufficient_scope になります。

rules必須object

入力の規則。実装と同じ定義から組み立てているので、上限を変えてもここがずれることはありません。

rate_limit必須object

ここに無い項目を送ると 422 になります。

応答の例
{
"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"

返りうるステータス

  • 200スペースと、このキーでできること。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

フリーページの一覧

GEThttps://api.framed.dog/v1/spaces/{subdomain}/pages

スペースのフリーページを、スラグの順にすべて返します。本文(HTML)は含みません。

パスパラメータ

フリーページの一覧のパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$

応答(200)スペースのページの一覧。

フリーページの一覧の応答
項目説明
data必須Page[]

ページ。スラグの順。

ここに無い項目を送ると 422 になります。

応答の例
{
"data": [
{
"id": "k3m9q2x7v4b8n6p5",
"slug": "about",
"kind": "scripted",
"url": "https://our-club.framed.dog/about",
"created_at": "2026-09-13T12:34:56.000Z",

返りうるステータス

  • 200スペースのページの一覧。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

フリーページを作る

POSThttps://api.framed.dog/v1/spaces/{subdomain}/pages

スラグと HTML を指定して、スペースのサイトにフリーページを作ります。

作ったページはすぐに https://<subdomain>.framed.dog/<slug> で公開されます。

HTML はサーバで書き換えずにそのまま保存し、Framed! 本体とは別のドメイン(<id>.pages.frmd.spot)から配信します。

同じスラグのページがすでにあると 409 を返します(既存のページは変わりません)。

パスパラメータ

フリーページを作るのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$

リクエスト本文(application/json)

フリーページを作るのリクエスト本文
項目説明
slug必須string

ページの URL(https://<subdomain>.framed.dog/<slug>)になる文字列。スペースの中で一意です。

英小文字・数字・ハイフンの 1〜60 文字で、先頭と末尾はハイフンにできません。前後の空白は取り除きます。

サイトが自分で使うパスは指定できません(articles)。

その名前のページは作れても開けなくなるためです。

  • 1〜60 文字
  • パターン: ^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$
kind任意string

ページの種類。

  • scripted(スクリプトあり): HTML・CSS・JavaScript をそのまま動かします。Framed! 本体とは別のドメインのフレームの中で表示するため、閲覧者のサインイン状態には触れられません。
  • static(スクリプトなし): スクリプトを動かさず、スペースのサイトに直接表示します。<script>・イベント属性・javascript: の URL・フォーム・<iframe> などの埋め込み・SVG は、表示するときに取り除きます(保存した本文は書き換えません)。

省略すると scripted。

  • 次のいずれか: scripted, static
html必須string

ページの中身。保存するときには書き換えません。

前後の空白を取り除いたうえで、空でなく 262144 文字以内であること。

kind が static のページは、表示するときにスクリプトなどを取り除きます(kind を参照)。

  • 1〜262144 文字

ここに無い項目を送ると 422 になります。

本文の例
{
"slug": "about",
"kind": "scripted",
"html": "<h1>わたしたちについて</h1>\n<style>h1 { font-family: system-ui; }</style>"
}

応答(201)ページを作りました。

フリーページを作るの応答
項目説明
id必須string

ページの ID。スラグを変えても変わりません。

  • パターン: ^[2-9a-z]{16}$
slug必須string

ページのスラグ。

kind必須string

ページの種類。

  • 次のいずれか: scripted, static
url必須string (uri)

ページが公開されている URL。

created_at必須string (date-time)

作った日時(UTC、ISO 8601)。

updated_at必須string (date-time)

最後に書き換えた日時(UTC、ISO 8601)。作っただけなら作った日時。

ここに無い項目を送ると 422 になります。

応答の例
{
"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"
}

返りうるステータス

  • 201ページを作りました。
  • 400リクエスト本文を JSON として読めません
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 409そのスラグのページはすでにあります
  • 413リクエスト本文が大きすぎます
  • 415Content-Type は application/json にしてください
  • 422入力が規則を満たしていません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

フリーページを取得する

GEThttps://api.framed.dog/v1/spaces/{subdomain}/pages/{page_id}

ページを本文(HTML)ごと返します。

パスパラメータ

フリーページを取得するのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
page_id必須string

ページの ID(作成・一覧の応答の id)。

  • パターン: ^[2-9a-z]{16}$

応答(200)ページ。

フリーページを取得するの応答
項目説明
id必須string

ページの ID。スラグを変えても変わりません。

  • パターン: ^[2-9a-z]{16}$
slug必須string

ページのスラグ。

kind必須string

ページの種類。

  • 次のいずれか: scripted, static
url必須string (uri)

ページが公開されている URL。

created_at必須string (date-time)

作った日時(UTC、ISO 8601)。

updated_at必須string (date-time)

最後に書き換えた日時(UTC、ISO 8601)。作っただけなら作った日時。

html必須string

ページの中身(保存したとおりの HTML)。

ここに無い項目を送ると 422 になります。

応答の例
{
"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>"

返りうるステータス

  • 200ページ。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのページはありません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

フリーページを書き換える

PATCHhttps://api.framed.dog/v1/spaces/{subdomain}/pages/{page_id}

slug・kind・html のうち、送ったものだけを書き換えます。どれも無いと 422 です。

スラグを変えると公開 URL が変わり、古い URL は開けなくなります(転送はしません)。ページ ID は変わりません。

パスパラメータ

フリーページを書き換えるのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
page_id必須string

ページの ID(作成・一覧の応答の id)。

  • パターン: ^[2-9a-z]{16}$

リクエスト本文(application/json)

フリーページを書き換えるのリクエスト本文
項目説明
slug任意string

ページの URL(https://<subdomain>.framed.dog/<slug>)になる文字列。スペースの中で一意です。

英小文字・数字・ハイフンの 1〜60 文字で、先頭と末尾はハイフンにできません。前後の空白は取り除きます。

サイトが自分で使うパスは指定できません(articles)。

その名前のページは作れても開けなくなるためです。

  • 1〜60 文字
  • パターン: ^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$
kind任意string

ページの種類。

  • scripted(スクリプトあり): HTML・CSS・JavaScript をそのまま動かします。Framed! 本体とは別のドメインのフレームの中で表示するため、閲覧者のサインイン状態には触れられません。
  • static(スクリプトなし): スクリプトを動かさず、スペースのサイトに直接表示します。<script>・イベント属性・javascript: の URL・フォーム・<iframe> などの埋め込み・SVG は、表示するときに取り除きます(保存した本文は書き換えません)。
  • 次のいずれか: scripted, static
html任意string

ページの中身。保存するときには書き換えません。

前後の空白を取り除いたうえで、空でなく 262144 文字以内であること。

kind が static のページは、表示するときにスクリプトなどを取り除きます(kind を参照)。

  • 1〜262144 文字

ここに無い項目を送ると 422 になります。

本文の例
{
"html": "<h1>わたしたちについて(更新)</h1>"
}

応答(200)書き換えました。

フリーページを書き換えるの応答
項目説明
id必須string

ページの ID。スラグを変えても変わりません。

  • パターン: ^[2-9a-z]{16}$
slug必須string

ページのスラグ。

kind必須string

ページの種類。

  • 次のいずれか: scripted, static
url必須string (uri)

ページが公開されている URL。

created_at必須string (date-time)

作った日時(UTC、ISO 8601)。

updated_at必須string (date-time)

最後に書き換えた日時(UTC、ISO 8601)。作っただけなら作った日時。

ここに無い項目を送ると 422 になります。

応答の例
{
"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"
}

返りうるステータス

  • 200書き換えました。
  • 400リクエスト本文を JSON として読めません
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのページはありません
  • 409そのスラグのページはすでにあります
  • 413リクエスト本文が大きすぎます
  • 415Content-Type は application/json にしてください
  • 422入力が規則を満たしていません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

フリーページを削除する

DELETEhttps://api.framed.dog/v1/spaces/{subdomain}/pages/{page_id}

ページを削除します。公開中のページは開けなくなり、元に戻せません。

パスパラメータ

フリーページを削除するのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
page_id必須string

ページの ID(作成・一覧の応答の id)。

  • パターン: ^[2-9a-z]{16}$

応答(204)削除しました(本文なし)。

返りうるステータス

  • 204削除しました(本文なし)。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのページはありません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

パーツの一覧

GEThttps://api.framed.dog/v1/spaces/{subdomain}/parts

スペースのパーツを、識別名の順にすべて返します。中身(HTML)は含みません。

パスパラメータ

パーツの一覧のパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$

応答(200)パーツの一覧。

パーツの一覧の応答
項目説明
data必須Part[]

パーツ。識別名の順。

ここに無い項目を送ると 422 になります。

応答の例
{
"data": [
{
"id": "q8v2n5k3m7x9b4p6",
"name": "site-header",
"created_at": "2026-09-15T10:00:00.000Z",
"updated_at": "2026-09-15T10:00:00.000Z"
}

返りうるステータス

  • 200パーツの一覧。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

パーツを作る

POSThttps://api.framed.dog/v1/spaces/{subdomain}/parts

識別名とテンプレート HTML でパーツを作ります。パーツ単独では公開されません(URL を持ちません)。

ページやほかのパーツから、Liquid の標準のタグで組み込んで使います。

  • {% render 'site-header', title: 'お知らせ' %} — 値を明示して渡す(ページの変数は見えない)
  • {% include 'site-header' %} — ページの変数をそのまま使う
  • {% layout 'base' %}{% block content %}…{% endblock %} — 共通の枠に本文をはめる

組み込みをたどって元に戻る形(循環)は 422 で断ります。評価が終わらなくなるためです。

パスパラメータ

パーツを作るのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$

リクエスト本文(application/json)

パーツを作るのリクエスト本文
項目説明
name必須string

パーツの識別名。テンプレートからは {% render '<識別名>' %} のように引用符で囲んで引きます。

英小文字・数字・ハイフン・アンダースコアの 1〜60 文字で、先頭と末尾は英小文字か数字です。スペースの中で一意です。

/ と . は使えないので、パスのように読める名前は作れません。

  • 1〜60 文字
  • パターン: ^[a-z0-9](?:[a-z0-9_-]{0,58}[a-z0-9])?$
html必須string

パーツの中身。Liquid のテンプレートです。

前後の空白を取り除いたうえで、空でなく 262144 文字以内であること。

1 スペースのパーツの中身の合計は 1048576 文字までです。

ほかのパーツを組み込めますが、たどって元に戻る形(循環)は保存できません。

  • 1〜262144 文字

ここに無い項目を送ると 422 になります。

本文の例
{
"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>"
}

応答(201)パーツを作りました。

パーツを作るの応答
項目説明
id必須string

パーツの ID。識別名を変えても変わりません。

  • パターン: ^[2-9a-z]{16}$
name必須string

パーツの識別名。

created_at必須string (date-time)

作った日時。

updated_at必須string (date-time)

最後に書き換えた日時。

ここに無い項目を送ると 422 になります。

応答の例
{
"id": "q8v2n5k3m7x9b4p6",
"name": "site-header",
"created_at": "2026-09-15T10:00:00.000Z",
"updated_at": "2026-09-15T10:00:00.000Z"
}

返りうるステータス

  • 201パーツを作りました。
  • 400リクエスト本文を JSON として読めません
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 409識別名が使われている(`part_name_taken`)か、パーツの合計の大きさが上限に達している(`parts_quota_exceeded`)。`code` で見分けてください。
  • 413リクエスト本文が大きすぎます
  • 415Content-Type は application/json にしてください
  • 422入力が規則を満たしていません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

パーツを取得する

GEThttps://api.framed.dog/v1/spaces/{subdomain}/parts/{part_id}

パーツを中身(HTML)ごと返します。

そのパーツを組み込んでいるページとパーツ(`used_by`)も返します。

識別名を変える前・消す前に、どこが壊れるかを確かめてください。

used_by に出るのは、識別名を引用符でそのまま書いた組み込みだけです。

変数で名前を渡した組み込み({% render part_name %})は探せません。

パスパラメータ

パーツを取得するのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
part_id必須string

パーツの ID(作成・一覧の応答の id)。識別名とは別物で、変わりません。

  • パターン: ^[2-9a-z]{16}$

応答(200)パーツ。

パーツを取得するの応答
項目説明
id必須string
  • パターン: ^[2-9a-z]{16}$
name必須string
created_at必須string (date-time)
updated_at必須string (date-time)
html必須string

パーツの中身(保存したとおりのテンプレート)。

used_by必須object

このパーツを組み込んでいるページとパーツ。

識別名を引用符でそのまま書いた組み込みだけが見つかります(変数で渡したものは探せません)。

ここに無い項目を送ると 422 になります。

応答の例
{
"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": [

返りうるステータス

  • 200パーツ。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのパーツはありません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

パーツを書き換える

PATCHhttps://api.framed.dog/v1/spaces/{subdomain}/parts/{part_id}

name と html のうち、送ったものだけを書き換えます。どちらも無いと 422 です。

組み込んでいる公開中のページは、直ちに変わります。

識別名を変えると、古い名前で組み込んでいるページはエラーのページになります。

組み込み側の記述は書き換わらないので、先に GET の used_by を見て、

ページやほかのパーツの方も直してください。

パスパラメータ

パーツを書き換えるのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
part_id必須string

パーツの ID(作成・一覧の応答の id)。識別名とは別物で、変わりません。

  • パターン: ^[2-9a-z]{16}$

リクエスト本文(application/json)

パーツを書き換えるのリクエスト本文
項目説明
name任意string

パーツの識別名。テンプレートからは {% render '<識別名>' %} のように引用符で囲んで引きます。

英小文字・数字・ハイフン・アンダースコアの 1〜60 文字で、先頭と末尾は英小文字か数字です。スペースの中で一意です。

/ と . は使えないので、パスのように読める名前は作れません。

  • 1〜60 文字
  • パターン: ^[a-z0-9](?:[a-z0-9_-]{0,58}[a-z0-9])?$
html任意string

パーツの中身。Liquid のテンプレートです。

前後の空白を取り除いたうえで、空でなく 262144 文字以内であること。

1 スペースのパーツの中身の合計は 1048576 文字までです。

ほかのパーツを組み込めますが、たどって元に戻る形(循環)は保存できません。

  • 1〜262144 文字

ここに無い項目を送ると 422 になります。

本文の例
{
"html": "<header><h1>{{ space.name }}</h1></header>"
}

応答(200)書き換えました。

パーツを書き換えるの応答
項目説明
id必須string

パーツの ID。識別名を変えても変わりません。

  • パターン: ^[2-9a-z]{16}$
name必須string

パーツの識別名。

created_at必須string (date-time)

作った日時。

updated_at必須string (date-time)

最後に書き換えた日時。

ここに無い項目を送ると 422 になります。

応答の例
{
"id": "q8v2n5k3m7x9b4p6",
"name": "site-header",
"created_at": "2026-09-15T10:00:00.000Z",
"updated_at": "2026-09-16T09:00:00.000Z"
}

返りうるステータス

  • 200書き換えました。
  • 400リクエスト本文を JSON として読めません
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのパーツはありません
  • 409識別名が使われている(`part_name_taken`)か、パーツの合計の大きさが上限に達している(`parts_quota_exceeded`)。`code` で見分けてください。
  • 413リクエスト本文が大きすぎます
  • 415Content-Type は application/json にしてください
  • 422入力が規則を満たしていません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

パーツを削除する

DELETEhttps://api.framed.dog/v1/spaces/{subdomain}/parts/{part_id}

パーツを削除します。組み込んでいたページはエラーのページになります。元に戻せません。 先に GET の used_by で影響を確かめてください。

パスパラメータ

パーツを削除するのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
part_id必須string

パーツの ID(作成・一覧の応答の id)。識別名とは別物で、変わりません。

  • パターン: ^[2-9a-z]{16}$

応答(204)削除しました(本文なし)。

返りうるステータス

  • 204削除しました(本文なし)。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのパーツはありません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

データストアのレコードの一覧

GEThttps://api.framed.dog/v1/spaces/{subdomain}/data

スペースのデータストアのレコードを、キーの順にすべて返します。値も含みます。

レコードは 100 件・合計 1048576 バイトまでなので、ページ分割はありません。

レコードを作るのは PUT /spaces/{subdomain}/data/{key} です。キーは呼び出し側が決めるので、ここに POST はありません。

パスパラメータ

データストアのレコードの一覧のパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$

応答(200)データストアのレコードの一覧。

データストアのレコードの一覧の応答
項目説明
data必須DataRecord[]

レコード。キーの順。

ここに無い項目を送ると 422 になります。

応答の例
{
"data": [
{
"key": "events",
"value": {
"items": [
{
"name": "春の展示",

返りうるステータス

  • 200データストアのレコードの一覧。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

データストアのレコードを取得する

GEThttps://api.framed.dog/v1/spaces/{subdomain}/data/{key}

キーを指定してレコードを 1 件返します。

書き換えるときは、返ってきた version をそのまま PUT に渡すと、他の人の更新を上書きしません。

パスパラメータ

データストアのレコードを取得するのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
key必須string

レコードのキー。呼び出し側が決めます。フリーページからは {{ data["<キー>"] }} で読めます。

  • 1〜60 文字
  • パターン: ^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$

応答(200)レコード。

データストアのレコードを取得するの応答
項目説明
key必須string

レコードのキー。

  • パターン: ^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$
value必須any

レコードの中身。JSON として表せるものなら何でも入ります。

1 件 65536 バイト・入れ子 32 段までです。

description必須stringnull

このレコードが何かの短い説明。スペースの管理画面に出ます。

  • 200 文字まで
version必須integer

書き換えるたびに 1 増えます。PUT に渡すと楽観ロックになります。

updated_at必須string (date-time)

最後に書き換えた日時(UTC、ISO 8601)。

schema必須object

value からその場で導いた形(JSON Schema)。保存していないので、値とずれません。

型と、その値に在るキーだけの緩いものです(enum / format / pattern は作りません)。

warnings必須ShapeWarning[]

形の食い違い。あっても保存は通っています。

同じ配列の要素どうしで、キーの集合・型が揃っているかだけを見ます。

ここに無い項目を送ると 422 になります。

応答の例
{
"key": "events",
"value": {
"items": [
{
"name": "春の展示",
"at": "2026-04-11"
},

返りうるステータス

  • 200レコード。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのレコードはありません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

データストアのレコードを保存する

PUThttps://api.framed.dog/v1/spaces/{subdomain}/data/{key}

キーを指定してレコードを作る、または丸ごと置き換えます。一部だけの更新はできません。

既存のレコードを直すときは、GET で読んだ値を元に、変えたところだけ直した value 全体を送ってください。

version を送ると、その版のままのときだけ書き換えます(楽観ロック)。合わないときは 409 version_conflict です。

description を省くと、既存の説明はそのままです。

形の食い違いでは断りません。 配列の要素どうしでキーや型が揃っていないときは、

保存したうえで応答の warnings に載せます。スキーマはユーザーが書くのではなく、入れた値から推論します。

ここに置いたものは公開されます。 フリーページから読める=誰でも読めるということです。秘密は置かないでください。

パスパラメータ

データストアのレコードを保存するのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
key必須string

レコードのキー。呼び出し側が決めます。フリーページからは {{ data["<キー>"] }} で読めます。

  • 1〜60 文字
  • パターン: ^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$

リクエスト本文(application/json)

データストアのレコードを保存するのリクエスト本文
項目説明
value必須any

レコードの中身。丸ごと置き換わります。

1 件 65536 バイト・入れ子 32 段までです。

description任意stringnull

短い説明。省くと、既存の説明はそのままです。

  • 200 文字まで
version任意integer

いま持っている版。送ると、その版のままのときだけ書き換えます(楽観ロック)。

省くと、あるかどうかに関わらず置き換えます。

ここに無い項目を送ると 422 になります。

本文の例
{
"value": {
"items": [
{
"name": "春の展示",
"at": "2026-04-11"
},
{

応答(200)保存しました。`warnings` があっても保存は通っています。

データストアのレコードを保存するの応答
項目説明
key必須string

レコードのキー。

  • パターン: ^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$
value必須any

レコードの中身。JSON として表せるものなら何でも入ります。

1 件 65536 バイト・入れ子 32 段までです。

description必須stringnull

このレコードが何かの短い説明。スペースの管理画面に出ます。

  • 200 文字まで
version必須integer

書き換えるたびに 1 増えます。PUT に渡すと楽観ロックになります。

updated_at必須string (date-time)

最後に書き換えた日時(UTC、ISO 8601)。

schema必須object

value からその場で導いた形(JSON Schema)。保存していないので、値とずれません。

型と、その値に在るキーだけの緩いものです(enum / format / pattern は作りません)。

warnings必須ShapeWarning[]

形の食い違い。あっても保存は通っています。

同じ配列の要素どうしで、キーの集合・型が揃っているかだけを見ます。

ここに無い項目を送ると 422 になります。

応答の例
{
"key": "events",
"value": {
"items": [
{
"name": "春の展示",
"at": "2026-04-11"
},

返りうるステータス

  • 200保存しました。`warnings` があっても保存は通っています。
  • 400リクエスト本文を JSON として読めません
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのレコードはありません
  • 409版が合わない(`version_conflict`)か、データストアの上限に達している(`data_quota_exceeded`)。`code` で見分けてください。
  • 413リクエスト本文が大きすぎます
  • 415Content-Type は application/json にしてください
  • 422入力が規則を満たしていません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

データストアのレコードを削除する

DELETEhttps://api.framed.dog/v1/spaces/{subdomain}/data/{key}

レコードを削除します。このレコードを読んでいるフリーページは、その部分が空になります。元に戻せません。

パスパラメータ

データストアのレコードを削除するのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
key必須string

レコードのキー。呼び出し側が決めます。フリーページからは {{ data["<キー>"] }} で読めます。

  • 1〜60 文字
  • パターン: ^[a-z0-9](?:[a-z0-9-]{0,58}[a-z0-9])?$

応答(204)削除しました(本文なし)。

返りうるステータス

  • 204削除しました(本文なし)。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのレコードはありません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

ストレージのファイルの一覧

GEThttps://api.framed.dog/v1/spaces/{subdomain}/files

スペースのストレージに上がっているファイルを、新しい順にすべて返します。使っている容量も返します。

ページの HTML から画像やフォントを参照するための `url` を知る手段は、これだけです

(上げたときの応答を除く。ID は推測できません)。テンプレートの中からは {{ files }} でも引けます。

パスパラメータ

ストレージのファイルの一覧のパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$

応答(200)ファイルの一覧と、使っている容量。

ストレージのファイルの一覧の応答
項目説明
data必須File[]

ファイル。新しい順。

storage必須object

スペースのストレージの状況。

ここに無い項目を送ると 422 になります。

応答の例
{
"data": [
{
"id": "w4t7h2n8k5m3q9x6",
"name": "logo.png",
"content_type": "image/png",
"size": 20480,
"url": "https://w4t7h2n8k5m3q9x6.files.frmd.spot",

返りうるステータス

  • 200ファイルの一覧と、使っている容量。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

ストレージにファイルを上げる

POSThttps://api.framed.dog/v1/spaces/{subdomain}/files

ファイル名と中身でファイルを上げ、url を返します。範囲 `files_write` が要ります。

中身は JSON の文字列で送ります。

  • encoding: "base64": 画像など、どんなファイルでも。改行や空白は無視します
  • encoding: "text": SVG・CSS・JSON のような文字のファイルを、そのまま(UTF-8 で保存します)

1 ファイル 10485760 バイト(元の大きさ)・1 スペース合計 104857600 バイトまでです。

base64 にすると大きくなるので、このエンドポイントだけ本文の上限を 14680064 バイトにしています。

配信する種類はファイル名の拡張子で決まります(送った Content-Type や中身は見ません)。

画像・動画・音声・PDF・テキスト・CSS・JSON・フォントはブラウザでそのまま開き、

それ以外(.html・.js・知らない拡張子)はダウンロードとして返します。どれも配信元でスクリプトは動きません。

同じ名前のファイルがあっても、別のファイルとして増えます(URL は ID で決まります)。

パスパラメータ

ストレージにファイルを上げるのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$

リクエスト本文(application/json)

ストレージにファイルを上げるのリクエスト本文
項目説明
name必須string

ファイル名。255 文字までです。拡張子で配信する種類が決まります。

パスの部分(/ や \ より前)は捨て、制御文字を取り除きます。. や .. だけの名前にはできません。

  • 1〜255 文字
content必須string

ファイルの中身。encoding が base64 なら base64 にした文字列、text なら中身の文字列そのままです。

1 ファイル 10485760 バイトまで(元の大きさで数えます)。空のファイルは上げられません。

  • 1 文字以上
encoding必須string

content の書き方。

  • base64: 画像などどんなファイルでも。改行や空白は無視します
  • text: SVG・CSS・JSON のような文字のファイル。UTF-8 で保存します
  • 次のいずれか: base64, text

ここに無い項目を送ると 422 になります。

本文の例
{
"name": "logo.png",
"content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==",
"encoding": "base64"
}

応答(201)上げました。

ストレージにファイルを上げるの応答
項目説明
id必須string

ファイルの ID。名前や中身を変えても変わりません。

  • パターン: ^[2-9a-z]{16}$
name必須string

ファイル名。同じ名前のファイルを複数置けます。

content_type必須string

配信するときの種類。ファイル名の拡張子から決めます(送られてきた種類や中身は使いません)。

size必須integer

大きさ(バイト)。

url必須string (uri)

ファイルの URL。1 ファイル 1 オリジンで、パスは見ません。

URL を知っていれば誰でも取り出せます(スペースのサイトは公開のため)。

uploaded_by必須stringnull

上げた人のハンドル。アカウントが消えていれば null。

created_at必須string (date-time)

上げた日時。

updated_at必須string (date-time)

最後に名前か中身を変えた日時。上げただけなら上げた日時。

ここに無い項目を送ると 422 になります。

応答の例
{
"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",

返りうるステータス

  • 201上げました。
  • 400リクエスト本文を JSON として読めません
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 409ストレージの容量が足りません
  • 413リクエスト本文が大きすぎます
  • 415Content-Type は application/json にしてください
  • 422入力が規則を満たしていません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

ストレージのファイルを 1 つ取得する

GEThttps://api.framed.dog/v1/spaces/{subdomain}/files/{file_id}

ファイルの名前・種類・大きさ・URL を返します。中身は返しません(url をそのまま取りに行けば取り出せます)。

パスパラメータ

ストレージのファイルを 1 つ取得するのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
file_id必須string

ファイルの ID(一覧・上げたときの応答の id)。名前や中身を変えても変わりません。

  • パターン: ^[2-9a-z]{16}$

応答(200)ファイル。

ストレージのファイルを 1 つ取得するの応答
項目説明
id必須string

ファイルの ID。名前や中身を変えても変わりません。

  • パターン: ^[2-9a-z]{16}$
name必須string

ファイル名。同じ名前のファイルを複数置けます。

content_type必須string

配信するときの種類。ファイル名の拡張子から決めます(送られてきた種類や中身は使いません)。

size必須integer

大きさ(バイト)。

url必須string (uri)

ファイルの URL。1 ファイル 1 オリジンで、パスは見ません。

URL を知っていれば誰でも取り出せます(スペースのサイトは公開のため)。

uploaded_by必須stringnull

上げた人のハンドル。アカウントが消えていれば null。

created_at必須string (date-time)

上げた日時。

updated_at必須string (date-time)

最後に名前か中身を変えた日時。上げただけなら上げた日時。

ここに無い項目を送ると 422 になります。

応答の例
{
"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",

返りうるステータス

  • 200ファイル。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのファイルはありません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

ストレージのファイルを書き換える

PATCHhttps://api.framed.dog/v1/spaces/{subdomain}/files/{file_id}

name と中身(content と encoding)のうち、送ったものだけを書き換えます。範囲 `files_write` が要ります。

ID と `url` は変わりません。 参照しているページの HTML を直さずに、画像や CSS を差し替えられます。

  • content と encoding は一緒に送ります。中身の書き方は POST と同じです
  • 名前を変えると、配信する種類も新しい拡張子で決め直します
  • 参照している公開中のページは、直ちに変わります。 前の中身には戻せません
  • 配信はブラウザに 5 分キャッシュさせるので、見る人によっては古い中身がしばらく見えます
  • 同時に書き換えられたときは、あとから届いた方が残ります

人が管理画面から上げたファイルも書き換えられます。

パスパラメータ

ストレージのファイルを書き換えるのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
file_id必須string

ファイルの ID(一覧・上げたときの応答の id)。名前や中身を変えても変わりません。

  • パターン: ^[2-9a-z]{16}$

リクエスト本文(application/json)

ストレージのファイルを書き換えるのリクエスト本文
項目説明
name任意string

ファイル名。255 文字までです。拡張子で配信する種類が決まります。

パスの部分(/ や \ より前)は捨て、制御文字を取り除きます。. や .. だけの名前にはできません。

  • 1〜255 文字
content任意string

ファイルの中身。encoding が base64 なら base64 にした文字列、text なら中身の文字列そのままです。

1 ファイル 10485760 バイトまで(元の大きさで数えます)。空のファイルは上げられません。

  • 1 文字以上
encoding任意string

content の書き方。

  • base64: 画像などどんなファイルでも。改行や空白は無視します
  • text: SVG・CSS・JSON のような文字のファイル。UTF-8 で保存します
  • 次のいずれか: base64, text

ここに無い項目を送ると 422 になります。

本文の例
{
"name": "theme.css",
"content": "body { font-family: system-ui; }",
"encoding": "text"
}

応答(200)書き換えました。

ストレージのファイルを書き換えるの応答
項目説明
id必須string

ファイルの ID。名前や中身を変えても変わりません。

  • パターン: ^[2-9a-z]{16}$
name必須string

ファイル名。同じ名前のファイルを複数置けます。

content_type必須string

配信するときの種類。ファイル名の拡張子から決めます(送られてきた種類や中身は使いません)。

size必須integer

大きさ(バイト)。

url必須string (uri)

ファイルの URL。1 ファイル 1 オリジンで、パスは見ません。

URL を知っていれば誰でも取り出せます(スペースのサイトは公開のため)。

uploaded_by必須stringnull

上げた人のハンドル。アカウントが消えていれば null。

created_at必須string (date-time)

上げた日時。

updated_at必須string (date-time)

最後に名前か中身を変えた日時。上げただけなら上げた日時。

ここに無い項目を送ると 422 になります。

応答の例
{
"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",

返りうるステータス

  • 200書き換えました。
  • 400リクエスト本文を JSON として読めません
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのファイルはありません
  • 409ストレージの容量が足りません
  • 413リクエスト本文が大きすぎます
  • 415Content-Type は application/json にしてください
  • 422入力が規則を満たしていません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

ストレージのファイルを削除する

DELETEhttps://api.framed.dog/v1/spaces/{subdomain}/files/{file_id}

ファイルを削除します。範囲 `files_write` が要ります。

`url` はすぐ 404 になり、参照しているページでは画像などが表示されなくなります。元に戻せません。

人が管理画面から上げたファイルも消せます。

パスパラメータ

ストレージのファイルを削除するのパスパラメータ
項目説明
subdomain必須string

スペースのサブドメイン(https://<subdomain>.framed.dog の部分)。

  • パターン: ^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$
file_id必須string

ファイルの ID(一覧・上げたときの応答の id)。名前や中身を変えても変わりません。

  • パターン: ^[2-9a-z]{16}$

応答(204)削除しました(本文なし)。

返りうるステータス

  • 204削除しました(本文なし)。
  • 401API キーが無いか、有効ではありません
  • 403キーのスペースがパスと違う(`space_mismatch`)か、キーにその操作の範囲が付いていない(`insufficient_scope`)。`code` で見分けてください。
  • 404そのファイルはありません
  • 429呼び出しの回数が上限を超えました
  • 500サーバの内部で問題が起きました

テンプレート

ページとパーツの本文は、配信する前にサーバでテンプレートとして評価されます。記法は Liquid です。ページの種類(scripted / static)に関わらず、どちらも評価します。

評価はサーバの中だけで完結します。ブラウザに届くのは評価が終わった HTML だけなので、テンプレートから API キーやデータストアの書き込み権限が漏れることはありません。 逆に言うと、ここで引けた値はすべて公開されます。

引ける値

変数型中身
space.name文字列スペースの表示名。
space.subdomain文字列スペースのサブドメイン。
space.url文字列スペースのサイトの URL。
page.slug文字列いま表示しているページのスラグ。
page.url文字列いま表示しているページの URL。
pages配列そのスペースのページ。要素は slug と url。ナビゲーションを組み立てるのに使います。
files配列ストレージのファイル。要素は name・contentType・size・url。画像を貼るための URL はここから取ります。
dataオブジェクトデータストアのレコード。キーで引きます(data.events)。ハイフンを含むキーは data["weekly-todo"] と書きます。
ページの本文
<h1>{{ space.name }}</h1>
<nav>
{% for p in pages %}
<a href="{{ p.url }}">{{ p.slug }}</a>
{% endfor %}
</nav>

出力は既定でエスケープされます

スペース名もファイル名もデータストアの中身も自由入力なので、生のまま埋めると、 ある参加者が別の参加者のページに HTML を注入できてしまいます。生の HTML として入れたいときだけ | raw を付けます。

エスケープ
{{ space.name }} → 太郎 &amp; 花子 (既定:エスケープされる)
{{ space.name | raw }} → 太郎 & 花子 (生の HTML として入る)

パーツを組み込む

共通の見出しやレイアウトはパーツに切り出し、識別名で組み込みます。 パーツ自身も URL を持たず、ページやほかのパーツから組み込まれて初めて世に出ます。 組み込めるのは同じスペースのパーツだけです。

組み込み
{% comment %} パーツ側: site-header {% endcomment %}
<header><h1>{{ title }}</h1></header>
{% comment %} ページ側 {% endcomment %}
{% render 'site-header', title: space.name %} ← 渡した title だけが見える
{% include 'site-header' %} ← ページの変数がそのまま見える
{% comment %} 共通の枠にページの本文をはめる {% endcomment %}
  • render の中からは、ページの変数が見えません。space や data も含めて、渡した値だけが見えます。{{ space.name }} と書いたパーツが空になるのはこれが理由です。値が要るなら{% render 'x', title: space.name %} のように明示して渡すか、include を使ってください(include はページの変数をそのまま使えます)。
  • 組み込みをたどって元に戻る形(循環)は保存できません。評価が終わらなくなるためです。
  • パーツの識別名を変える・消すと、組み込んでいるページはエラーのページになります。組み込み側の記述は書き換わりません。先に GET /parts/{part_id} の used_by で影響を確かめてください。

書き間違えたとき

  • 知らない変数は空になります。{{ space.nmae }} と書いてもページ全体は壊れず、 そこが空文字になるだけです。
  • 構文エラーのときは、直し方が分かるページを返します。白紙にも 500 にもしません。閉じ忘れた {% for %} などがこれに当たります。
  • 保存するときは構文を検査していません。壊れたテンプレートも保存でき、開いて初めて分かります。作ったあとに URL を開いて確かめてください。

上限とできないこと

  • 解析するのは 1 本あたり 262,144 文字まで。 1 スペースのパーツの中身の合計は 1,048,576 文字まで。
  • 1 回の評価は 250ms まで。入れ子のループで重くすると、途中で打ち切られてエラーのページになります。
  • ファイルを読むタグ(テーマのファイルなど)は使えません。include / render / layout が引けるのは、そのスペースのパーツだけです。
  • 任意の JavaScript は動きません。オブジェクトのプロトタイプ経由で値を引くこともできません。
  • 評価のたびに、そのスペースのページ・ファイル・データ・パーツを全部読みます。 表示のたびに評価するので(配信はキャッシュしません)、重いテンプレートはそのまま表示の遅さになります。

データストア

スペースには、キーで引ける JSON を置けるデータストアがあります。フリーページの HTML からは、テンプレートで {{ data.<キー> }} として読めます。見た目を直すのと、中身を更新するのを分けられます。

フリーページの HTML
<h2>今年のイベント</h2>
<ul>
{% for e in data.events.items %}
<li>{{ e.name }}({{ e.at }})</li>
{% endfor %}
</ul>
データストアのレコード(events)
curl -X PUT https://api.framed.dog/v1/spaces/our-club/data/events \
-H "Authorization: Bearer $FRAMED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"value":{"items":[{"name":"春の展示","at":"2026-04-11"}]},"description":"今年のイベント"}'

ストレージ

スペースには画像・フォント・CSS などを置けるストレージがあります。 上げたファイルは URL を持ち、ページの HTML から参照できます。一覧と取得には files、上げる・書き換える・消すには files_write の範囲が要ります。

中身は JSON の文字列で送ります。画像などは "encoding": "base64"、SVG・CSS・JSON のような文字のファイルは "encoding": "text" で中身をそのまま送れます。

画像を上げる(curl)
{ printf '{"name":"logo.png","encoding":"base64","content":"'
base64 < logo.png | tr -d '\n'
printf '"}'; } |
curl -X POST https://api.framed.dog/v1/spaces/our-club/files \
-H "Authorization: Bearer $FRAMED_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @-
URL を変えずに中身を差し替える(curl)
curl -X PATCH https://api.framed.dog/v1/spaces/our-club/files/<file_id> \
-H "Authorization: Bearer $FRAMED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content":"body { font-family: system-ui; }","encoding":"text"}'

エラー

エラー(RFC 9457 Problem Details)。機械的な分岐には code を使ってください。

失敗は application/problem+json(RFC 9457)で返します。code の一覧は api.framed.dog の 2 つの API(この API と ユーザーAPI)で共通なので、こちらでは起きないものも含みます。

エラーの形
項目説明
type必須string (uri)

エラーの種類を説明するドキュメントの URI。

title必須string

エラーの種類ごとに固定の説明。

status必須integer

HTTP ステータスコード。

detail任意string

このリクエストに固有の説明。

code必須string

エラーの種類を表す機械向けのコード。

  • 次のいずれか: 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
errors任意ValidationError[]

validation_failed のときだけ、通らなかった項目を並べます。

例(422)
{
"type": "https://framed.dog/developers#error-validation_failed",
"title": "入力が規則を満たしていません",
"status": 422,
"code": "validation_failed",
"detail": "入力を 1 件直してください",
"errors": [
{
invalid_json400リクエスト本文を JSON として読めません

本文が JSON として壊れているか、空です。

unauthorized401API キーが無いか、有効ではありません

Authorization: Bearer <API キー> が無い、形が違う、存在しない、失効しているのいずれかです。どれに当たるかは返しません。

space_mismatch403この API キーでは、そのスペースを操作できません

API キーはスペースごとに発行されます。パスの subdomain と、キーを発行したスペースが違います。

insufficient_scope403この資格情報には、その操作の範囲が付いていません

API キーには触れる範囲(pages / data / files / files_write)、ユーザーAPIトークンには範囲(posts / comments / likes / profile)が付いています。範囲は発行するときに決まり、あとから増やせません。必要な範囲を選んで発行し直してください。

not_found404そのエンドポイントはありません

パスが間違っています。ベース URL に /v1 が含まれているか確かめてください。

page_not_found404そのページはありません

パスの page_id のページが、そのスペースにありません。削除されたか、ID が間違っています。

part_not_found404そのパーツはありません

パスの part_id のパーツが、そのスペースにありません。削除されたか、ID が間違っています。

file_not_found404そのファイルはありません

パスの file_id のファイルが、そのスペースのストレージにありません。削除されたか、ID が間違っています。

data_not_found404そのレコードはありません

パスの key のレコードが、そのスペースのデータストアにありません。削除されたか、キーが間違っています。

post_not_found404その投稿はありません

パスの post_id の投稿がありません。削除されたか、ID が間違っています。書き換え・削除では、他人の投稿を指したときもこれになります(ID の存在を他人に教えないため)。ほかの人の下書きも同じです。

comment_not_found404そのコメントはありません

パスの comment_id のコメントがありません。削除されたか、ID が間違っています。他人のコメントを書き換え・削除しようとしたときもこれになります(ID の存在を他人に教えないため)。

method_not_allowed405そのメソッドは使えません

エンドポイントが受け付けるメソッドは Allow ヘッダに入っています。

slug_taken409そのスラグのページはすでにあります

スラグはスペースの中で一意です。別のスラグを指定してください。

version_conflict409他の人が先に更新しました

version を送ると、その版のままのときだけ書き換えます(楽観ロック)。合わないときは読み直してから、もう一度送ってください。

handle_taken409そのハンドルはすでに使われています

ハンドルは大文字・小文字を区別せずに一意です(Apple がいれば apple は使えません)。別のハンドルを指定してください。

part_name_taken409その識別名のパーツはすでにあります

識別名はスペースの中で一意です。別の識別名を指定してください。

parts_quota_exceeded409パーツの合計の大きさが上限に達しました

1 スペースのパーツの中身の合計には上限があります。要らないパーツを消すか、中身を減らしてください。

data_quota_exceeded409データストアの上限に達しました

レコードの数か、スペースの合計サイズが上限に達しています。要らないレコードを消してください。

storage_quota_exceeded409ストレージの容量が足りません

1 スペースのファイルの合計には上限があります。要らないファイルを消すか、小さくしてから送ってください。

payload_too_large413リクエスト本文が大きすぎます

本文は 1MiB まで、ファイルを上げる・書き換えるときだけ 14MiB までです。HTML やファイルそのものの上限は別にあります(validation_failed)。

unsupported_media_type415Content-Type は application/json にしてください

本文は JSON で送ってください。

validation_failed422入力が規則を満たしていません

errors に、どの項目がなぜ通らなかったかが並びます。

rate_limited429呼び出しの回数が上限を超えました

Retry-After ヘッダの秒数だけ待ってから、もう一度呼び出してください。上限は「制限」の節にあります。

internal_error500サーバの内部で問題が起きました

送った内容に問題はありません。少し待ってからもう一度呼び出し、続くようなら detail の照合用の番号を添えて問い合わせてください。

制限

呼び出しの回数

大きさと規則

MCP サーバ

Claude などの AI エージェントから、同じ操作を MCP(Model Context Protocol)で使えます。 REST の API と同じ API キー・同じ規則・同じ回数の上限で動きます。

Claude Code
claude mcp add --transport http framed https://api.framed.dog/mcp \
--header "Authorization: Bearer $FRAMED_API_KEY"
HTTP でつなげるクライアントの設定(例)
{
"mcpServers": {
"framed": {
"type": "http",
"url": "https://api.framed.dog/mcp",
"headers": {
"Authorization": "Bearer framed_sk_…"
}
stdio だけに対応したクライアントの設定(mcp-remote を使う例)
{
"mcpServers": {
"framed": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://api.framed.dog/mcp",

ツール

get_spaceスペースを確かめる読み取りだけ

この API キーで操作できるスペースの名前・URL、このキーで使える範囲(scopes)、ページ・パーツ・データ・ファイルの入力の規則、呼び出し回数の上限を返す。最初に必ず呼ぶこと。範囲の無いツールを呼ぶと insufficient_scope で失敗する。

list_pagesページの一覧読み取りだけ

スペースのフリーページを、スラグの順にすべて返す。本文(HTML)は含まない。

get_pageページを取得読み取りだけ

ページを本文(HTML)ごと返す。

create_pageページを作って公開

スラグと HTML(と種類 kind)でフリーページを作り、https://<subdomain>.framed.dog/<slug> に直ちに公開する。kind は scripted(スクリプトあり・既定)か static(スクリプトなし)。同じスラグがあると slug_taken で失敗する。実行前に利用者に内容を確かめること。

update_pageページを書き換え公開中の内容を変える

公開中のページの slug・kind・html のうち、渡したものだけを書き換える。slug を変えると URL が変わり、古い URL は開けなくなる。実行前に利用者に確かめること。

delete_pageページを削除公開中の内容を変える

公開中のページを削除する。URL は開けなくなり、元に戻せない。実行前に必ず利用者に確かめること。

list_partsパーツの一覧読み取りだけ

スペースのパーツ(ページから組み込む共通のテンプレート)を、識別名の順にすべて返す。中身は含まない。

get_partパーツを取得読み取りだけ

パーツを中身ごと返す。そのパーツを組み込んでいるページとパーツ(used_by)も返すので、識別名を変える前・消す前に影響を確かめられる。

create_partパーツを作る

識別名とテンプレート HTML でパーツを作る。パーツ単独では公開されず、ページやほかのパーツから組み込んで使う。

組み込みは Liquid の標準のタグ: {% render 'name' %}(値を明示して渡す)、{% include 'name' %}(ページの変数をそのまま使う)、{% layout 'name' %}{% block content %}…{% endblock %}(共通の枠にはめる)。

同じ識別名があると part_name_taken、組み込みをたどって元に戻る形(循環)は 422 で失敗する。

update_partパーツを書き換え公開中の内容を変える

パーツの name と html のうち、渡したものだけを書き換える。組み込んでいる公開中のページが直ちに変わる。

識別名を変えると、古い名前で組み込んでいるページはエラーのページになる(組み込み側は書き換わらない)。先に get_part の used_by を見て、組み込み側も直すこと。

実行前に利用者に確かめること。

delete_partパーツを削除公開中の内容を変える

パーツを削除する。組み込んでいたページはエラーのページになる。元に戻せない。先に get_part の used_by で影響を確かめ、実行前に必ず利用者に確かめること。

list_filesストレージのファイルの一覧読み取りだけ

スペースのストレージに上がっているファイルを新しい順に返す。ページの HTML から画像やフォントを参照するための url を知る手段は、これか upload_file の結果だけ(ID は推測できない)。使っている容量も返す。

get_fileストレージのファイルを 1 つ取得読み取りだけ

ファイルの名前・種類・大きさ・URL を返す。中身は返さない(url をそのまま取りに行けば取り出せる)。

upload_fileストレージにファイルを上げる

ファイル名と中身でファイルを上げ、url を返す。url は URL を知っている誰でも開けるので、ページの HTML から画像・フォント・CSS として参照できる。

encoding は、SVG・CSS・JSON・テキストのような文字のファイルなら text(content に中身をそのまま渡す)、画像などは base64。

配信する種類はファイル名の拡張子で決まる(.html や .js はダウンロードとして返り、ページの中では動かない)。同じ名前のファイルがあっても別のファイルとして増える。

1 ファイルと合計の上限は get_space の rules.file にある。容量が足りなければ storage_quota_exceeded。実行前に利用者に確かめること。

update_fileストレージのファイルを書き換え公開中の内容を変える

ファイルの name と中身(content と encoding)のうち、渡したものだけを書き換える。ID と url は変わらないので、参照しているページの HTML は直さなくてよい。

そのファイルを参照している公開中のページが直ちに変わる(見る人のブラウザには、古い中身が最大 5 分残ることがある)。前の中身には戻せない。

名前を変えると、配信する種類も新しい拡張子で決め直す。人が管理画面から上げたファイルも書き換えられるので、実行前に必ず利用者に確かめること。

delete_fileストレージのファイルを削除公開中の内容を変える

ファイルを削除する。url はすぐ開けなくなり、参照しているページでは画像などが表示されなくなる。元に戻せない。人が管理画面から上げたファイルも消せるので、実行前に必ず利用者に確かめること。

list_dataデータストアの一覧読み取りだけ

スペースのデータストアのレコードを、キーの順にすべて返す。値のほかに、値から推論した形(schema)と形の食い違い(warnings)も付く。

get_dataデータストアのレコードを取得読み取りだけ

キーを指定して 1 件返す。書き換えるときは、返ってきた version をそのまま put_data に渡すと、他の人の更新を上書きしない。

put_dataデータストアのレコードを保存公開中の内容を変える

キーを指定してレコードを作る、または丸ごと置き換える。一部だけの更新はできないので、先に get_data で読み、変えたところだけ直した value 全体を渡すこと。

version を渡すと、その版のままのときだけ書き換える(合わなければ version_conflict)。

データはフリーページから読まれて公開される。実行前に利用者に内容を確かめること。

応答の warnings は「形が他の要素と食い違う」という知らせで、保存そのものは通っている。

delete_dataデータストアのレコードを削除公開中の内容を変える

キーを指定してレコードを消す。そのレコードを読んでいるフリーページは、その部分が空になる。元に戻せない。実行前に必ず利用者に確かめること。

OpenAPI

この API は OpenAPI 3.1.0 で定義しています。クライアントの生成や API ツールへの取り込みには、次の文書を使ってください。

https://api.framed.dog/openapi.json