API

画面でできることは、すべて /api/v1 でもできます。プロジェクトフォルダを開き、ページを操作で編集し、保存して、フォルダを持ち出します。

認証

  1. ログインして「アカウント」→「APIトークン」でトークンを発行します。トークンは発行した画面で一度だけ表示されます。有効期間は 90 日です。
  2. 要求に Authorization: Bearer <トークン> を付けます。

トークンは発行したアカウントのものです。そのアカウントが開けるプロジェクトだけを扱え、権限(編集できる・見るだけ)もアカウントと同じです。いらなくなったトークンは同じ画面で失効させます。

応答はすべて JSON(フォルダのダウンロードは ZIP)で、Cache-Control: private, no-store です。

export EDITOR=https://editor.magichtml.dev
export TOKEN=...   # 発行したトークン
curl -H "Authorization: Bearer $TOKEN" $EDITOR/api/v1/projects

プロジェクト

要求 すること
GET /api/v1/projects 開けるプロジェクトの一覧
POST /api/v1/projects/folder プロジェクトフォルダか素のサイト(multipart の zip)を開いて、新しいプロジェクトにする(201)
GET /api/v1/projects/{id} 1 つのプロジェクト
PATCH /api/v1/projects/{id} 名前を変える({"title": "..."})。履歴に残り、元に戻せる
DELETE /api/v1/projects/{id} 削除する。オーナーだけ
GET /api/v1/projects/{id}/folder 保存版をプロジェクトフォルダ(ZIP)で持ち出す
GET /api/v1/projects/{id}/folder/{version} 過去の版のフォルダ
POST /api/v1/projects/{id}/folder フォルダで中身を置き換える。1 つの変更として履歴に残る
GET /api/v1/projects/{id}/pages ページの一覧

プロジェクトは次の形で返ります。

{
  "id": "0f8c…",
  "title": "灯台珈琲",
  "media": {"id": "web", "version": 1},
  "medium": "Webサイト",
  "role": "editor",
  "owner": true,
  "pages": 3,
  "revision": 12,
  "created_at": "2026-10-10T09:00:00+00:00",
  "updated_at": "2026-10-10T09:30:00+00:00",
  "url": "/projects/0f8c…",
  "editor_url": "https://editor.magichtml.dev/projects/0f8c…",
  "folder_url": "https://editor.magichtml.dev/api/v1/projects/0f8c…/folder"
}

role は呼び出したアカウントの権限(owner・editor・viewer)で、owner はオーナーのとき true です。editor_url を人に渡すと、そのプロジェクトが編集画面で開きます(その人がメンバーのとき)。

メンバー

要求 すること
GET /api/v1/projects/{id}/members メンバーの一覧(id・name・email・role)。メンバーなら誰でも
PUT /api/v1/projects/{id}/members {"email": "...", "role": "editor"} で、アカウントをメンバーにする、または権限を変える。オーナーだけ
DELETE /api/v1/projects/{id}/members/{user} メンバーを外す。オーナーは誰でも、ほかのメンバーは自分だけ(抜けると応答の members は null)
  • 権限は owner(編集・メンバーの管理・削除)、editor(編集)、viewer(見る・持ち出す)です。
  • メンバーにできるのは、すでにアカウントがある人だけです。ない人は 422 で、アカウントは事務局が発行します。
  • オーナーは 1 人以上必要です。最後のオーナーの権限を下げる・外すと 422 になります。
  • フォルダを開いてプロジェクトを作ったアカウントが、最初のオーナーです。
# 依頼者をオーナーにして、編集画面の URL を渡す
curl -X PUT -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"email": "person@example.com", "role": "owner"}' $EDITOR/api/v1/projects/<id>/members
# フォルダを開く
curl -H "Authorization: Bearer $TOKEN" -F zip=@site.zip $EDITOR/api/v1/projects/folder
# 編集したフォルダを持ち出す
curl -H "Authorization: Bearer $TOKEN" -o edited.zip $EDITOR/api/v1/projects/<id>/folder

フォルダが契約に合わないときは 422 で、どの検査で何が問題だったか(checks・violations)を返します。

project.json がなく index.html がある ZIP は、素のサイトとして Web のプロジェクトフォルダに変換してから開きます(サービス画面の使い方の「手元のサイトを開く」と同じ規則)。応答には site が加わります。

"site": {
  "pages": [{"file": "index.html", "public_url": "/"}, {"file": "about.html", "public_url": "/about/"}],
  "ignored": ["README.md"],
  "warnings": [{"code": "site.link_removed", "path": "index.html", "target": "link[href]", "actual": "favicon.ico", "message": "…"}]
}

サイトを変換できないときは 422 で、checks.site が failed、violations のコードは site.external・site.missing・site.file_type・site.page_url・site.read・site.limit のどれかです。

ページを編集する

編集は「ロックを取る → 下書きを開く → 操作する → 保存する → ロックを手放す」の順に行います。

PUT    /api/v1/projects/{id}/lock          ロックを取る(60 秒以内に繰り返して保つ)
GET    /api/v1/pages/{page}/draft          下書きを開く(draft_token を受け取る)
POST   /api/v1/pages/{page}/operations     操作する(新しい draft_token が返る)
POST   /api/v1/pages/{page}/draft/undo     サーバー側で 1 つ戻す(redo でやり直す)
PUT    /api/v1/pages/{page}/operations     保存する(ここで検査する)
DELETE /api/v1/projects/{id}/lock          ロックを手放す
  • 書き込みの要求には X-Editor-Client: <クライアントID> を付けます。クライアント ID は、実行ごとに作る英数字と ._:- の 64 文字までの文字列(UUID でかまいません)です。
  • ほかのクライアントがロックを持っているあいだ、書き込みは 423 Locked になります。応答の lock に、相手の種類と期限が入っています。
  • ロックを取るときは {"kind": "agent", "label": "my-script"} を送ります。label は、締め出された画面に「編集中」として表示されます。

操作

POST /api/v1/pages/{page}/operations に、直前の draft_token と操作を 1 つ送ります。

{
  "draft_token": "…",
  "operation": {"kind": "replace", "target": "#hero-title", "html": "<h1 id=\"hero-title\">春の新メニュー</h1>"},
  "outline": true
}
kind すること 必要な項目
insert 要素を足す parent、html、任意で before
move 要素を動かす target、parent、任意で before
remove 要素を消す target
duplicate 要素を複製する target
replace 要素を置き換える target、html
swap 要素を入れ替える target、html

要素は id(#hero-title)か、下書きのアウトラインでの位置(@12)で指定します。"outline": true を付けると、応答の outline に <body> の中の要素が順に並びます(node・tag・id・classes・先頭の text)。@n はその下書きでの番号なので、同じ下書きの draft_token と一緒に使ってください。

操作は検査せずに進みます(速く返すため)。検査は保存のときに一度だけ行います。

保存

PUT /api/v1/pages/{page}/operations に draft_token と、要求ごとに作る commit_id(UUID)を送ります。検査に通ると保存版になり、履歴に 1 つ積まれます。通らないと 422 で、violations に理由が入ります。保存版がほかで変わっていたときは 409 です。

確定した版

人が編集画面で「確定」した版を読み出します。保存(自動保存を含む)とは別です。連携先は、確定した版だけを取り込んでください。

要求 すること
GET /api/v1/projects/{id}/confirmations/latest 最新の確定。まだなければ 404
GET /api/v1/projects/{id}/confirmations 確定の一覧(新しい順。per_page、next_page_url でページ送り)
GET /api/v1/projects/{id}/folder/{version} その版の ZIP。X-Content-SHA256 と ETag に ZIP の SHA-256 が入る
{
  "id": "9b1f…",
  "version": 42,
  "revision": 12,
  "sha256": "3a7c…",
  "note": null,
  "confirmed_at": "2026-10-10T10:00:00+00:00",
  "confirmed_by": {"name": "山田"},
  "download_url": "https://editor.magichtml.dev/api/v1/projects/0f8c…/folder/42",
  "has_unconfirmed_changes": false
}
  • 取り込む側は、download_url から ZIP を取り、sha256 と照らし合わせます。同じ sha256 は取り込み済みとして扱えます。
  • has_unconfirmed_changes は、確定のあとに確定と違う状態が保存されているかどうかです。
  • 確定(POST /api/v1/projects/{id}/confirmations)は、編集画面から人が行う操作です。API トークンでは 403 になります。

履歴

要求 すること
GET /api/v1/projects/{id}/history 履歴と、戻せるか・やり直せるか
PUT /api/v1/projects/{id}/history {"direction": "undo"} または "redo" と、今の revision を送って、保存版を戻す・やり直す

エラー

状態 意味
401 トークンがない、期限切れ、または失効した
403 見るだけの権限で書き込もうとした、またはオーナーでないのにメンバーを変えた・削除しようとした
404 プロジェクトやページがない、または開く権限がない
409 ほかで保存されて、手元の版が古い
422 入力や契約の違反。errors か violations に理由が入る
423 ほかのクライアントが編集中(ロックを持っている)
429 要求が多すぎる。少し待ってからやり直す