コンテンツにスキップ

HTTP API リファレンス

CLI / MCP / GitHub Action を使わず、HTTP で直接 Miseru を操作するためのリファレンスです。ベース URL は https://api.miseru.dev です。

公開 API はすべて Upload Token で認証します。

Authorization: Bearer msr_••••••••
  • トークン形式は msr_ + 64桁の16進文字列(256bit)です
  • トークンはダッシュボードでワークスペース(org)単位に発行します(平文が見られるのは発行時の一度きり。サーバーには SHA-256 ハッシュのみ保存されます)
  • アップロード・失効・コメント取得はどのトークンでも行えます。差分の承認とベースラインの削除には、ベースラインの権限が必要です(既存トークンは権限あり、新規発行も既定は権限あり)
  • 失効(revoke)済みトークンは即座に使えなくなります

認証に失敗した場合はすべて 401 { "error": { "code": "UNAUTHORIZED", "message": "unauthorized", "status": 401 } } を返します。


画像をアップロードし、署名付き配信 URL と貼り付け用 markdown を返します。

画像は配信コンテキストという単位にまとめられ、その単位で失効します。コンテキストは初回アップロード時に自動作成されます(find-or-create)。

multipart/form-data で送信します。

フィールド 必須 型・制約
image ✅ 画像ファイル。5MB 未満、image/png / image/jpeg / image/webp のみ
name ✅ スクリーンショット名。1〜200文字。同じコンテキストで name と viewport が同じ画像をアップロードすると、古い画像は失効します(置き換え)
context_key ✅ コンテキストのキー。1〜200文字。使える文字は A-Z a-z 0-9 . _ - / : #。context_type=github_pr では owner/repo#<pr number> 形式。旧 <pr number> 形式は 400
context_type — github_pr / manual。既定は manual。レジストリに無い値は 400
context_label — 人間向けの表示名。1〜200文字
commit_sha — コミット SHA。16進 7〜40 桁。GitHub 文脈の証跡なので任意
viewport — ビューポートサイズ。幅x高さ 形式(例: 1280x800)
tag — タグ名。複数指定可(同名フィールドを繰り返す)。1画像につき最大20個
project — プロジェクト名。ワークスペース内に無ければ作成し、新しいコンテキストを紐づける
compare — true / false。true でベースラインとの比較(Kurabe)を頼む。省略と空文字は false。それ以外の値は 400。ワークスペースの比較モードが有効なら、省略しても比較する。比較できるのは PNG だけです。PNG 以外の画像に true を付けると 400 になり、何も保存しません。比較モードが有効でも、PNG 以外の画像は比較しません(diff_requested: false)
max_diff_ratio — 「変化なし」とみなす差分率の上限。0〜1 の比率(例: 0.001 = 0.1%)。比較の設定が無い場合、省略と空文字は 0。正規表現 ^\d+(\.\d+)?([eE]-?\d+)?$ に合い、Number() で読んだ値が 0 以上 1 以下(例: 0・0.001・1・1e-3)を受け付けます。それ以外(abc・-0.1・1.5・2e0・Infinity・NaN・0x1・.5・空白だけ・前後に空白がある値)は 400(message: max_diff_ratio must be a number between 0 and 1)。比較を頼まないアップロードでも保存します。承認の権限の無いトークンで 0 より大きい値を送ると 403。省くと、その論理名の比較の設定が使われます
mask — 比較から外す矩形。x,y,width,height の画像の画素の座標(CSS の px ではありません)。任意・同名項目の繰り返しで最大 20 個。比較の設定が無い場合、省略はマスクなし。正規表現 ^\d{1,5},\d{1,5},\d{1,5},\d{1,5}$ に合い、width・height が 1 以上の値を受け付けます(0,0,1,1・99999,99999,99999,99999、先頭ゼロの 007,0,1,1 は 7 として読みます)。それ以外(空文字・負の数・小数・6 桁以上・要素が 4 つでない・中や前後の空白・幅か高さが 0)は 400(message: mask must be x,y,width,height (non-negative integers, width and height >= 1))。21 個以上は 400(message: too many masks (max 20))。座標は比べる 2 枚の大きい方の画面上で読み、はみ出した部分は切り捨てます。全部が画面外なら何も外しません。比較を頼まないアップロードでも保存します。承認の権限の無いトークンで指定すると 403。省くと、その論理名の比較の設定が使われます
ターミナルウィンドウ
curl -X POST https://api.miseru.dev/v1/upload \
-H "Authorization: Bearer $MISERU_TOKEN" \
-F "image=@./shot.png" \
-F "name=home" \
-F "context_type=github_pr" \
-F "context_key=elchika-inc/miseru#482" \
-F "commit_sha=$(git rev-parse HEAD)" \
-F "viewport=1280x800" \
-F "tag=review" \
-F "tag=desktop" \
-F "project=website"
{
"id": "3f2a…(32桁hex)",
"view_url": "https://img.miseru.dev/i/<id>.png?exp=<unixtime>&token=<kid>.<hmac>",
"markdown": "![home](https://img.miseru.dev/i/<id>.png?exp=…&token=…)",
"tags": ["review", "desktop"],
"replaced": 0,
"diff_requested": false
}
  • view_url の署名有効期限(exp)はアップロードから365日です。実際の公開停止は失効ポリシーによる削除で行われるため、URL の exp より先にポリシーが優先されます(→ 配信とセキュリティの仕組み)
  • markdown をそのまま PR コメントに貼れば画像が表示されます
  • replaced は、このアップロードで置き換えて失効させた古い画像の件数です。置き換えられた画像の配信 URL は、キャッシュの残存(最長 5 分)の後に 404 になります
  • diff_requested は、比較のキューに入れたかどうかです。true なら GET /v1/images/:id/diff で結果を取れます。キューへの投入に失敗したときは、アップロード自体は成功のまま false になります

API と画像配信の HTTP エラーは、次の共通形式で返します。message は表示用の文言、status は HTTP ステータスと同じ数値です。code は下表のとおり HTTP ステータスから一意に決まり、ルートごとの固有コードではありません。

{ "error": { "code": "NOT_FOUND", "message": "not found", "status": 404 } }

login や missing などの追加情報は error と同階層に残ります。Retry-After や Cache-Control などの応答ヘッダーも維持します。

HTTP ステータス code
400 INVALID_INPUT
401 UNAUTHORIZED
403 FORBIDDEN
404 NOT_FOUND
409 CONFLICT
410 GONE
413 PAYLOAD_TOO_LARGE
415 UNSUPPORTED_MEDIA_TYPE
422 UNPROCESSABLE_CONTENT
429 RATE_LIMITED
500 INTERNAL
502 UPSTREAM_FAILED
503 SERVICE_UNAVAILABLE
ステータス ボディ 条件
400 { "error": { "code": "INVALID_INPUT", "message": "invalid multipart/form-data body", "status": 400 } } multipart として解釈できない
400 { "error": { "code": "INVALID_INPUT", "message": "image file required", "status": 400 } } image フィールド欠落
400 { "error": { "code": "INVALID_INPUT", "message": "name required", "status": 400 } } name が欠落・空
400 { "error": { "code": "INVALID_INPUT", "message": "invalid name", "status": 400 } } name が200文字超
400 { "error": { "code": "INVALID_INPUT", "message": "context_key required", "status": 400 } } context_key が空
400 { "error": { "code": "INVALID_INPUT", "message": "context_key for github_pr must be owner/repo#<pr number>", "status": 400 } } github_pr の context_key が旧形式(番号のみ)または形式不正
400 { "error": { "code": "INVALID_INPUT", "message": "unknown context_type: …", "status": 400 } } レジストリに無い context_type
409 { "error": { "code": "CONFLICT", "message": "context is closed", "status": 409 } } 閉じたコンテキストへ追加しようとした
400 { "error": { "code": "INVALID_INPUT", "message": "commit_sha must be a hex sha (7-40 chars)", "status": 400 } } commit_sha が 16進 7〜40 桁でない
400 { "error": { "code": "INVALID_INPUT", "message": "viewport must be like 1280x800", "status": 400 } } viewport の形式不正
400 { "error": { "code": "INVALID_INPUT", "message": "invalid tag", "status": 400 } } タグ名が空・制御文字を含む・50文字超
400 { "error": { "code": "INVALID_INPUT", "message": "too many tags (max 20)", "status": 400 } } タグが20個を超える
400 { "error": { "code": "INVALID_INPUT", "message": "invalid project", "status": 400 } } プロジェクト名が空・制御文字を含む・100文字超
401 { "error": { "code": "UNAUTHORIZED", "message": "unauthorized", "status": 401 } } トークン無効・欠落
403 { "error": { "code": "FORBIDDEN", "message": "this token cannot set max_diff_ratio or mask; push without them, or use a token that can manage baselines", "status": 403 } } 承認の権限の無いトークンで max_diff_ratio が 0 より大きいか mask がある(比較を頼まない場合も同じ)
413 { "error": { "code": "PAYLOAD_TOO_LARGE", "message": "payload too large (max 5MB)", "status": 413 } } 画像が 5MB 以上
415 { "error": { "code": "UNSUPPORTED_MEDIA_TYPE", "message": "unsupported content-type (png/jpeg/webp)", "status": 415 } } 対応外の画像形式
415 { "error": { "code": "UNSUPPORTED_MEDIA_TYPE", "message": "declared <content-type>, but the file content is not a valid <PNG|JPEG|WEBP>", "status": 415 } } 宣言した Content-Type とファイルの中身(先頭バイト)が一致しない。文言は宣言された型によって変わる
429 { "error": { "code": "RATE_LIMITED", "message": "rate limited", "status": 429 } } + Retry-After: 60 レート制限超過(ワークスペースあたり 1 分 120 回が目安。Cloudflare の拠点ごとに数えるため厳密な上限ではない)

レート制限のカウンタ基盤に障害が起きた場合、アップロードは拒否されません(fail-open)。


POST /v1/images/:id/pause / POST /v1/images/:id/resume

Section titled “POST /v1/images/:id/pause / POST /v1/images/:id/resume”

画像の配信を停止・再開します。停止中は配信 URL が 404 になりますが実体は残り、再開すると同じ URL がそのまま復活します。消してよいことが確定するまでの一時的な非表示に使ってください。完全に消す場合は DELETE /v1/images/:id を使います。

ターミナルウィンドウ
curl -X POST https://api.miseru.dev/v1/images/<id>/pause \
-H "Authorization: Bearer $MISERU_TOKEN"
curl -X POST https://api.miseru.dev/v1/images/<id>/resume \
-H "Authorization: Bearer $MISERU_TOKEN"
  • どちらも冪等です。既にその状態なら changed: false で 200 を返します
  • 停止しても retain_days の期限は進みます(停止は「永久保持」ではありません)。停止中の画像もクォータに数えます
  • 貼り先(GitHub Camo など)のキャッシュは配信の Cache-Control: max-age=300 により最長 5 分で切れます
  • トークンの org に属さない画像 ID は、存在有無を漏らさないため一律 404 を返します
ステータス ボディ 条件
200 { "ok": true, "id": "<id>", "paused": true, "changed": true } 停止した(resume は paused: false)
401 { "error": { "code": "UNAUTHORIZED", "message": "unauthorized", "status": 401 } } トークン無効
404 { "error": { "code": "NOT_FOUND", "message": "not found", "status": 404 } } 画像が存在しない(または別 org)

画像を1件、即座に失効させます。失効ポリシーに関わらず、いつでも手動失効できます。

ターミナルウィンドウ
curl -X DELETE https://api.miseru.dev/v1/images/<id> \
-H "Authorization: Bearer $MISERU_TOKEN"
  • 失効は「オリジン(R2)削除 → 記録削除」の順で実行されます(キャッシュ済みの配信は Cache-Control の max-age=300 により最長 5 分で切れます)
  • トークンの org に属さない画像 ID は、存在有無を漏らさないため一律 404 を返します
ステータス ボディ 条件
200 { "ok": true, "id": "<id>" } 失効完了
401 { "error": { "code": "UNAUTHORIZED", "message": "unauthorized", "status": 401 } } トークン無効
404 { "error": { "code": "NOT_FOUND", "message": "not found", "status": 404 } } 画像が存在しない(または別 org)
503 { "error": { "code": "SERVICE_UNAVAILABLE", "message": "expire failed, retry later", "status": 503 } } 失効処理が完了しなかった(後述の自動リトライ対象)

503 の場合でも画像は失効予約済みとしてマークされ、毎時の自動スイープが失効を完了させます。

画像の比較(Kurabe)の状態を返します。比較は、アップロードに compare=true を付けたとき(またはワークスペースの比較モードが有効なとき)に行われます。比較はキューで非同期に処理され、通常は数秒で終わります。

ターミナルウィンドウ
curl https://api.miseru.dev/v1/images/<id>/diff \
-H "Authorization: Bearer $MISERU_TOKEN"

state によって返す項目が変わります。

state 意味 ほかの項目
not_requested 比較を頼まれていない —
processing 計算中 requested_at(比較を頼んだ時刻。UNIX 秒)
baseline_created 同じ論理名(name + viewport)のベースラインが無かったので、この画像をベースラインとして登録した baseline_id・baseline_scope
compared ベースラインと比べた diff_id・diff_ratio(0〜1)・max_diff_ratio(アップロード時に保存した許容幅。push の指定を優先し、省略時は比較の設定、どちらも無ければ 0)・changed(diff_ratio が max_diff_ratio より大きいか)・status(pending / approved)・stale・baseline_scope
failed 比較できなかった diff_id・failure_reason(unsupported_format = PNG 以外、decode = 画像を読めない・大きすぎる、r2_missing / not_found = 画像が見つからない、retry_exhausted = 一時的な障害で再試行を使い切った(比較つきで push し直す)、invalid_mask = 保存されたマスクを読めない)
{
"image_id": "3f2a…",
"state": "compared",
"diff_id": "9c1e…",
"diff_ratio": 0.0132,
"max_diff_ratio": 0,
"changed": true,
"status": "pending",
"stale": false,
"baseline_scope": "context"
}
  • 比較できるのは PNG だけです
  • baseline_scope は context(その PR のベースライン)/ shared(共有)/ null(行が無い)です。compared は差分が参照する行、baseline_created は登録先の層から決めます。PR の登録先が消えた場合は共有へ読み替えず baseline_id: null・baseline_scope: null です
  • stale が true の未承認の差分は、現在の比較先と行または版が一致しません(承認・削除・共有への反映など)。比較つきで push し直してください。閉じた PR の差分は stale が false でも承認できません(409 context is closed)。既に承認した差分の再承認は、閉じた後も何もせず 200 を返します
  • 画像が存在しない・トークンのワークスペースに属さない場合は 404 です

差分の画像を PNG で返します。kind は baseline(比較した時点のベースライン)/ candidate(比べた画像)/ diff(差分画像。変化した画素を赤、サイズの違う領域を青、マスクの中を灰色で塗る)です。

ターミナルウィンドウ
curl -o diff.png https://api.miseru.dev/v1/diffs/<diff_id>/images/diff \
-H "Authorization: Bearer $MISERU_TOKEN"

応答は Cache-Control: private, no-store です。ベースラインと差分画像に公開配信の URL はありません。

ステータス 条件
200 PNG のバイト列
404 差分が存在しない・別のワークスペース・kind が不正。比べた画像が失効すると、差分も消えます
409 比較に失敗した差分
410 画像がもう無い(baseline は、比較した後にベースラインが置き換えられたとき。承認済みの差分もこれに当たります)

差分を承認し、比べたベースラインを候補の画像に置き換えます。取り消せません。PR(github_pr)の差分はその PR のベースラインを置き換え、無ければ新しく作ります(共有はまだ変えません)。merged で閉じたときに共有へ反映します。manual の差分は共有を置き換えます。置き換えた層の古い実体は削除されます。同じ差分をもう一度承認しても、何もせず 200 を返します。

ターミナルウィンドウ
curl -X POST https://api.miseru.dev/v1/diffs/<diff_id>/approve \
-H "Authorization: Bearer $MISERU_TOKEN"
ステータス ボディ 条件
200 { "diff_id": "<id>", "status": "approved", "baseline": { "id": "<baseline_id>", "source_image_id": "<image_id>", "scope": "context" } } 承認した(承認済みだった)
403 { "error": { "code": "FORBIDDEN", "message": "this token cannot approve diffs; ask someone whose token can manage baselines to run: miseru approve <id>", "status": 403 } } ベースラインの権限が無い(差分の存在確認より先に判定)
404 { "error": { "code": "NOT_FOUND", … } } 差分が存在しない・別のワークスペース
409 { "error": { "code": "CONFLICT", … } } 比較に失敗した差分、閉じた PR の未承認の差分、または比較した後にベースラインが更新された(比較つきで push し直す)
410 { "error": { "code": "GONE", … } } 比べた画像の実体が無い
  • baseline.scope は context / shared / null です。承認済みの応答も現在参照する行の層を返し、行が無ければ baseline 自体が null です
  • 承認には、そのワークスペースのベースラインの権限を持つ Upload Token が必要です。403 のときは message のとおり人に承認を頼んでください。誰が承認したかはトークン単位で記録されます
  • 比較(Kurabe)は β の間、無料で使えます

ワークスペースの Kurabe のベースライン(比べる基準の画像)を、新しい順に返します。

ターミナルウィンドウ
curl "https://api.miseru.dev/v1/baselines?limit=100" \
-H "Authorization: Bearer $MISERU_TOKEN"
クエリ 説明
project プロジェクトの id で絞り込む。none ならプロジェクトに属さないものだけ。省略すると全部
limit 1〜200 の整数(既定 100)。範囲外は 400
cursor 前の応答の next_cursor。続きを取るときに渡す
{
"baselines": [
{
"id": "<baseline_id>",
"project_id": null,
"context_id": null,
"name": "home",
"viewport": "1280x800",
"size_bytes": 48213,
"created_at": 1792390000,
"source_image_id": "<image_id>"
}
],
"next_cursor": null
}
  • created_at は最初に登録された時刻です。承認で中身が置き換わっても変わりません
  • source_image_id は、今のベースラインの元になった画像の id です
  • context_id は、PR ごとのベースラインならそのコンテキストの id、共有なら null です。一覧には両方の層が混ざります

ベースラインを 1 件削除します。共有・PR ごとのどちらの層も削除できます。取り消せません。削除したぶん、ストレージ上限の使用量が減ります。

ターミナルウィンドウ
curl -X DELETE https://api.miseru.dev/v1/baselines/<baseline_id> \
-H "Authorization: Bearer $MISERU_TOKEN"
  • 削除の後に同じ名前・同じ viewport の画像を比較つきで push すると、その PR のベースラインがあればそれと、無ければ共有と比べます。どちらも無ければ新しく登録します(PR の push なら PR のベースライン、manual なら共有)
  • そのベースラインと比べた差分は残りますが、承認できなくなります(比較つきで push し直してください)
  • 削除には、そのワークスペースのベースラインの権限を持つ Upload Token が必要です。403 のときは message のとおり人に削除を頼んでください
ステータス ボディ 条件
200 { "ok": true, "id": "<baseline_id>" } 削除した
401 { "error": { "code": "UNAUTHORIZED", … } } トークン無効
403 { "error": { "code": "FORBIDDEN", "message": "this token cannot delete baselines; ask someone whose token can manage baselines to run: miseru baselines delete <id>", "status": 403 } } ベースラインの権限が無い(ベースラインの存在確認より先に判定)
404 { "error": { "code": "NOT_FOUND", … } } ベースラインが存在しない(または別のワークスペース)
409 { "error": { "code": "CONFLICT", … } } 削除の途中で、承認によるベースラインの置き換えが続いた。もう一度削除してください
503 { "error": { "code": "SERVICE_UNAVAILABLE", … } } 削除が完了しなかった。もう一度削除してください

Upload Token のワークスペースにある比較の設定を全件返します。承認の権限は不要です。直下(project: null)を先に、プロジェクト名・論理名・viewport(null が先)の順に並べます。ページ分けはありません。他のワークスペースの設定は含みません。認証が無い・無効な場合は 401 です。

{
"settings": [
{ "id": "…", "project_id": null, "project": null, "name": "home", "viewport": "1280x800", "max_diff_ratio": 0.001, "masks": [{ "x": 0, "y": 0, "width": 1280, "height": 40 }], "updated_by": "…", "updated_at": 1792939000 }
]
}

project はプロジェクトの名前、project_id はその id(直下は両方 null)です。max_diff_ratio と masks の null は未設定を表します。updated_by は最後に設定を置いたトークンの id、updated_at は更新時刻(Unix 秒)です。保存したマスクが読めない行は、masks: null で返し、設定の id を含む compare settings masks unreadable をログに残します。許容幅はそのまま返します。

論理名ごとの比較の設定を作るか、同じワークスペース・プロジェクト・論理名・viewport の設定を置き換えます。承認の権限(can_manage_baselines)がある Upload Token が必要です。ベースラインを消しても比較の設定は残ります。

本文は JSON のオブジェクトで送ります。

項目 必須 型・制約
name ✅ アップロードと同じ論理名。1〜200文字で制御文字を含めない。未指定・空文字は name required、規則違反は invalid name
viewport — 1280x800 のような1〜5桁の数字を x で区切った文字列。省略・null は viewport なし。空文字や数などは viewport must be like 1280x800
project — プロジェクト名。前後の空白を除いて1〜100文字、制御文字不可。省略はトークンの既定プロジェクト、null はワークスペース直下。型や形式の違反は invalid project。名前が無ければ 404(作成しない)
max_diff_ratio — 0〜1 の JSON の数。省略・null は未設定。文字列や範囲外は max_diff_ratio must be a number between 0 and 1
masks — { x, y, width, height } の配列、最大20個。各値は0〜99999の整数で幅・高さは1以上、数の形の文字列と余分なキーは不可。省略・null・空配列は未設定。型や値の違反は mask must be x,y,width,height (non-negative integers, width and height >= 1)、21個以上は too many masks (max 20)

部分更新ではありません。許容幅だけ指定すると以前のマスクが消え、マスクだけ指定すると以前の許容幅が消えます。同じキーへ再度置いても id と作成時刻は変わらず、並行の PUT は後のものが勝ちます。

設定は、そのプロジェクト・論理名・viewport の画像をアップロードするときに写します(比較を頼まない push とダッシュボードのアップロードも含む)。push で明示した値は項目ごとに設定より優先され、設定自体は変えません。権限の無いトークンは許容幅の 0 だけ指定でき、マスクを省くと設定のマスクを使います。push でマスクを「無し」にはできないため、外すには設定を置き換えるか削除します。

設定を変えても、すでにアップロードした画像の値は変わりません。変化ありのまま残った画像は、比較つきで push し直してください。

{ "name": "home", "viewport": "1280x800", "project": null, "max_diff_ratio": 0.001, "masks": [{ "x": 0, "y": 0, "width": 1280, "height": 40 }] }

200 で置いた1件(GET の要素と同じ形)を返します。

{ "id": "…", "project_id": null, "project": null, "name": "home", "viewport": "1280x800", "max_diff_ratio": 0.001, "masks": [{ "x": 0, "y": 0, "width": 1280, "height": 40 }], "updated_by": "…", "updated_at": 1792939000 }
ステータス message 条件
400 json body required 壊れた JSON、配列・null・文字列など、本文がオブジェクトでない
400 上の表の文 各項目の型・値が不正
400 max_diff_ratio or masks required 許容幅とマスクが両方未設定
401 unauthorized 認証が無い・無効
403 this token cannot change compare settings; ask someone whose token can manage baselines 承認の権限が無い
404 project not found プロジェクトが無い、または別のワークスペース

認証後、本文がオブジェクトか → name → viewport → project の形 → 許容幅 → masks(配列・個数・要素)→ 両方未設定か → 権限 → プロジェクトの存在の順で検証し、最初のエラーを返します。エラー本文は共通の { "error": { "code": "…", "message": "…", "status": 400 } } の形です(status は実際のステータス)。

プロジェクトを削除すると、その設定は直下へ移ります。直下に同じ論理名・viewport の設定があれば、直下の設定を残してプロジェクトの設定を削除します。

承認の権限がある Upload Token で設定を1件消します。200 で { "ok": true, "id": "…" } を返します。削除後の push は、明示した値を使うか、設定が無ければ許容幅0・マスクなしになります。

ステータス message 条件
401 unauthorized 認証が無い・無効
403 this token cannot change compare settings; ask someone whose token can manage baselines 承認の権限が無い。id の存在確認より先に判定
404 not found id が無い、または別のワークスペース

PR の全スクリーンショットを集約した、1 PR = 1 コメント用の markdown を返します。GitHub Action がコメントの投稿・更新に使う API ですが、独自のコメント連携を組む場合にも利用できます。

ターミナルウィンドウ
curl "https://api.miseru.dev/v1/comment?repo=elchika-inc%2Fmiseru&pr_number=482" \
-H "Authorization: Bearer $MISERU_TOKEN"
クエリ 必須 型・制約
repo ✅ GitHub リポジトリ。owner/repo 形式
pr_number ✅ 正の整数
{ "repo": "elchika-inc/miseru", "pr_number": 482, "markdown": "<!-- miseru:pr-482 -->\n### 📸 スクリーンショット(3枚)\n…" }
  • 先頭の HTML コメント <!-- miseru:pr-<n> --> はマーカーです。既存コメントをこのマーカーで探して更新することで「1 PR = 1 コメント」を保ちます
  • 画像は投稿順に並び、各エントリに名前・viewport・コミット SHA(7桁)が付きます
  • pr_number が正の整数でない場合は 400 { "error": { "code": "INVALID_INPUT", "message": "pr_number required", "status": 400 } }
  • repo が欠落または owner/repo 形式でない場合は 400 { "error": { "code": "INVALID_INPUT", "message": "repo required (owner/repo)", "status": 400 } }
  • まだ画像が 1 枚も無い PR でも 200 を返します(0 枚のコメント本文)。Action を落とさないためです

配信コンテキストの画像一覧を、宛先に依存しない JSON で返します。GitHub markdown 以外の形式で貼りたいときはこちらを使ってください。

key に / を含む場合は、そのままパスに書けます。# は URL フラグメントになるため、必ず %23 にパーセントエンコードしてください。encodeURIComponent(key) を使うと / も %2F になりますが、どちらの表記でも同じコンテキストに解決されます。

ターミナルウィンドウ
curl "https://api.miseru.dev/v1/contexts/github_pr/elchika-inc/miseru%23482/manifest" \
-H "Authorization: Bearer $MISERU_TOKEN"
ターミナルウィンドウ
curl "https://api.miseru.dev/v1/contexts/manual/design/review/manifest" \
-H "Authorization: Bearer $MISERU_TOKEN"
{
"context": { "type": "github_pr", "key": "elchika-inc/miseru#482", "label": null, "state": "open" },
"images": [
{
"id": "…",
"name": "home",
"viewport": "1280x800",
"commit_sha": "abc1234",
"url": "https://img.miseru.dev/i/….png?exp=…&token=…",
"created_at": 1786880000
}
]
}
  • レジストリに無い type は 400、コンテキストが存在しなければ 404
  • 他プロジェクトのコンテキストは存在を漏らさず 404

コンテキストの比較の判定を返します。Upload Token の認証は必要ですが、ベースラインの承認権限は不要です。読むだけの冪等な API です。key のエンコードは manifest と同じで、# は %23 にします。

ターミナルウィンドウ
curl "https://api.miseru.dev/v1/contexts/github_pr/elchika-inc/miseru%23482/check" \
-H "Authorization: Bearer $MISERU_TOKEN"

画像ごとの result は次の規則で決まります。比較済みで changed: false なら、承認の状態や stale より先に unchanged を採用します。

result 条件
unchanged 比較済みで変化なし
new 初回の画像をベースラインに登録した
approved 変化ありで承認済み
changed 変化ありで未承認、今も承認できる
stale 変化ありで未承認だが、ベースラインが更新・削除されて承認できない
failed 比較に失敗した、または変化ありで未知の承認状態
uncompared 比較を頼んでいないが、同じ論理名のベースラインがある
processing 比較を計算中
not_compared 比較を頼んでおらず、ベースラインも無い(比較の対象に数えない)

コンテキスト全体の verdict は次の順です。

  1. 閉じたコンテキスト、または changed・stale・failed・uncompared が 1 枚以上なら fail。
  2. それ以外で processing があれば pending。
  3. それ以外は pass。比較の対象が 0 枚でも通ります。

停止中の画像も含めます。images は manifest と同じ順で、各 diff は画像の diff API と同じ状態です。counts は 0 件のものを含む 9 種類すべてを返します。

{
"context": { "type": "github_pr", "key": "elchika-inc/miseru#482", "label": null, "state": "open" },
"verdict": "fail",
"counts": { "unchanged": 0, "new": 0, "approved": 0, "changed": 1, "stale": 0, "failed": 0, "uncompared": 0, "processing": 0, "not_compared": 0 },
"images": [
{
"image_id": "…",
"name": "home",
"viewport": "1280x800",
"commit_sha": "abc1234",
"result": "changed",
"diff": { "state": "compared", "diff_id": "…", "diff_ratio": 0.032, "max_diff_ratio": 0, "changed": true, "status": "pending", "stale": false, "baseline_scope": "context" }
}
]
}
ステータス code 条件・message
401 UNAUTHORIZED トークン無効・欠落
400 INVALID_INPUT 不正な type などのパス入力
404 NOT_FOUND コンテキストが無い、または別のワークスペース
422 UNPROCESSABLE_CONTENT 画像が 200 枚を超える。too many images to check (max 200)。不要な画像を失効させて減らしてください

PR コメントにも画像ごとの比較状態と全体の判定が出ます。比較の対象が 0 枚なら比較の行は出しません。判定の取得失敗・200 枚超過時には、コメントは比較状態の無い従来の本文になります。承認後のコメント更新には Action の再実行が必要です。

配信コンテキストを閉じ、その失効ポリシーを適用します。通常は GitHub Action が pull_request: [closed] を契機に呼び出します。

冪等です。既に閉じている場合も 200 を返し、marked / expired は 0 になります(Action の再実行や at-least-once 配信に耐えます)。

ターミナルウィンドウ
curl -X POST https://api.miseru.dev/v1/contexts/close \
-H "Authorization: Bearer $MISERU_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "context_type": "github_pr", "context_key": "elchika-inc/miseru#482", "reason": "merged" }'
フィールド 必須 意味
context_type ✅ github_pr / manual
context_key ✅ コンテキストのキー
reason — キーが無いか null なら manual。merged / closed / manual の文字列ならその値。それ以外(空文字・未知の文字列・数値・真偽値・配列・オブジェクト)は 400

不正な reason の応答は { "error": { "code": "INVALID_INPUT", "message": "reason must be one of merged, closed, manual", "status": 400 } } で、コンテキストを閉じません。

失効ポリシーに関わらず、merged なら PR のベースラインを共有へ上げ、closed・manual(既定)なら捨てます。close の中では50行まで処理し、残りや失敗した分は毎時の Cron が500行まで拾います。この処理の失敗で close の応答は変わりません。reason: merged は呼び手の申告で、GitHub のマージ状態は検証しません。

{
"context": { "type": "github_pr", "key": "elchika-inc/miseru#482", "state": "closed", "closed_at": 1786884000, "close_reason": "merged" },
"policy": "on_close",
"marked": 1,
"expired": 3
}
フィールド 意味
policy 適用された有効ポリシー(コンテキスト優先・プロジェクト継承)
marked 状態を open → closed に変えたなら 1、既に閉じていれば 0
expired 実際に失効(削除)した画像数。on_close 以外のポリシーでは常に 0

コンテキストが存在しない場合は 404。ポリシーごとの挙動の違いは失効ポリシーの選び方を参照してください。

閉じた配信コンテキストを open に戻し、新しい画像をアップロードできるようにします。Upload Token で認証し、そのトークンの org に属するコンテキストを対象にします。

冪等です。既に open の場合も 200 を返し、reopened は 0 になります。

ターミナルウィンドウ
curl -X POST https://api.miseru.dev/v1/contexts/reopen \
-H "Authorization: Bearer $MISERU_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "context_type": "github_pr", "context_key": "elchika-inc/miseru#482" }'
フィールド 必須 意味
context_type ✅ github_pr / manual
context_key ✅ コンテキストのキー(1 文字以上)

context の主要フィールドを抜粋した例です。

{
"context": { "type": "github_pr", "key": "elchika-inc/miseru#482", "state": "open", "closed_at": null, "close_reason": null },
"reopened": 1
}
フィールド 意味
context 更新後のコンテキストの行。closed_at と close_reason は null に戻ります
reopened 状態を closed → open に変えたなら 1、既に open なら 0

コンテキストが存在しない場合や他 org に属する場合は 404 です。

on_close で失効済みの画像は戻りません。retain_days の既存画像の期限や、forever の画像も変更しません。


https://app.miseru.dev のダッシュボードは、セッション Cookie 認証の /v1/dashboard/* API(プロジェクト管理・トークン発行・画像一覧など)を使っています。これは Web ダッシュボード専用の内部 API であり、安定した公開契約ではありません。予告なく変更されることがあるため、自動化には上記の Upload Token 認証 API を使ってください。