Web API
agboard serveのHTTP APIです。書き込みの保護を含むリクエストの処理は、サーバーと画面で説明しています。
タスクの書き込みでは、自動生成する履歴の言語をX-Agboard-Language: enまたはjaで指定できます。ボードとモニターは現在の画面言語を送ります。未指定・重複・未対応の値では、本文の言語に従います。そのリクエストで自動生成する文章だけが対象で、明示した理由と保存済みの履歴は変わりません。履歴を参照してください。
エンドポイント
| API | 入出力と動作 |
|---|---|
GET / | 書き込み用トークンをmeta[name=agboard-token]に埋め込んだHTML。 |
GET /api/projects | projects(id・name・createdAt)とcatalogVersion。タスクは含まない。0件でも200。 |
POST /api/projects/{projectId}/tasks | title・body・acceptance・assignee・isDraftからTodo・Idleのタスクを作る。サーバーがfrontmatterを組み立てて検証する。201。 |
POST /api/projects/{projectId}/tasks/multipart | 画像付きの通常タスク、または依頼本文・任意のエピック・画像をまとめた簡易依頼タスクを作る。下記を参照。 |
PATCH /api/projects/{projectId}/tasks/{id} | 下書きのタスクのtitle・body・acceptanceと任意のfinalize: trueを更新する。bodyだけならfrontmatterを、acceptanceだけなら本文を保つ。descriptionとbodyの同時指定は422 invalid_value。後から保存した内容が優先される。下書きでなければ409 task_not_draft。 |
PUT /api/projects/{projectId}/tasks/{id}/draft | isDraftを設定する。Runningのタスクは409 invalid_state_for_draft。 |
PUT /api/projects/{projectId}/tasks/{id}/epic | {epicId: string | null}。未知のEPIC-IDは404 epic_not_found。 |
POST /api/projects/{projectId}/tasks/{id}/move | taskStatus、任意のreason、任意のfromStatus(利用者が移動を選んだときに画面が表示していた状態)。fromStatusがあるとき、タスクがもうその状態にいなければ409 task_status_changedで、何も書かない。本文には今の状態のcurrentStatusを加えるので、画面はそれを表示し、利用者はそこからもう一度移動できる。ボードの移動のダイアログは、いつもfromStatusを送る。 |
POST /api/projects/{projectId}/tasks/{id}/answer | requestIdと任意のchoiceId・textをロックの中で照合する。両方空は422、未知の選択肢は422 request_choice_unknown、依頼が変わっていれば409 request_changed。 |
POST /api/projects/{projectId}/tasks/{id}/agent-status | 状態の規則の範囲で稼働状態を変える。画面から操作する機能はない。 |
DELETE /api/projects/{projectId}/tasks/{id} | アーカイブ済みのタスクだけを削除する。それ以外は409 task_not_archived。204、すでにないときは404。 |
…/tasks/{id}/attachments、…/attachments/{attachmentId} | タスクの画像(GET・POST・DELETE)。ProjectId・タスクID・添付IDで照合する。 |
GET・POST /api/projects/{projectId}/epics | エピックの一覧と作成(id・name・color・isDraft・documentまたはbody)。acceptanceの入力は受け付けない。201。 |
GET・PATCH・DELETE /api/projects/{projectId}/epics/{epicId} | 取得、またはname・color・isDraft・documentまたはbody・priorityの更新。本文と未知のメタデータを保つ。EPIC.md→epic.jsonの順に保存する。DELETEは204を返す。そのエピックを名指ししているタスク(アーカイブ済みを含む)が一つでもあれば、409 epic_in_useで拒否する。 |
…/epics/{epicId}/attachments、…/{attachmentId} | エピックの画像。 |
POST …/tasks/{id}/archive、POST …/tasks/{id}/restore | 本文なし。すでにその状態のタスクに繰り返すと、変更せず同じタスクを返す。 |
POST …/tasks/archive-done | {ids: number[]}。空・重複・不正なIDは422。{tasks, archivedCount}を返す。 |
GET /api/projects/{projectId}/archive | 本文・全履歴・添付のメタデータを持つアーカイブ済みのタスクと、同じスナップショットのepics。各タスクのepicIdは同じ応答のepicsで解決する。エピックがないタスク、または存在しないエピックのタスクにはラベルを付けない。 |
GET /api/projects/{projectId}/summary | ボードの一覧に使うデータ。プロジェクト、version、タスク、エピック、problemsを返し、本文は含めない。SummaryとDetailを参照。 |
GET /api/projects/{projectId}/tasks/{id}/detail | 1件のタスクの本文・受入条件・履歴をNDJSONのストリームで返す。アーカイブ済みのタスクも同じ方法で読む。 |
GET /api/projects/{projectId}/epics/{epicId}/detail | 1件のエピックのドキュメントをNDJSONのストリームで返す。 |
GET /api/projects/{projectId}/docs/treeGET /api/projects/{projectId}/docs/fileGET /api/projects/{projectId}/docs/assetsPOST /api/projects/{projectId}/docs/open | プロジェクトのdocs/にある文書を読むだけのルート。フォルダのツリー、MarkdownかMDXのファイル1つ、画像、フォルダやファイルをコンピュータのアプリで開く操作。プロジェクトのドキュメントを参照。 |
GET /assets/{name} | build.rsが生成した表にある、内容から名前を付けたスクリプトとスタイルシートだけ。ほかは404。immutableでキャッシュする。Accept-Encodingが許せばgzipで送り、Vary: Accept-Encodingを付ける。 |
GET /docs-sandboxGET /docs-sandbox/{name} | 文書のMDXを実行するページと、内容から名前を付けたそのスクリプト(ほかの名前は404)。トークンもボードのデータも持たない固定のページ。専用のContent-Security-Policy(default-src 'none'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'self')とX-Content-Type-Options: nosniffを付ける。ボードはsandbox="allow-scripts"のiframeで埋め込む。ボードのルーターだけにあり、モニターにはない。ドキュメントのMDXを参照。 |
GET /api/events | SSE。購読後にボードのビルドIDを含むready({"build":"<16桁の16進数>"})、変更されたリソースを運ぶchanged(変更イベントを参照)、保存した言語やテーマが変わったときにpreferences(GET /api/preferencesと同じJSON)、15秒ごとに接続維持のコメント。 |
GET /api/preferences | 保存済みの言語とカラーテーマ。locale・theme(mode・accent)・revision。ファイルがない、または読めない場合は既定値。キャッシュしない。表示設定を参照。 |
PUT /api/preferences | JSONの本文でlocale・theme・両方のいずれかを保存し、GETと同じ形を、revisionを1増やして返す。未知の項目と不正な値は422 invalid_preferences、書き込みの失敗は500 preferences_unavailable。拒否・失敗のときは保存済みの値を変えない。 |
POST /api/notify | 全クライアントに再取得を促す。データを変えず、トークンも不要。204。 |
GET /api/health | okとpid。 |
GET /api/monitor-connectionsとその配下の経路 | ngrokのモバイル接続。モバイル接続を参照。 |
SummaryとDetail
ボードとモニターは、プロジェクトを2段階で読みます。Summaryは、カードや一覧の行に表示する内容をまとめて返します。Detailは、カードやエピックを開いたときにだけ必要な内容をストリームで返します。モバイルモニターには同じ契約を/monitor/api/projects/{projectId}/…で提供し、モニターのセッションを必要とします。モニターの経路を参照してください。パスのプロジェクトIDは読む対象の指定であり、アクセス権ではありません。
以下の形は、src/server/contracts/のRustの型と、tests/fixtures/data-layer/のゴールデン例で固定します。RustとTypeScriptのテストは、同じゴールデン例を読みます。
Summary。 GET …/summaryは、project(id・name)、version、tasks、epics、problemsを返します。本文、受入条件の文章、履歴、エピックのドキュメント、画像のバイト列は含みません。応答はインデックスの投影から作るため、ファイルは読みません。アーカイブ済みのタスクは一覧に含めません(エピックの件数には数えます)。versionは解析済みデータのハッシュで、ETag("<version>")にもなります。If-None-Matchがこの値を指す要求(複数の値、弱い形式、*も一致)には、本文なしの304を返します。存在しないプロジェクトは404 project_not_found、形式の誤ったIDは422 invalid_value、project.jsonを読めないプロジェクトはそのファイルのエラーです。
| 項目 | フィールド |
|---|---|
| タスク | id(整数)、title、taskStatus、agentStatus、assignee、isDraft、position、epicId(なければnull)、acceptance(done・total)、acceptanceError、createdAt、updatedAt、taskStatusChangedAt、agentStatusChangedAt、pendingRequestId、pendingReason、pendingChoices、onboardingSample(案内のサンプルのみ)、detailVersions(document・history)。名前はボードのTaskと同じ。 |
| エピック | id(文字列)、name、color、isDraft、priority(エピックが定める順序のタスクID。定めなければ空)、priorityError(保存された順序を読めないため、priorityは無視する)、progress(todo・doing・done・archived・total)、detailVersions(document)。 |
| Problem | file・field・reason。ボードと同じ。 |
versionはすべて不透明な文字列で、解析済みデータの内容ハッシュです。サーバーの起動回に依存しないため、再起動後も保存済みの値と比べられます。versionは等しいかどうかだけを比べてください。
Detail。 GET …/tasks/{id}/detailとGET …/epics/{epicId}/detailは、Content-Type: application/x-ndjsonとCache-Control: no-storeつきの200を返します。各行は1つのJSONオブジェクトで、typeがイベントを表します。
type | フィールド | 意味 |
|---|---|---|
start | resource(taskまたはepic)、id、projectId、sections、versions | 最初の行。versionsは要求したセクションだけを持つ。 |
section-start | section(documentまたはhistory) | セクションを開く。 |
attachments | items | 要求したリソースの添付画像。documentセクションで1回だけ送り、section-startの直後、最初のブロックの前に置く。section-endのcountには数えない。 |
block | kind(markdownまたはmermaid)、text、任意のimages | ドキュメントの最上位のMarkdownブロック。ブロックを連結すると本文になる。各画像はsrc(text中のURL)を持ち、ファイルのヘッダーを読めたときはwidthとheightも持つ。 |
acceptance | id、text、done | 受入条件1件の全文。タスクのみ。ブロックの後に続く。 |
history-item | at、reason | 履歴1件。古い順。タスクのみ。 |
section-end | section、count | セクションを閉じる。countはそのセクションの項目行の数。 |
complete | versions | 最後の行。startと同じversionsを繰り返す。どちらも1つのスナップショットから計算する。 |
順序は、start、要求した各セクションのsection-start・項目・section-end、最後にcompleteです。documentはhistoryより前に置きます。タスクのdocumentセクションは、ブロックの後に受入条件が続きます。エピックにはdocumentセクションしかありません。completeなしで終わったストリームは不完全であり、クライアントは受け取った内容を破棄します。
?sections=document、?sections=history、?sections=document,historyでセクションを選びます。指定しなければ両方を送ります。エピックにhistoryはありません。ほかの値は400 invalid_sectionsです。プロジェクト、タスク、エピックがなければ、ストリームの開始前に404を返します。最初の行の後で失敗したときはcompleteを送らず、接続を閉じます。
変更イベント
changedは、変更されたリソースを運びます。
{"instanceId": "inst-5f3a", "seq": 42, "projectId": "…", "changes": [
{"resource": "task", "id": 302, "facets": ["summary", "history"]},
{"resource": "epic", "id": "EPIC-1", "facets": ["document"]},
{"resource": "project", "id": "…", "facets": ["deleted"]}
]}| フィールド | 意味 |
|---|---|
instanceId | サーバーの起動ごとに1つの値。新しい値は、seqが最初からになったことをクライアントに伝える。 |
seq | 1つのインスタンス内で、changedごとに1ずつ増える整数。欠番はイベントを取り逃したことを表す。 |
projectId | 変更が属するプロジェクト。モニターのストリームは、要求したプロジェクトの変更だけを送る。PCのストリームは全プロジェクトを送り、画面は現在のプロジェクト以外を無視する。 |
changes[].resource | task、epic、project(プロジェクト一覧の変化: 作成・名前の変更・削除)。 |
changes[].id | タスクは整数、エピックとプロジェクトは文字列。 |
changes[].facets | 重複のない、空でない一覧。summaryはタイトル・状態・位置・エピック・Pendingの質問・受入条件の件数。documentは本文と受入条件の文章。historyは履歴。deletedはリソースが消えたこと。プロジェクトはsummaryかdeletedだけ。タスクをアーカイブへ移す、または戻すことはsummaryの変更。 |
seqの欠番、新しいinstanceId、未知のイベントを見たクライアントは、Summaryと関係するキャッシュ済みのDetailを破棄します。readyとpreferencesは変わりません。
サーバーは再読込のたびに解析済みデータを前後で比べるため、SummaryにもDetailにも見える内容が変わらない保存ではイベントを送りません。解析できないファイルは、そのリソースのすべてのfacetを変更とします。遅れすぎたストリームには、取りこぼしたイベントの代わりにresync({"instanceId", "seq"}。seqは最新の番号)を送り、クライアントは全体を取得し直します。モニターのストリームは送るイベントに自分で番号を振るため、ほかのプロジェクトの変更でseqに欠番は生じず、projectの変更も送りません。
モバイル接続
これらの経路はローカルのリスナー(127.0.0.1)にだけあり、ngrokの入口は配信しません。読み取りはローカルのページからでなければならず、別のHost、クロスサイトのSec-Fetch-Site、別のOriginは403です。書き込みは、ほかの書き込みと同じブラウザの書き込みガードを通ります。
| API | 契約 |
|---|---|
GET /api/monitor-connections | 現在の状態(下記)。秘密の値は含まない。 |
PUT /api/monitor-connections/settings/ngrok | {enabled, revision}を保存し、状態を返す。revisionは最後に読んだ値でなければならない。未知のフィールドは422 invalid_value、古いrevisionは409 revision_conflict、保存の失敗は500 save_failedで何も変えない。トンネルが動いている間に保存しても作り直さない。作り直すのは開始だけ。 |
POST /api/monitor-connections/transports/ngrok/start | 接続を開始し、202で状態(通常はstarting)を返す。任意の本文は{token, save}。tokenはメモリーだけに保持する。save: trueはこのPCの安全な保管場所(OSの資格情報ストア)にも保存し、tokenが必要。無効な設定は、先に有効として保存する。トンネルが動いている間の開始は、トンネルを作り直す(合言葉も新しくなる)。 |
POST /api/monitor-connections/transports/ngrok/stop | 接続を停止して無効として保存し、200で状態を返す。 |
DELETE /api/monitor-connections/token | 安全な保管場所に保存したトークンを削除し、200で状態を返す。何も保存していなくてもエラーにしない。 |
状態のオブジェクトは次のフィールドを持ちます。
| フィールド | 意味 |
|---|---|
status | disabled・needs_config・starting・ready・reconnecting・failed・stoppingのいずれか。 |
url | readyのときの公開HTTPSアドレス。それ以外はnull。 |
revision | PUTに使う設定のrevision。 |
settings | enabled。 |
tokenConfigured | 開始したときにトークンが見つかるか。 |
tokenSource | env(NGROK_AUTHTOKEN)、keychain(安全な保管場所)、session(今回の起動だけ入力)、nullのいずれか。値は返さない。 |
keychainAvailable | このPCに、トークンを保存できる安全な保管場所(OSの資格情報ストア)があるか。 |
keychainTokenSaved | 別の取得元が使われていても、安全な保管場所にトークンが保存されているか。 |
failure | null、またはconnect_failed・endpoint_failed・public_url_invalid・internal・config_unreadable・config_invalidのいずれか。 |
passcode | 動いている接続の4桁の合言葉。PCが表示するために返す。statusがreadyの間だけ含まれ、開始中・停止中・失敗のときは含まれない。このAPIが返す唯一の秘密。 |
開始の進み具合はこの状態で報告されます。statusがstartingでなくなるまでGETで確認してください。トークン、間違いの回数、sessionが、応答や設定ファイルに現れることはなく、合言葉が現れるのはこことserveの起動行だけです。設定の保存先は共有ファイル、エラーのコードはエラーコードにあります。
表示設定
GETとPUT /api/preferencesはローカルのリスナーにあります。ngrokのモニターには、同じ組が/monitor/api/preferencesにあります(モニターのルートを参照)。保存先のファイルは共有ファイルで説明しています。
PUTは、locale(enかja)・theme({"mode": "light" | "dark", "accent": <12色のいずれか>})・両方のいずれかをJSONで受け取ります。送らなかった項目は、保存済みの値を保ちます。
{"locale": "ja", "theme": {"mode": "dark", "accent": "teal"}}保存に成功すると保存した内容を返し、開いているイベントストリームすべてにpreferencesイベントを送ります。対象は、ボードの/api/eventsと、モニターの/monitor/api/eventsです。イベントのデータはGETと同じJSONです。PCのPUTは、ほかのPCの書き込みと同じ書き込みの保護(Host・Origin・Sec-Fetch-Site: same-origin・X-Agboard-Token)を通ります。モニターのPUTは、回答と同じくモニターのsessionとCSRFトークンが必要です。
JSONとして読めない本文は、ほかの書き込みと同じく400です。localeとthemeのどちらもない本文、それ以外の項目を含む本文、許可されない値は422 invalid_preferencesです。revisionの確認はなく、後から保存した内容が優先されます。
プロジェクトのドキュメント
次のルートは、プロジェクトのdocs/(shared/projects/<projectId>/docs/)にあるファイルを読みます。書き込むルートはなく、ファイルのルートへのPUTは405です。ボードのルーターにだけあり、モニターの入口では提供しません。ディスク上のドキュメントの変化は、changedではなくSSEのdocsイベントで知らせます(変更の通知を参照)。パスはdocs/からの相対パスで、区切りは/です。検証の内容はセキュリティモデルにあります。プロジェクトIDが32文字の小文字16進数でないときは422 invalid_valueです。
| API | 契約 |
|---|---|
GET …/docs/tree | docs/の下のフォルダとファイルの名前だけを、{nodes, truncated}で返す。ファイルにはviewerがあり、.mdと.mdxはmarkdown、.png・.jpg・.jpeg・.webp・.gif・.avif・.svgはimage、それ以外はother(一覧には出すが表示しない)。フォルダにはviewerがない。フォルダが先、ファイルが後で、それぞれ大文字・小文字を区別せずに名前順。ドットで始まる名前、シンボリックリンク、パスの規則で拒否される名前は含めない。フォルダがないときはエラーではなく空のツリー。一覧は最大5,000項目・16階層までで、上限で打ち切ったときはtruncated: true。 |
GET …/docs/file?path= | {path, content, revision}。contentはファイルの原文そのもの(BOMも含む)。revisionはsha256:とファイルのバイト列のSHA-256(16進数)で、画面はこれを比べてファイルが変わったかを判断する。対象は、.mdと.mdx(大文字・小文字は問わない)で、2 MiB以下で、正しいUTF-8のファイルだけ。 |
PUT …/docs/file | 本文は{path, content, expectedRevision}。知らない項目は400 invalid_json。ファイルがまだexpectedRevisionのときだけ、contentをそのままのバイト列で書き、新しいrevisionを含む{path, revision}を返す。ほかの書き込みと同じ書き込みのガードを通る。ファイルを新しく作ることはない。 |
GET …/docs/assets?path= | 10 MiB以下のPNG・JPEG・WebP・GIF・AVIF・SVGの画像のバイト列。Content-Typeは拡張子から決め(image/png・image/jpeg・image/webp・image/gif・image/avif・image/svg+xml)、X-Content-Type-Options: nosniffを付ける。SVGの応答にはContent-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandboxも付けるので、URLを直接開いてもスクリプトは動かない。画面では<img>で表示するので、そこでもスクリプトは動かない。ほかの形式は415 docs_asset_type。 |
POST …/docs/open[?path=] | 本文なし。サーバーを動かしているコンピュータで、プロジェクトのdocs/をファイルマネージャで開く。?path=があるときは、そのファイルを、コンピュータがその種類に使うアプリで開く。macOSはopen、Windowsはexplorer、それ以外はxdg-openを使う。パスは読み込みと同じ方法で確かめ、存在するファイル(種類は問わない)であることを求める(400 docs_path_invalid、404 docs_not_found)。プログラムはシェルを通さず、フォルダかファイルだけを引数にして起動し、終了は待たない。成功すると204。プロジェクトがないときは404 project_not_found。docs/がない、またはプログラムを起動できない(たとえばxdg-openが入っていない)ときは500 docs_open_failed。プログラムを起動するので、ほかの書き込みと同じく書き込みガードを通る。 |
ツリーの例:
{"nodes":[{"name":"design","path":"design","kind":"dir","children":[{"name":"images","path":"design/images","kind":"dir","children":[]},{"name":"spec.md","path":"design/spec.md","kind":"file","viewer":"markdown"}]},{"name":"flow.png","path":"flow.png","kind":"file","viewer":"image"},{"name":"app.pen","path":"app.pen","kind":"file","viewer":"other"},{"name":"README.md","path":"README.md","kind":"file","viewer":"markdown"}],"truncated":false}ファイルの例:
{"path":"design/spec.md","content":"---\ntitle: Design\n---\n# Design\n","revision":"sha256:b9dc9fdb…"}失敗は通常のエラーの形式です。コードとステータスはエラーコードにあります。
multipartでタスクを作成する
POST /api/projects/{projectId}/tasks/multipartは、JSONのmetadataパートを一つ、fileパートを0件以上受け付けます。各ファイルには、metadata.attachmentsに対応する{id, name}が必要です。通常の添付枚数・サイズ・メディア形式の制限も適用されます。通常のタスクでは、requestId・title・description・assigneeと任意のisDraftを指定します。簡易依頼フォームの場合は次のように送ります。
{
"requestKind": "simple",
"description": "1行目をタイトルに使います\n\n依頼の詳細",
"epicId": "DESIGN",
"attachments": [{"id": "<画像ID>", "name": "sketch.png"}]
}epicIdとattachmentsは任意です。エピックがない場合は省略するかnull、画像がない場合は空配列にします。簡易依頼ではtitle・assignee・isDraft・finalizeを指定できません。サーバーはAI担当・Todoのタスクを作り、入力本文を## 元の依頼で囲み、最初の空でない行(Unicode文字数で最大100文字)からタイトルを作ります。プロジェクトロック内でタスクと画像を検証・ステージングし、完成したタスクフォルダを一度にrenameして公開します。受け付けた送信は毎回新しいタスクになり、201 {"task": …}を返します。本文などの一致による重複判定はしません。手動で作る通常タスクは従来どおりPOST /tasksか通常のmultipart形式を使い、簡易依頼用の契約へ切り替わりません。
一括アーカイブ。 対象のIDは確認を開いた時点で固定し、送信時にDoneのタスクを集め直すことはありません。ロックの中で、未アーカイブの対象がすべてまだDoneであることを確かめます。移動または削除された対象が一つでもあれば、要求全体を409 archive_targets_changedにして何も変えません。アーカイブ済みの対象は完了済みとして扱うので、応答を失った後の再送で履歴は重複しません。対象は一件ずつ保存し、最後に一回だけ再読み込みと通知を行います。途中で保存に失敗すると、保存済みの対象はアーカイブされたまま残り、再送で残りを処理します。
エラーはcode・message・reasonを持ちます。
| ステータス | 意味 |
|---|---|
| 400 | 壊れたJSON、不正なsectionsクエリ(invalid_sections)、または不正なドキュメントのパス(docs_path_invalid)。 |
| 403 | 保護に失敗した。 |
| 404 | プロジェクト・タスク・エピックがない。またはドキュメントのファイルがない。 |
| 409 | request_changed・task_status_changed・task_not_draft・invalid_state_for_draft・epic_in_use・epic_id_taken・task_not_archived・archive_targets_changed。 |
| 413 | 1 MiBを超える本文。または、上限を超えるドキュメントのファイル(docs_too_large)。 |
| 415 | Markdownでないドキュメントのファイル、または提供しない形式の画像(docs_not_markdown・docs_asset_type)。 |
| 422 | ID構文・入力・状態の規則の違反。UTF-8でないドキュメントのファイル(docs_not_utf8)。または、パス・項目・理由つきの正本ファイルの不備(file_invalid)。 |
| 503 | ロック待ちが時間切れになった。 |
| 500 | それ以外(format_unsupported・file_unreadable・file_errorを含む)。 |
初回の案内
| メソッドとパス | 役割 |
|---|---|
GET /api/onboarding | 案内の状態。読み取りだけ。 |
POST /api/onboarding/claim | タブに案内を渡す。clientId、expectedRevision。 |
POST /api/onboarding/replay | 完了またはスキップ済みの案内をもう一度提示する。clientId、expectedRevision。 |
POST /api/onboarding/heartbeat | リースを延長する。clientId。 |
POST /api/onboarding/skip | カードを作る前にスキップする。clientId、expectedRevision、commandId。 |
POST /api/onboarding/recover | リースが切れた run を終了する。 |
POST /api/projects/{projectId}/onboarding/start | run を始める。clientId、attemptId、expectedRevision、locale。同じ attempt の再送は同じ run を返す。 |
POST /api/projects/{projectId}/onboarding/{runId}/advance | 手順を 1 つ進める。clientId、expectedRevision、commandId、transition。 |
POST /api/projects/{projectId}/onboarding/{runId}/finish | run のカードをアーカイブし、案内を完了する。 |
POST /api/projects/{projectId}/onboarding/{runId}/abort | run を途中で終え、カードをアーカイブする。 |
書き込みは write guard を通り、未知のフィールドは拒否されます。サンプルの質問への回答は、既存の /api/projects/{projectId}/tasks/{id}/answer を使います。

