Magic Coderサービス

API の使い方

サーバー間の API です。セッション・Cookie はなく、すべてのリクエストに Authorization: Bearer <token> を付けます。サービス画面の使い方は はじめに をご覧ください。/api/* の応答は、エラーも含めてすべて JSON で、Cache-Control: private, no-store です。

トークン

クライアントはトークンの SHA-256 ハッシュで登録します。サービス画面の「API クライアント」で発行する(発行したアカウントのクライアントになります)か、クライアント側で作ったトークンのハッシュを php artisan api-clients:register {name} {sha256} [--days=N] で登録します。トークンがない・形が違う・未登録・期限切れ・失効済みの場合は 401 {"message": "Unauthenticated."} と WWW-Authenticate: Bearer を返します。

1 クライアントあたり毎分 600 リクエストまでです。応答には X-RateLimit-Limit と X-RateLimit-Remaining が付き、超えると 429 と Retry-After を返します。

納品(コーディングの依頼)

CANVAS と原稿を渡して、HTML/CSS の納品を依頼します。画面で発行したクライアントは、発行したアカウントの納品を扱います(サービス画面で依頼したものも見えます)。コマンドで登録したクライアントは、自分で依頼した納品だけを扱います。生成は裏で進むので、依頼は 202 で受け付け、status が completed か failed になるまで読み直します。

リクエスト内容
GET /api/v1/templates依頼できる媒体(preset。media-contract の媒体 ID)と、CANVAS の枚数・順・制作枠。
GET /api/v1/templates/{preset}その媒体の原稿の記入例(manuscript、原稿の契約 magic://schemas/manuscript/v1 の形)と、その媒体の原稿のスキーマ(manuscript_schema)。記入例の値をすべて書き換えてから送ります。
POST /api/v1/deliveriesmultipart/form-data で manuscript(原稿の JSON)・canvases[](PNG・JPEG・WebP、原稿の構成の面・ページ・スライドの順に 1 枚ずつ。ランディングページは PC、SP の順)・任意の preset(省略すると原稿の media.id)・任意の title。202 と Location。原稿が JSON でない・原稿の契約に合わない・媒体が違う・まだ作れないもの(フォーム・QR コード・添付ファイル)がある・CANVAS の枚数が面・ページの数と違う場合は 422。
GET /api/v1/deliveries納品の一覧(新しい順、20 件ずつ、?page=)。
GET /api/v1/deliveries/{id}状態(queued・generating・completed・failed)、検査の結果 check、error、download_url。
PATCH /api/v1/deliveries/{id}JSON の title だけなら 200。manuscript(JSON のオブジェクトか、その文字列。同じ媒体で同じ面・ページの数)か "generate": true は新しい版として描き直し、202。生成中は 409。
DELETE /api/v1/deliveries/{id}納品と、その CANVAS・納品物を消します(204)。
GET /api/v1/deliveries/{id}/project.zip今の版のプロジェクトフォルダの ZIP。検査に通らなかったときも、最後に書いたものを取れます。まだなければ 404。
curl -sS https://coder.magichtml.dev/api/v1/deliveries \
  -H "Authorization: Bearer $TOKEN" -H 'Accept: application/json' \
  -F preset=banner -F title='灯台珈琲のバナー' \
  -F manuscript=@manuscript.json -F 'canvases[]=@canvas.png'
{
  "data": {
    "id": "01k…", "title": "灯台珈琲のバナー", "preset": "banner", "status": "completed", "revision": 1, "turns": 2,
    "pages": 1, "canvases": [{"position": 1, "label": "バナー", "viewport": null, "width": 960, "height": 800, "sha256": "…"}],
    "manuscript": {"schema_version": 1, "kind": "publication-manuscript", "media": {"id": "banner", "version": 1}, …}, "check": {"valid": true, "checks": {…}, "violations": [], "warnings": []}, "error": null,
    "download_url": "https://coder.magichtml.dev/api/v1/deliveries/01k…/project.zip", "created_at": "…", "started_at": "…", "completed_at": "…"
  }
}

原稿は、原稿の契約(magic://schemas/manuscript/v1)の JSON です。content.items が載せる項目、structure が面・ページ・セクションの構成で、AI は各ページにその構成の部分が指す項目をすべて、原稿の値のまま載せます。納品物の m-field のキーは掲載項目のキー(部分に分けたときは「キー-部分」)で、リンク先は原稿にあるものだけです。画像の項目は CANVAS から切り出します(原稿の src のファイルは使いません)。

依頼できる媒体:landing、html-email、business-card、business-card-vertical、flyer、flyer-a4-landscape、flyer-a3、flyer-a3-landscape、flyer-b5、flyer-b5-landscape、book-cover、slide、slide-4x3、banner、banner-336x280、banner-160x600、banner-300x600、banner-728x90、banner-970x90、banner-970x250、banner-300x50、banner-320x50、banner-320x100、banner-1200x628、banner-1200x1200、banner-900x1600、youtube-thumbnail、x-post、x-profile、x-header、line-rich-message、instagram-story、instagram-post、instagram-carousel、note-cover、ogp

POST /api/v1/projects/check

multipart/form-data の zip に、プロジェクトフォルダの ZIP(アーカイブの直下に project.json)を送ります。合否にかかわらず 200 で、成果物の契約の project:check の結果に、違反ごとの修正指示 repair を足した JSON を返します(php artisan coder:check と同じ)。repair.scope は直す場所で、html(ページの HTML・CSS)、contract(project.json・page.json などの定義)、content(保存された値)、environment(送り方・検査の環境)、folder(フォルダの構成・ファイル名・ファイルの有無)のどれかです。専用の指示がない違反は "repair": null です。ページの HTML で見つけた違反には、そのページ ID(page)が付き、repair.selector にその要素の XPath(結果の anchors から)が入ります。HTML の中の参照(外部の画像など)やスタイルの目印についてのフォルダの違反も、repair.selector にその要素の XPath が入ります。警告(warnings)は違反と同じ形で、"repair": null です。受け付けるのは magic://schemas/project/v2(schema_version: 2)のフォルダだけです。

{
  "path": "site.zip",
  "valid": false,
  "schema": "magic://schemas/project/v2",
  "checks": {"read": "passed", "layout": "failed", ...},
  "violations": [{
    "code": "binding.field", "path": "...", "target": "...", "expected": "...", "actual": "...", "message": "...",
    "page": "pages:<uuid>",
    "repair": {"scope": "html", "candidates": ["summary"], "selector": "/*/*[2]/*/*/*[4]/*[2]", "instruction": "...", "steps": ["..."]}
  }],
  "warnings": [],
  "html_checked_pages": [], "contract_checked_pages": [], "html_compared_pages": [],
  "anchors": {"pages:<uuid>": {"<target>": "<XPath>"}},
  "source_sha256": "..."
}

ZIP は 42.0 MiB まで受け付けます。422 は読める ZIP がなかったことを表します(zip がない、アップロードの失敗、上限超え、ZIP として開けない)。ファイル数や展開後の大きさの上限超え、安全でない名前やパスは 200 の違反として返します。アップロードは検査の間だけ非公開の作業ディレクトリに置き、終わったら消します。

curl -sS https://coder.magichtml.dev/api/v1/projects/check \
  -H "Authorization: Bearer $TOKEN" -H 'Accept: application/json' \
  -F zip=@my-project.zip

POST /api/v1/pages/check

書いている途中のページを 1 枚ずつ検査します。JSON の本文に、ページの html、その page(UUID か pages:UUID)、検査に要るプロジェクトの文脈 project(Project の契約データ)と pages(全ページの定義)を送ります。HTML と値は送られたとおりに検査します(前後の空白も変えません)。合否にかかわらず 200 で、projects/check と同じ形の結果(scope は page、違反ごとに repair か null)を返します(php artisan coder:check-page と同じ)。本文が形に合わなければ 422 です。

curl -sS https://coder.magichtml.dev/api/v1/pages/check \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"project": {...}, "pages": [...], "page": "<uuid>", "html": "<!doctype html>..."}'

GET /api/v1/schemas/project/v2/{file}

プロジェクトフォルダの契約の JSON Schema(draft 2020-12)を application/schema+json で返します。ほかの名前は 404 です。

GET /api/v1/schemas/{names|media}/v{n}

プロジェクトのスキーマが $ref で参照する、media-contract のスキーマです(キー・パス・URL の名前の規則と、媒体の一覧)。スキーマの $id は、magic://schemas/ を https://coder.magichtml.dev/api/v1/schemas/ に置き換えると、ここの URL になります。検証するときは、この対応で $ref を解決してください。

そのほか

GET /up はヘルスチェックです。GET / は Accept: application/json のときサービス名と版を JSON で返します。